基于MSPM0L1306的智能温度调节展示系统
任务要求
1. 设计一个基于温度变化的展示系统。
2. 热敏电阻检测温度变化。
3. 彩色LCD显示当前温度和设定阈值。
4. LED和RGB LED根据温度高低展示不同颜色。
5. 按键用于设置温度阈值。
6. 电位计调节LED显示的模式和强度。
完成的功能
1. 使用扩展版上NST112-DUST温度传感器检测温度变化。
2. 使用MSPM0L1306内部温度传感器检测温度变化。
3. 彩色LCD显示当前扩展版上NST112-DUST温度传感器检测的温度。
4. 彩色LCD显示当前MSPM0L1306内部温度传感器检测的温度。
5. 彩色LCD显示温度阈值(上限,初始默认为30℃,可以以1℃为步长进行修改)。
6. MSPM0L1306上RGB LED根据扩展版上NST112-DUST温度传感器检测的温度高低展示不同颜色。
7. 扩展板上RGB LED根据扩展版上NST112-DUST温度传感器检测的温度是否超过阈值亮灭(超过阈值时亮红灯,未超过阈值则不亮)。
8. MSPM0L1306上RGB LED根据MSPM0L1306内部温度传感器检测的温度高低展示不同颜色。
9. 扩展板上RGB LED根据MSPM0L1306内部温度传感器检测的温度是否超过阈值亮灭(超过阈值时亮红灯,未超过阈值则不亮)。
10. 通过MSPM0L1306上按键S1设置温度阈值(短按则阈值温度加1℃,长按则阈值温度减1℃)。
11. 彩色LCD显示当前MSPM0L1306上RGB LED和扩展板上RGB LED显示对应的温度是扩展版上NST112-DUST温度传感器测得还是MSPM0L1306内部温度传感器测得(当前对应的温度传感器,彩色LCD上显示的其名称和温度值为红色)。
12. 电位计调节MSPM0L1306上RGB LED和扩展板上RGB LED显示对应的温度的来源(按下即可在两者之间进行切换)。
13. 彩色LCD显示当前扩展版上NST112-DUST温度传感器处状态(未加热)。
实现的功能有一部分因实际硬件限制或扩展内容使用的端口冲突与任务要求有一些不同:
1. 扩展板电位计损坏,只有是否按下才会输出不同电压值,而拧动则输出电压没有变化,故没有采用拧动控制LED显示的模式。
2. 由于与扩展版上NST112-DUST温度传感器通信的IIC总线端口冲突,没有使用MSPM0L1306上的红色LED,而是用扩展版上RGB LED。
3. 由于TI官网所给的该开发平台热敏电阻相关数据缺失,及后续扩展功能中热敏电阻电路输出电压端口被占用,热敏电阻电路精度不如另外两种方法(ADC重复读取时数据变化更大)等原因,故未使用该方法测量温度。
设计思路
使用IIC总线读取NST112-DUST内部温度寄存器数据,得到其测量的温度值。
ADC工作在序列重复转换模式,同时读取MSPM0L1306内部温度传感器输出电压和扩展板电位计输出电压,转换的各512个12bit数据取平均保证精度,进而转化为温度值和当前电位计状态(是否按下)。
使用软件模拟SPI总线时序控制彩色LCD显示。
使用GPIO引脚电平上升沿触发中断得到按键S1开始按下状态,进而计时,控制温度阈值。
硬件框图
MSPM0L1306内部温度传感器在单片机芯片内部,只需更改ADC采样通道即可读取其输出电压(Channel 11),故在硬件框图中无法看到。
软件流程图 图片展示
扩展版上NST112-DUST温度传感器
MSPM0L1306内部温度传感器
调高温度阈值
降低温度阈值
主要代码片段及说明
彩色LCD屏幕驱动
由于官方所给的软件SPI对引脚电平的改变已进行了宏定义,所有函数中均使用宏定义的代码,故只需改动头文件的宏定义和引脚的初始化即可完成移植。
引脚使用sysconfig配置,主动生成代码,此处不多加赘述。
宏定义改动如下:
//-----------------LCD端口定义----------------
#define LCD_SCLK_Clr() DL_GPIO_clearPins(LCD_SPI_PORT, LCD_SPI_LCD_SCL_PIN)//SCL=SCLK
#define LCD_SCLK_Set() DL_GPIO_setPins(LCD_SPI_PORT, LCD_SPI_LCD_SCL_PIN)
#define LCD_MOSI_Clr() DL_GPIO_clearPins(LCD_SPI_PORT, LCD_SPI_LCD_SDA_PIN)//SDA=MOSI
#define LCD_MOSI_Set() DL_GPIO_setPins(LCD_SPI_PORT, LCD_SPI_LCD_SDA_PIN)
#define LCD_RES_Clr() DL_GPIO_clearPins(LCD_SPI_PORT, LCD_SPI_LCD_RES_PIN)//RES
#define LCD_RES_Set() DL_GPIO_setPins(LCD_SPI_PORT, LCD_SPI_LCD_RES_PIN)
#define LCD_DC_Clr() DL_GPIO_clearPins(LCD_SPI_PORT, LCD_SPI_LCD_DC_PIN)//DC
#define LCD_DC_Set() DL_GPIO_setPins(LCD_SPI_PORT, LCD_SPI_LCD_DC_PIN)
#define LCD_CS_Clr() DL_GPIO_clearPins(LCD_SPI_PORT, LCD_SPI_LCD_CS_PIN)//CS
#define LCD_CS_Set() DL_GPIO_setPins(LCD_SPI_PORT, LCD_SPI_LCD_CS_PIN)
//#define LCD_BLK_Clr() LCD_BLK=0//BLK
//#define LCD_BLK_Set() LCD_BLK=1
由于实际扩展版电路中LED-引脚下拉接地,默认低电平(通电就打开背光),此处不再定义,代码中其操作也已删除。
NST112-DUST驱动
由于该芯片默认工作模式即符合需求,故无需初始化,只需直接读温度寄存器即可。
uint16_t nst112_readRegister(uint8_t reg_address) {
uint16_t value=0;
uint8_t temp[2]={0x00,0x00};
// if (HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)NST112_I2C_ADDRESS_W, ®_address, 0x01,10000) != HAL_OK)
// {
// /* Error_Handler() function is called when error occurs. */
// Error_Handler();
// }
//
// /*##- Wait for the end of the transfer #################################*/
// /* Before starting a new communication transfer, you need to check the current
// state of the peripheral; if it�s busy you need to wait for the end of current
// transfer before starting a new one.
// For simplicity reasons, this example is just waiting till the end of the
// transfer, but application may perform other tasks while transfer operation
// is ongoing. */
// while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
// {
// }
//
//
//
// /* Read data back from the I2C slave */
//
// if (HAL_I2C_Master_Receive(&hi2c1, (uint16_t)NST112_I2C_ADDRESS_R, temp, 0x02,10000) != HAL_OK)
// {
// /* Error_Handler() function is called when error occurs. */
// Error_Handler();
// }
//
// /*##- Wait for the end of the transfer #################################*/
// /* Before starting a new communication transfer, you need to check the current
// state of the peripheral; if it�s busy you need to wait for the end of current
// transfer before starting a new one.
// For simplicity reasons, this example is just waiting till the end of the
// transfer, but application may perform other tasks while transfer operation
// is ongoing. */
// while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
// {
// }
/*
* Fill FIFO with data. This example will send a MAX of 8 bytes since it
* doesn't handle the case where FIFO is full
*/
DL_I2C_fillControllerTXFIFO(I2C_INST, ®_address, 1);
/* Wait for I2C to be Idle */
while (!(DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));
/* Send the packet to the controller.
* This function will send Start + Stop automatically.
*/
DL_I2C_startControllerTransfer(I2C_INST, NST112_I2C_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_TX, 1);
/* Poll until the Controller writes all bytes */
while (DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
/* Trap if there was an error */
if (DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_ERROR) {
__BKPT(0);
}
/* Wait for I2C to be Idle */
while (!(DL_I2C_getControllerStatus(I2C_INST) & DL_I2C_CONTROLLER_STATUS_IDLE));
/* Add delay between transfers */
delay_cycles(1000);
/* Send a read request to Target */
DL_I2C_startControllerTransfer(I2C_INST, NST112_I2C_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_RX, 2);
/*
* Receive all bytes from target. LED will remain high if not all bytes
* are received
*/
for (uint8_t i = 0; i < 2; i++) {
while (DL_I2C_isControllerRXFIFOEmpty(I2C_INST));
temp[i] = DL_I2C_receiveControllerData(I2C_INST);
}
value=temp[0];
value=value<<8;
value=value | temp[1];
return value;
}
int nst112_init(void) {
return 0;
}
float nst112_readTemp(void) {
uint16_t Origin_Temp = nst112_readRegister(TEMP_REG);
float Temp=0;
Temp = (char)(Origin_Temp>>8);
unsigned char decimal=(unsigned char)Origin_Temp;
if(Temp>=0){
Temp += (float)(decimal>>4)/16;
}
else
{
Temp -= (float)(((~decimal)>>4)+1)/16;
}
return Temp;
}
注释了的代码为移植前硬件IIC读写代码,将其照着TI所给例程中硬件IIC相关代码配置、改动即可成功移植。
判断模式并改变RGB LED颜色显示当前温度
MSP0L1306上RGB LED利用PWM波占空比的改变,根据温度变化改变颜色,三种颜色对应PWM波的频率相同,占空比由每次采集的温度进行计算得到,此方法使其颜色在温度改变时连续的渐变变化,不会跳变至另一颜色,从低温到高温颜色变化为蓝->绿->红。
x=Curr_Temp;
if(model==1)
{
x=t_sample;
}
y_B = 1599*(1-exp(-x * x / 10));
y_G = 1599*(1-exp(-(x-20) * (x-20) / 100));
y_R = 1599*(1-exp(-(x-40) * (x-40) / 100));
DL_TimerG_setCaptureCompareValue(PWM_0_INST, (int)y_R, GPIO_PWM_0_C0_IDX);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, (int)y_B, GPIO_PWM_0_C1_IDX);
DL_TimerG_setCaptureCompareValue(PWM_1_INST, (int)y_G, GPIO_PWM_1_C1_IDX);
按键触发的中断处理函数
此处对按下做了消抖和计时,进而改变温度阈值。
void GROUP1_IRQHandler(void)
{
// DL_GPIO_togglePins(LED_PORT,LED_LED_warning_PIN);
switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) {
case GPIO_SWITCHES_INT_IIDX:
delay_cycles(640000);
if(DL_GPIO_readPins(GPIO_SWITCHES_PORT, GPIO_SWITCHES_switch_PIN) && flag){
temp_YuZhi++;
int time_an_xia=0;
while(DL_GPIO_readPins(GPIO_SWITCHES_PORT, GPIO_SWITCHES_switch_PIN))
{
delay_cycles(640000);
time_an_xia++;
}
if(time_an_xia>=15)
{
temp_YuZhi-=2;
}
}
break;
}
}
遇到的主要难题及解决方法
1. MSPM0L1306内部温度传感器有温度传感器稳定时间(tSET, TS)2.5μs,这是由 ADC 进行测量时,温度传感器稳定所需的时间上限。它可用于指定测量温度传感器时的最短 ADC 采样时间。我最开始未认真阅读数据手册,没有注意到,导致采样时间设置过短时ADC采样数据出现问题但浪费大量时间才找出问题。
2. 彩色LCD显示屏驱动代码移植时,我本来是用硬件SPI,但屏幕始终无法正常显示,后直接将对应引脚改为GPIO输出引脚,使用软件模拟SPI,但仍失败,最后改用其它端口软件模拟SPI时序才成功,由于手头无相关测量仪器,推测是由于存在部分杜邦线或电路板断路导致。
3. 为彩色LCD显示屏驱动代码扩充汉字字库时,发现汉字无法正常打印,后调试发现过程中汉字译码为3个字节,而代码中对应结构体元素仅两个字节,存储时编码错位,故无法检索出,更不能打印。
未来的计划
1. 将彩色LCD显示屏通信驱动程序改为硬件SPI + DMA传输,以提高刷新率
2. 优化ADC采样数据处理算法,提高测量出温度的准确度和稳定性
3. 进一步查找该板上热敏电阻的数据,添加利用其测温部分
4. 添加摇杆控制,利用摇杆控制LCD屏上光标,使操作更为便捷
5. 增加温度阈值调整的精度和方式,使其可选择改变十位、个位及十分位等
6. 添加加热功能,可选择加热至多少度并保持或一直加热
7. 美化显示界面,添加更多显示模式
建议
1. 希望电子森林能及时更新网页所给数据,尤其是自己做的电路板(如本次使用的扩展板等)上面使用的器件数据,本次活动中MAP0L1306资料页上(基于TI MSPM0L1306的综合开发平台 (eetree.cn),网址https://www.eetree.cn/platform/2588)扩展板元器件部分LCD显示屏(ZJY144SN005)链接就已失效,在淘宝上查找到的显示屏我也不敢确定就是一样的,后来在去年的活动页面里面才找到该显示屏的资料,最开始移植出现问题时也多次怀疑驱动代码不匹配,浪费了大量时间又去找相关驱动进行对比,对我造成了很大困扰。
2. 建议电子森林优化各次活动开源的项目的展示和检索,以去年的该活动为例,在项目基地的项目分享中仅能看到一小部分开源的项目,点开后在最后的猜你喜欢处又能找到不在项目基地中出现的一些项目;且最多只能按照活动筛选,建议加上年份、平台、项目,只需在数据库的各个项目上添加数个标签,再根据标签建立筛选的选择项即可,或建立搜索树(森林),根据用户选择的限制条件选择路径和搜索的深度,在叶节点处存储各项目链接或指向该项目的标志,整棵树向下包含,若搜索在非叶节点处停止(用户没有选择满所有筛选的条目),则显示其所有子树的叶节点的项目即可。