项目介绍
实物展示视频:
本项目基于2024年寒假在家一起练(带调试器的i.MX RT1021开发板)平台,设计的一款基于LVGL框架和FreeRTOS调度系统的PID温度控制系统。温湿度的采集采用NSHT30芯片,光照检测采用BH1730芯片。
实现的功能:
1.完成对环境和板面温湿度的采集和显示,通过触摸屏幕实现设定温度的调节,进入参数显示页面,可以观测到温度以大概1℃的幅度变化。
2.完成对环境温度的感知,并可以依据gamma视觉感知曲线修正屏幕光线的亮度,实现屏幕光线自适应。
设计思路
本项目的主体是设计关于RT1021的温控系统,为了达到温控目标,我们将项目的设计分为:算法设计、底层设计、抽象层设计。针对本项目采用将温度检测和加热模块集成在小区块的方式,在实际控制中发现加热模块的响应比较迅速,且由于温控系统的性能一定程度上受限于实际环境温度和散热条件,且加热系统天生具有热惯性,所以D的参与会引入噪音,使得控制效果不理想。我们将算法定为经典PI控制,板面在由冷制热和热态下的温度变化的两种控制情况下的实际效果是不一样的,往往需要一段时间来预热板子,所以在实际控制中,需要考虑抗积分饱和。由于温控电路只存在加热,所以设定温度必须大于环境温度,所以在实际控制中,设定温度增加外界环境约束,且对最终实际控制做限幅约束,在第一次工作时会保存第一次温度值作为温度设定的最低约束,且为了避免第一次采集温度的波动对后续的影响,将最低约束定在比当前温度高5℃,且限制板面温度最高为60℃。由于散热的条件很大程度影响温度的降低,所以当设定温度降低时,PI的控制本身很受限,为了实现目的,我们对PI控制采用步跃式,在很短周期和温差内,温度的控制可以看似对环境的依赖很小,且近似线性,这也是温控最常见的问题。而对于光线的控制,采用Gamma视觉感知曲线采点,对于不同的光线,查表直接获取对应的占空比,为了实现光线控制的柔和性,采用缓升降的方法。
底层的设计基于RT1021的SDK(MCUXpresso原生态)。
抽象层设计采用LVGL+FreeRTOS,借用MCUXpresso原生态的抽象层调用,高效实现控制。
硬件框图
基于板子的硬件平台,NSHT30和BH1730采用同一个IIC协议建立联系,加热电路和LCD背光均采用PWM的方式调压控制,实现加热电路的功率控制和LCD背光的无极控制。LVGL显示部分通过SPI与触摸屏芯片和LCD芯片建立联系,采用LVGL框架搭建基于触摸屏的页面交互显示。
软件流程图
整个软件设计分为:主函数、LVGL任务、传感器任务。主函数主要是初始化除LVGL之外的硬件,由于工程要兼容GUI Guider和MCUXpresso两者的图形化工具,LVGL的初始化和任务由GUI Guider生成,而其他的初始化与LVGL的关系不大,由MCUXpresso生成。所以LVGL的初始化放在LVGL任务中,传感器的初始化则直接由主函数生成。除了初始化,主函数还要负责FreeRTOS现场的创建和任务的调用,保障FreeRTOS的正常运行。LVGL任务则由GUI Guider生成,包括页面刷写、LVGL循环任务,可以通过API的调用创建任务,本项目中就创建了定时刷写的定时器任务。LVGL可以通过模拟操作实现编码器模拟按键功能,由于该方法实现的功能比较单一,所以在本项目中只是补充式的控制方式,主要还是触摸控制。传感器任务则由编程工程中调用FreeRTOS的API实现,采集和算法集成在一个任务,由于采用通讯协议的方式,所以几乎不存在噪音跳变的问题,将采集和控制在一个周期中运作,在实际验证下,效果尚佳。
硬件介绍
BH1730
上图是我从手册中总结出来的BH1730的编程逻辑,包括:控制流程图、指令格式、寄存器地址、物理值换算关系、示例。
BH1730是由作为基本电路的光电二极管、电流电压转换电路、A/D转换器、控制逻辑电路以及接口电路等构成的环境光亮度传感器IC,采用I^2C\ Bus协议与外界传输数据。
NSHT30
NSHT30是一款基于CMOS-MEMS的相对湿度和温度传感器,包括:电容式的相对湿度传感器、COMS温度传感器和信号处理器以及I^2C数字通信接口。
XPT2046
XPT2046是一款4线制电阻式触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压,可以作为辅助输入、温度测量和电池监测之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器。
LCD
图中是本项目所用的LCD的数据手册中提炼的,只进行基本的操作所需要考虑的内容,包括:时序、初始化、控制逻辑。
实现的功能及图片展示
功能1:温度控制
开机进入温度设置界面,可以通过触摸加减键或者通过编码器选择来调节设定温度,然后触摸NXP图标进入参数界面。
其中,Lux表明当前的环境亮度值,Temp表示当前的测量温度值,Hum表示当前的测量湿度值。
将设定温度调节到40度,并触摸NXP进入参数界面。
可以看到温度也被控制在40℃
功能2:基于gamma的亮度调节
开放在正常环境下,屏幕亮度自动调节到最高。
将手遮在光照传感器上方,LCD背光缓慢下降,最终降到最低亮度。
主要代码片段及说明
代码讲解视频:
NSHT30读取温湿度
do{
reVal = NSHT30_Write_Command();
vTaskDelay(100);
}while(reVal != kStatus_Success);
reVal = kStatus_Fail;
do{
reVal = NSHT30_Read_Data();
vTaskDelay(10);
}while(reVal != kStatus_Success);
详细通讯过程可以参考硬件介绍中的NSHT30的介绍,主要是IIC写入读写温湿度指令,然后等待一段时间,再IIC读取传感器传回的温湿度数据。
BH1730读取
do{
reVal = BH1730_Write_Command();
vTaskDelay(100);
}while(reVal != kStatus_Success);
reVal = kStatus_Fail;
do{
reVal = BH1730_Read_Data();
vTaskDelay(100);
}while(reVal != kStatus_Success);
详细通讯过程可以参考硬件介绍中的BH1730的介绍,主要是IIC写入读写亮度指令,然后持续读写,直到不再收到NACK,IIC读取传感器传回的亮度数据。
PID算法
/*定义结构体和公用体*/
typedef struct
{
uint8_t setpoint; //设定值
float proportiongain; //比例系数
float integralgain; //积分系数
float derivativegain; //微分系数
uint8_t lasterror; //前一拍偏差
uint8_t preerror; //前两拍偏差
uint8_t deadband; //死区
int result; //输出值
uint8_t maximum;//最大值
uint8_t minimum;//最小值
}PID;
PID Temp_PID_Paramer={
.proportiongain = 0.3; //比例系数
.integralgain = 0.02; //积分系数
.derivativegain = 0; //微分系数
.maximum = 60;//最大值
.minimum = 30;//最小值
};
void PIDRegulation(PID *vPID, uint8_t processValue)
{
uint8_t thisError;
uint8_t increment;
uint8_t pError,dError,iError;
thisError=vPID->setpoint-processValue; //得到偏差值
pError=thisError-vPID->lasterror;
iError=0;
dError=thisError-2*(vPID->lasterror)+vPID->preerror;
if(vPID->result>vPID->maximum)
{
if(thisError<=0)
{
iError=thisError;
}
}
else if(vPID->result<vPID->minimum)
{
if(thisError>=0)
{
iError=thisError;
}
}
else
{
iError=thisError;
}
increment=vPID->proportiongain*pError+vPID->integralgain*iError+vPID->derivativegain*dError; //增量计算
vPID->preerror=vPID->lasterror; //存放偏差用于下次运算
vPID->lasterror=thisError;
vPID->result+=increment;
if(vPID->result > 100)
{
vPID->result = 100;
}else if(vPID->result < 10)
{
vPID->result = 10;
}
}
void Temp_PID_Ctr(uint8_t New_setpoint)
{
PID Temp_PID_Paramer={
.setpoint = New_setpoint; //设定值
};
PIDRegulation(Temp_PID_Paramer,Sensor_Value.NSHT30_Temp_Value);
}
uint8_t NewTempPoint = 0;
Tick++;
if(Tick == 5)
{
if(NewTempPoint < counter)
NewTempPoint ++;
else
NewTempPoint = counter;
Tick = 0;
}
Temp_PID_Ctr(NewTempPoint);
采用积分遇限消弱法(clamping)进行PID抗饱和算法,且做了PWM限幅(控制在30%~100%)。当PWM输出大于60%,PWM的积分只接收消弱,当PWM输出小于30%,PWM的积分只接收增强。
gamma亮度曲线拟合与缓变
基于视觉曲线(图中为归一化之后),建立亮度与屏幕的关系。由于IPS屏的亮度有极值,且保证舒适性,在多次实验之后,我们拟合出如下图的分区亮度调节的曲线图。
其中:f(x)表示屏幕背光的占空比,单位为%
x表示当前的环境光线亮度,单位为Lux
if(Sensor_Value.BH1730_Lux_Value < 5000)
{
LCD_Percent = 10;
}else if(Sensor_Value.BH1730_Lux_Value >= 40000)
{
LCD_Percent = 100;
}else
{
LCD_Percent = 10+((((Sensor_Value.BH1730_Lux_Value-5000)/1000.0)/(35.0))*90);
}
if(LCD_Percent_Jg < LCD_Percent-15)
{
LCD_Percent_Jg +=5;
}else if(LCD_Percent_Jg < LCD_Percent)
{
LCD_Percent_Jg +=1;
}
else if(LCD_Percent_Jg > LCD_Percent+15)
{
LCD_Percent_Jg -=5;
}
else if(LCD_Percent_Jg > LCD_Percent)
{
LCD_Percent_Jg -=1;
}
遇到的主要难题及解决方法
问题1:温度控制的非线性
由于温度是典型的非线性控制,传统的PID算法适合在线性非时变系统中去做线性跟踪。而本项目的温度的控制本身受材料的热特性、环境温差、散热媒介等多因素影响,温度控制系统是变参数、有时滞和随机干扰的动态系统,纯经典PID控制并不适合精细的控制温度,常见会采用广义预测PID控制算法,但算法过于复杂。
解决方法
为了解决问题,我们在一个很短周期内,将温度控制近似为线性控制,在一个周期内,温度只在很短的温度范围变化。从而通过拉长温控周期,牺牲时间为代价来保全温度控制的精度,实现温度的牵引式的爬升和降低。
问题2:视觉曲线的复杂性
视觉曲线一直是一个复杂问题,涉及人的视觉结构、LCD色素的显示以及HSV模型等,本身是一个很复杂的系统,依据视觉模型去建立亮度调节是一个涉及多变量的求解问题。
解决方法
采用最典型的Gamma模型,依据多次实验以及多人的使用体验来选择一个合适的参数,在粗略的控制上大体的满足屏幕调节的舒适性。