1.项目功能介绍
此次项目主要是基于RT1021核心板及其扩展板实现光强度检测,让后获取到光强后,根据人对亮度的感知曲线,自动调整屏幕的亮度,对眼睛进行保护。
2.硬件框图
RT1021核心板是基于恩智浦i.MX RT1020系列MIMXRT1021CAG4A芯片设计的综合开发板,i.MX RT1020跨界MCU基于Arm®Cortex®-M7内核,运行频率高达500MHz,内置256KB片上RAM,该MCU系列提供各种存储器接口和丰富的连接接口,包括UART、SPI、I2C、USB、10/100M以太网和CAN,具有实时性能和高集成功能,该适用于工业和物联网应用。i.MX RT1020系列由MCUXpresso生态合作体系支持,其中包括SDK、IDE选项以及安全配置和配置工具,可实现快速开发。
本次活动采用的是144pin脚的MIMXRT1021DAG5A芯片,外扩8MB FLASH用于存储程序,核心板上集成了四个按键,一个电源指示灯,一个用户LED灯,两组2x20 Pin的排针将调试接口和可用IO口基本全部引出,方便扩展与测试。核心板采用TYPE-C接口进行供电和数据传输,此外该接口还支持480M高速通信模式。核心板卡上预留有Micro SD卡槽,可用作数据存储与回放。
实际项目主要使用boot reset 按键 、USB boot 刷写 及SWD 调试接口
屏幕和环境光传感器都在扩展板上
屏幕使用LSPI2 接口进行驱动,屏幕片选有区别其他模块需要DC和CS 与门使能片选,编程时需注意。
屏幕的亮度显示主要通过MOS 管驱动,通过GPIO2_31 可以通过PWM 进行亮度调节,也是本项目主要展示功能。
环境光主要使用罗姆全新BH1730FVC -TR 环境光传感器,罗姆的传感器datasheet写的比较详细,对原理理解及代码编程可以很快上手。这里也标注了IIC 地址0x29(7bit) IIC SDA SCL 电路板已经进行了上拉。
3.软件流程图
整体软件流程如上,屏幕初始化主要进行lspi4的初始化,然后根据屏幕的初始化流程进行SPI通讯。BH1730读取与控制主要通过IIC 进行控制,这里IIC 控制逻辑有别于常规的IIC 控制,需要 自己定制逻辑实现。
4.主要代码说明
屏幕spi 初始化函数
const lpspi_master_config_t LPSPI4_config = {
.baudRate = 10000000UL,
.bitsPerFrame = 8UL,
.cpol = kLPSPI_ClockPolarityActiveLow,
.cpha = kLPSPI_ClockPhaseSecondEdge,
.direction = kLPSPI_MsbFirst,
.pcsToSckDelayInNanoSec = 1000UL,
.lastSckToPcsDelayInNanoSec = 1000UL,
.betweenTransferDelayInNanoSec = 1000UL,
.whichPcs = kLPSPI_Pcs0,
.pcsActiveHighOrLow = kLPSPI_PcsActiveLow,
.pinCfg = kLPSPI_SdiInSdoOut,
.pcsFunc = kLPSPI_PcsAsCs,
.dataOutConfig = kLpspiDataOutTristate,
.enableInputDelay = false
};
static void LPSPI4_init(void) {
LPSPI_MasterInit(LPSPI4_PERIPHERAL, &LPSPI4_config, LPSPI4_CLOCK_FREQ);
}
根据屏幕datasheet 属性,这里初始化spi模式是模式4,时钟采用10M,单次发送8bit .CS采用单独控制,由于显示屏和触摸屏公用SPI 通过CS 进行使能不同的功能
PWM 初始化
PWM_GetDefaultConfig(&PWM2_SM2_config);
PWM2_SM2_config.reloadLogic = kPWM_ReloadPwmFullCycle;
PWM2_SM2_config.pairOperation = kPWM_Independent;
PWM2_SM2_config.enableDebugMode = true;
uint16_t freq =1000;
uint16_t temp_prsc =(PWM_SRC_CLK_FREQ/freq)>>16;
if(PWM_SRC_CLK_FREQ%(freq<<16)) temp_prsc++;
if (1 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_1;
else if (2 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_2;
else if (4 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_4;
else if (8 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_8;
else if (16 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_16;
else if (32 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_32;
else if (64 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_64;
else if (128 >= temp_prsc) PWM2_SM2_config.prescale = kPWM_Prescale_Divide_128;
else
{
assert(0) ;//频率过小 或者IPG频率过高
}
PWM_Init(PWM2_PERIPHERAL, PWM2_SM2, &PWM2_SM2_config);
PWM_Deinit(PWM2_PERIPHERAL, PWM2_SM2); //enable clock
PWM_Init(PWM2_PERIPHERAL, PWM2_SM2, &PWM2_SM2_config);//reconfig
PWM_SetPwmLdok(PWM2_PERIPHERAL, kPWM_Control_Module_2, false); //清除LOAD OKAY位 以设置新的参数
PWM_SetupPwm(PWM2_PERIPHERAL, PWM2_SM2, PWM2_SM2_pwm_function_config, 1U, kPWM_EdgeAligned, freq,PWM_SRC_CLK_FREQ);
PWM_SetPwmLdok(PWM2_PERIPHERAL, kPWM_Control_Module_2, true); //更新有关设置
PWM_StartTimer(PWM2_PERIPHERAL, kPWM_Control_Module_2); //开始计数
PWM2_PERIPHERAL->SM[PWM2_SM2].DISMAP[0]=0;
最初PWM 初始化使用的是 CONFIG 默认,发现时钟设置不成功,这里主要跟prescale及 fault map 有关,界面设计的不是很智能,这里只有自己制定prescale 进行设置及fault 设置,针对PWM 调整,这里使用的是1KHz 频率 50%默认占空比配置。
动态调整占空比函数, 首先清楚LOAD OKAY 位置,以便设置参数,更新完成PWM 占空比后,再使能LOAD OKAY 位置即可。
void LCD_duty(uint8_t duty)
{
PWM_SetPwmLdok(PWM2_PERIPHERAL, kPWM_Control_Module_2, false); //清除LOAD OKAY位 以设置新的参数
PWM_UpdatePwmDutycycle(PWM2_PERIPHERAL,PWM2_SM2,(pwm_submodule_t)kPWM_PwmB,kPWM_EdgeAligned,duty);
PWM_SetPwmLdok(PWM2_PERIPHERAL, kPWM_Control_Module_2, true); //更新有关设置
}
环境光获取IIC 配置
默认硬件的IIC 使用config 界面遇到些问题,这里初步先使用gpio模拟完成相关功能,以下是gpio 基础设置
gpio_pin_config_t i2c_pin_cfg;
i2c_pin_cfg.direction = kGPIO_DigitalOutput;
i2c_pin_cfg.outputLogic = 1;
i2c_pin_cfg.interruptMode = (gpio_interrupt_mode_t)NO_INT;
IOMUXC_SetPinMux(IOMUXC_GPIO_EMC_02_GPIO2_IO02,0);
IOMUXC_SetPinConfig(IOMUXC_GPIO_EMC_02_GPIO2_IO02,GPIO_PIN_CONFIG);
GPIO_PinInit(GPIO2,2U,&i2c_pin_cfg);
IOMUXC_SetPinMux(IOMUXC_GPIO_EMC_03_GPIO2_IO03,0);
IOMUXC_SetPinConfig(IOMUXC_GPIO_EMC_03_GPIO2_IO03,GPIO_PIN_CONFIG);
GPIO_PinInit(GPIO2,3U,&i2c_pin_cfg);
实际使用中主要使用的接口如下
#define SDA GPIO_ReadPadStatus(SDA_PORT,SDA_PIN)
#define SDA0() GPIO_ClearPinsOutput(SDA_PORT,1<<SDA_PIN)
#define SDA1() GPIO_SetPinsOutput(SDA_PORT,1<<SDA_PIN)
#define SCL0() GPIO_ClearPinsOutput(SCL_PORT,1<<SCL_PIN)
#define SCL1() GPIO_SetPinsOutput(SCL_PORT,1<<SCL_PIN)
#define DIR_OUT() SDA_PORT->GDIR |= (1U <<SDA_PIN);
#define DIR_IN() SDA_PORT->GDIR &=~(1U << SDA_PIN);
实际逻辑分析仪抓取的格式
datasheet 规定的格式
这里主要通过读取partnum 寄存器进行IIC 通讯验证
bool BH1730_init(void)
{
// Verify if there is a BH1730 on this address
if((read8(BH1730_REG_PART_ID) >> 4) != BH1730_PART_NUMBER){
PRINTF("BH1730 not found\n");
return false;
};
// Reset
write8(BH1730_CMD_SPECIAL | BH1730_CMD_SPECIAL_SOFT_RESET, (1<<7));
// setGain
setGain(GAIN_X1);
}
gamma校正
人眼对亮度的感知符合gamma曲线,即对暗区的变化较敏感,对亮区的变化比较迟钝。详细的资料参考知乎的说明
https://zhuanlan.zhihu.com/p/475916892
我这里针对log 曲线设计了一个函数,横坐标为传感器读取的到光强参数,纵坐标为PWM 输出值,在低光强时,PWM调整速度变快,满足人的感知,在光强比较强时,PWM变化比较慢。
5.功能实现图片展示
显示屏会显示平滑过滤的光强
会根据当前的光强调整屏幕的亮度,具体调节细节参考log 曲线
6.遇到的难题及解决方法
1.pwm 初始化总是有问题,参考群友说明和datasheet ,手写了底层驱动,才实现功能初始化。
2.配置工具使用起来不方便,总是卡卡的,希望后续可以进行优化,像ST cubemx 操作起来简单易用些
3.IIC 配置感觉跟GPIO 没有关联性,需要手动配置,后续进行下实现,目前使用GPIO 模拟实现相关功能
7.未来规划及建议
1.发现platform 有rt1021 Zephyr 操作系统SDK ,后续开发可以采用此方案,底层开发比较费时费力,后续应用开发,应该更注重平台中间件复用及持续可维护性,单独采用芯片供应商的方案不太友好。