项目要求
本项目要求基于带调试器的i.MX RT1021开发板实现亮度测量与控制功能。板上集成了环境亮度传感器,与屏幕搭配实现亮度测量与控制,并能够根据当前亮度,并依照人眼对亮度的感知曲线,自动调整屏幕亮度。
板卡介绍
i.MX RT1021核心板是基于恩智浦i.MX RT1020系列MIMXRT1021CAG4A芯片设计的综合开发板。该芯片基于Arm Cortex-M7内核,运行频率高达500MHz,内置256KB片上RAM。具有丰富的存储器接口和连接接口,包括UART、SPI、I2C、USB、以太网和CAN,适用于工业和物联网应用。核心板提供了MCUXpresso生态合作体系支持,包括SDK、IDE选项以及安全配置和配置工具,可实现快速开发。
MIMXRT1021DAG5A芯片特点:
- 高性能 Arm Cortex-M7
- 2517 CoreMark/1070 DMIPS@500 MHz
- 256KB紧耦合内存(TCM)
- 低延迟响应低至20 ns
- 多种存储器接口:SDRAM、RAW NAND、闪存、NOR闪存、SD/eMMC、四通道SPI
- 丰富的连接接口:UART、SPI、I2C、USB、CAN
- 高达480M的USB高速通信模式支持
- 音频功能:SPDIF和I2S音频接口
- 低功耗运行模式下运行频率为24MHz
- 支持多种RTOS
- 集成DC-DC转换器降低动态功耗
- 多种外部存储器接口选项:NAND、eMMC、QuadSPI NOR闪存、并行NOR闪存
- 多种无线扩展连接接口:Wi-Fi、蓝牙、低功耗蓝牙、ZigBee和Thread
- 适用于低成本PCB设计的144LQFP和100LQFP封装
- MCUXpresso软件和工具套件支持,包括IDE选项的选择、引脚、时钟、外设、安全和存储器配置工具以及安全编程和配置工具。
实现思路
实验的核心目标是测量和控制亮度。基于i.MX RT1021核心板和MIMXRT1021DAG5A芯片的强大功能,我们将通过该MCU实现功能。首先,通过核心板上的光敏传感器或其他传感器获取环境亮度数据。然后,使能MCU的PWM功能调节屏幕的背光亮度。最后,重复测量亮度进行反馈。
实现框图
实现方法
- 使用MCUXpresso软件和工具套件配置I2C功能用于驱动温度传感器。
在原理图中可以看到,BH1730亮度传感器的SDA连接到了 GPIO2_03,SCL连接到了 GPIO2_02。
在外设列表中,添加 LPI2C1 用于驱动I2C。
- 使用MCUXpresso软件和工具套件配置SPI功能用于驱动屏幕。
屏幕使用 st7735s,通过原理图中可以看到, rst 连接到了 GPIO3_07,dc连接到了 GPIO3_04,sdi连接到了 GPIO3_03,sdo连接到了GPIO3_02,cs连接到了GPIO3_01,SCK连接到了GPIO3_0。
在外设标签中添加上LPSPI4,用于驱动SPI。
- 使用MCUXpresso软件和工具套件配置PWM功能用于驱动背光。
在原理图中可以看到,GPIO2_31是连接到背光控制的。所以在引脚设置中,将GPIO2_31连接到PWM外设。
在外设页面中,使能 PWM2,并且使能 B 通道输出。这里设置的频率是 100KHz,占空比通过主程序代码调节。
- 编写程序,获取亮度传感器数据。
可以在 CSDN 下载到 BH1730 的驱动,通过移植接口可以适配新的 I2C 驱动。
/*
Simple library to use a BH1730FVI Digital Light Sensor.
Uses I2C communication.
Written by Janco Kock, Jan 2021.
*/
#include "BH1730.h"
/**
* Constructor
*
*/
BH1730::BH1730() {}
/**
* Configure sensor
*/
bool BH1730::begin() {
// Verify if there is a BH1730 on this address
if ((read8(BH1730_REG_PART_ID) >> 4) != BH1730_PART_NUMBER) {
PRINTF("BH1730 not found");
return false;
} else {
PRINTF("BH1730 PART ID: %d\r\n", read8(BH1730_REG_PART_ID) >> 4);
}
// Reset
write8(BH1730_CMD_SPECIAL | BH1730_CMD_SPECIAL_SOFT_RESET, (1 << 7));
// Check if gain is set before begin
if (gain != GAIN_X1) {
setGain(gain);
}
return true;
}
/**
* Set gain of the internal ADC
*
*/
void BH1730::setGain(BH1730_GAIN gain) {
if (gain == GAIN_X1) {
write8(BH1730_REG_GAIN, BH1730_GAIN_X1_MODE);
} else if (gain == GAIN_X2) {
write8(BH1730_REG_GAIN, BH1730_GAIN_X2_MODE);
} else if (gain == GAIN_X64) {
write8(BH1730_REG_GAIN, BH1730_GAIN_X64_MODE);
} else if (gain == GAIN_X128) {
write8(BH1730_REG_GAIN, BH1730_GAIN_X128_MODE);
} else {
#if BH1730_DEBUG == 1
Serial.println("Gain invalid");
#endif
return;
}
BH1730::gain = gain;
}
void BH1730::delay(unsigned int t) {
SDK_DelayAtLeastUs(1000 * t, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
}
/**
* Read lux level from sensor.
* Returns -1 if read is timed out
*
*/
float BH1730::readLux() {
// Start one time measurement
write8(BH1730_REG_CONTROL, BH1730_REG_CONTROL_POWER |
BH1730_REG_CONTROL_ADC_EN |
BH1730_REG_CONTROL_ONE_TIME);
// Wait for ADC data is valid
uint8_t ret = 0;
while (((read8(BH1730_REG_CONTROL) & BH1730_REG_CONTROL_ADC_VALID) == 0) &&
++ret < BH1730_RET_TIMEOUT) {
delay(10);
}
if (ret == BH1730_RET_TIMEOUT) {
#if BH1730_DEBUG == 1
Serial.println("Read timed out");
#endif
return -1;
}
// Read real light and IR light from registers
float data0 = (float)read16(BH1730_REG_DATA0_LOW);
float data1 = (float)read16(BH1730_REG_DATA1_LOW);
// Calculate lux based on formula in datasheet.
if (data0 == 0)
return 0;
float lx = 0;
float div = data1 / data0;
if (div < 0.26) {
lx = ((1.29 * data0) - (2.733 * data1)) / gain * 102.6 / BH1730_ITIME_MS;
} else if (div < 0.55) {
lx = ((0.795 * data0) - (0.859 * data1)) / gain * 102.6 / BH1730_ITIME_MS;
} else if (div < 1.09) {
lx = ((0.51 * data0) - (0.345 * data1)) / gain * 102.6 / BH1730_ITIME_MS;
} else if (div < 2.13) {
lx = ((0.276 * data0) - (0.13 * data1)) / gain * 102.6 / BH1730_ITIME_MS;
}
return lx;
}
void BH1730::write8(uint8_t a, uint8_t d) {
size_t txCount = 0;
/* Send master blocking data to slave */
if (kStatus_Success == LPI2C_MasterStart(LPI2C1, BH1730_ADDR, kLPI2C_Write)) {
/* Check master tx FIFO empty or not */
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
while (txCount) {
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
}
/* Check communicate with slave successful or not */
if (LPI2C_MasterGetStatusFlags(LPI2C1) & kLPI2C_MasterNackDetectFlag) {
return;
}
uint8_t g_master_txBuff[] = {a | BH1730_CMD, d};
status_t reVal = LPI2C_MasterSend(LPI2C1, g_master_txBuff, 2);
if (reVal != kStatus_Success) {
if (reVal == kStatus_LPI2C_Nak) {
LPI2C_MasterStop(LPI2C1);
}
return;
}
reVal = LPI2C_MasterStop(LPI2C1);
if (reVal != kStatus_Success) {
return;
}
}
}
uint8_t BH1730::read8(uint8_t a) {
uint8_t g_master_rxBuff[1] = {0};
size_t txCount = 0;
if (kStatus_Success == LPI2C_MasterStart(LPI2C1, BH1730_ADDR, kLPI2C_Write)) {
/* Check master tx FIFO empty or not */
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
while (txCount) {
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
}
/* Check communicate with slave successful or not */
if (LPI2C_MasterGetStatusFlags(LPI2C1) & kLPI2C_MasterNackDetectFlag) {
return -1;
}
uint8_t g_master_txBuff[] = {a | BH1730_CMD};
status_t reVal = LPI2C_MasterSend(LPI2C1, g_master_txBuff, 1);
if (reVal != kStatus_Success) {
if (reVal == kStatus_LPI2C_Nak) {
LPI2C_MasterStop(LPI2C1);
}
return -1;
}
reVal = LPI2C_MasterRepeatedStart(LPI2C1, BH1730_ADDR, kLPI2C_Read);
if (reVal != kStatus_Success) {
return -1;
}
reVal = LPI2C_MasterReceive(LPI2C1, g_master_rxBuff, 1);
if (reVal != kStatus_Success) {
if (reVal == kStatus_LPI2C_Nak) {
LPI2C_MasterStop(LPI2C1);
}
return -1;
}
reVal = LPI2C_MasterStop(LPI2C1);
if (reVal != kStatus_Success) {
return -1;
}
}
return g_master_rxBuff[0];
}
uint16_t BH1730::read16(uint8_t a) {
uint8_t g_master_rxBuff[2] = {0};
size_t txCount = 0;
if (kStatus_Success == LPI2C_MasterStart(LPI2C1, BH1730_ADDR, kLPI2C_Write)) {
/* Check master tx FIFO empty or not */
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
while (txCount) {
LPI2C_MasterGetFifoCounts(LPI2C1, NULL, &txCount);
}
/* Check communicate with slave successful or not */
if (LPI2C_MasterGetStatusFlags(LPI2C1) & kLPI2C_MasterNackDetectFlag) {
return -1;
}
uint8_t g_master_txBuff[] = {a | BH1730_CMD};
status_t reVal = LPI2C_MasterSend(LPI2C1, g_master_txBuff, 1);
if (reVal != kStatus_Success) {
if (reVal == kStatus_LPI2C_Nak) {
LPI2C_MasterStop(LPI2C1);
}
return -1;
}
reVal = LPI2C_MasterRepeatedStart(LPI2C1, BH1730_ADDR, kLPI2C_Read);
if (reVal != kStatus_Success) {
return -1;
}
reVal = LPI2C_MasterReceive(LPI2C1, g_master_rxBuff, 2);
if (reVal != kStatus_Success) {
if (reVal == kStatus_LPI2C_Nak) {
LPI2C_MasterStop(LPI2C1);
}
return -1;
}
reVal = LPI2C_MasterStop(LPI2C1);
if (reVal != kStatus_Success) {
return -1;
}
}
return g_master_rxBuff[1] << 8 | g_master_rxBuff[0];
}
在主函数中就可以直接进行初始化和亮度读取。
BH1730 bh = BH1730();
bh.setGain(GAIN_X128);
bh.begin();
int lux = bh.readLux();
- 根据收集的亮度信号,控制屏幕亮度。通过观察,可以发现亮度变化和 PWM 占空比近似为三分之一的关系,所以可以直接通过对 Lux 除以 3 得到屏幕的PWM占空比。
while (1) {
int t = int(bh.readLux() / 3);
PWM_UpdatePwmDutycycle(PWM2, kPWM_Module_2, kPWM_PwmB,
kPWM_SignedCenterAligned, max(5, min(100, t + 5)));
PWM_SetPwmLdok(PWM2, kPWM_Control_Module_2, true);
}
- 屏幕内容刷新
在初始化的时候调用 set_Color_Loop 提前生成渐变颜色条。
static unsigned int RGB[32 * 3];
void set_Color_Loop() {
for (uint8_t i = 0; i <= 32; i++) {
RGB[i] = (((0xff / 32) * i) << 8) | ((0xff / 32) * (32 - i)); // 蓝到绿
RGB[32 + 1 + i] = (((0xff / 32) * i) << 16) | ((0xff / 32) * (32 - i))
<< 8; // 绿到红
RGB[((32 + 1) * 2) + i] = ((0xff / 32) * i) | ((0xff / 32) * (32 - i))
<< 16; // 红到蓝
}
return;
}
然后在主函数中循环调用 refreshColor 进行屏幕刷新。
unsigned int rgb_position = 0;
unsigned int current_line = 0;
lcd_status_t refreshColor() {
rgb_position %= 32 * 3;
current_line %= lcd_settings->height;
lcd_drawHorizontalLine(
0, current_line, lcd_settings->width, RGB[rgb_position] >> 16 & 0xFF,
RGB[rgb_position] >> 8 & 0xFF, RGB[rgb_position] & 0xFF);
rgb_position++;
current_line++;
return LCD_OK;
}
因此,在屏幕上就会产生渐变效果。
实物展示
遇到问题
在实现过程中,会面临硬件连接、软件配置以及编程等多方面的问题。需要根据具体情况调整引脚配置、外设参数。
总结感想
通过本次实验,我成功实现了基于i.MX RT1021开发板的亮度测量与控制功能。这得益于该开发板强大的处理能力和丰富的外设接口。同时,我要感谢硬禾学堂的赞助,为我提供了实施该项目所需的资源和支持,使实验顺利完成。
感谢硬禾学堂对教育事业的支持,为培养科技人才做出了积极的贡献。希望未来能有更多类似的合作,推动教育与科技的发展。