1 项目需求
设计一个环境监测系统,利用热敏电阻和光传感器监测温度和光照强度。使用彩色LCD显示实时数据,并结合LED和RGB LED以不同颜色表示不同的环境条件。通过按键选择监测模式,电位计调整LED亮度和颜色变化速率。
2 完成的功能及达到的性能
2.1热敏电阻和光传感器的读取
在MSPM0L1306的主板上就自带了温度传感器和光传感器,他们都是通过ADC的方式来读取。
打开原理图,便可以发现采用的是TMP6131电阻进行温度的检测,随着温度的变化,电阻上的电压也会随之而发生变化,这样可以在PA15上进行电压的检测,尽可以知道温度的大小。据手册上说明,当温度是25°时其电压为1.6V,而TMP6131的电阻值随温度变化为6400ppm/°C,这样就可以通过ADC知道温度大小。
其次是光电传感器,光电传感器有两种工作模式,即光伏模式和光导模式。由于PA25,22,24都与单片机内部的一个放大器相连接,所以这个光电传感器是工作在光伏模式下的。这样,可以通过检测PA24上面的电压,也就是内部放大器的输出端来知道此时光照的强度。
2.2电位计和摁键的检测
硬禾学堂给的IO拓展板上的电位计和摁键是通过电阻权网络的形式进行分压的。通过读取电压的大小就可以知道选择的是哪一种摁键,这样的好处是大大减少了IO口的占用,只需要一个ADC端口就行。可以通过理论的计算来知道不同摁键对应的电压的大小,从而知道ADC转换后值的大小。但我选择直接通过测试来得到不同摁键的大致范围,事实说明在不追求精度下也是可以实现的。
2.3LCD的显示
可以从电路上知道LCD采用的是IIC协议,在以前的开源项目里可以找到它的配置底层文件。
2.4不同LED灯的颜色代表的环境条件
光的三原色分别是红绿蓝,控制对应灯的亮度就可以组合成不同的颜色,为了方便,我只选取了7种颜色来代表不同的环境条件。
亮度低 | 亮度中 | 亮度高 | |
---|---|---|---|
温度低 | 蓝 | 青 | 白 |
温度中 | 绿 | 黄 | 白 |
温度高 | 红 | 紫 | 白 |
2.5不同监测模式的切换
可以通过摁键来进行检测模式的切换,我一共设置了5种检测模式,具体功能如下表所示。
模式名称 | 模式功能 | |
---|---|---|
模式1: | 自动监测 | 自动监测环境的温度和亮度 |
模式2: | 手动监测 | 只有受到摁键触发才会检测 |
模式3: | 温度只读 | 只对温度进行监测 |
模式4: | 亮度只读 | 只对亮度进行监测 |
模式5: | 设置 | 设置温度和亮度的环境阈值 |
3.芯片初始化设置
TI的新款MSPM0L1306可以通过CCS上的软件进行很方便的初始化设置,包括了GPIO,SYSTICK,ADC,OPA,TIMER等等。由于操作方便,就不再多介绍,但是我想提及在测试过程中遇到的一个坑。就是由于我一开始在ADC监测上采用了连续执行的方式,而ADC的检测非常迅速,会影响LCD的IIC协议,导致屏幕显示不了。所以我使用了软件定时触发ADC检测,CCS上的配置如下
而定时器选择一个1ms定时就可以了。
其次要在初始化程序里进行修改
4.软件程序介绍
整个软件程序的流程如下:
4.1初始化程序
初始化程序除了之前讲到的LCD显示问题外,还需要对LED的亮度以及环境阈值进行设置。由于需要控制LED的温度,所以我才用了PWM对LED进行控制。其最大值为500。
SYSCFG_DL_initPower();
SYSCFG_DL_GPIO_init();
SYSCFG_DL_SYSCTL_init();
LCD_Init();
LCD_Fill(0,0,128,128,RED);
LCD_ShowString(5,5,"let us have fun",RED,WHITE,12,0);
SYSCFG_DL_OPA_0_init();
SYSCFG_DL_ADC_init();
SYSCFG_DL_TIMER_0_init();
SYSCFG_DL_TIMER_1_init();
SYSCFG_DL_PWM_0_init();
SYSCFG_DL_PWM_1_init();
SYSCFG_DL_SYSTICK_init();
NVIC_EnableIRQ(ADC_INST_INT_IRQN);
NVIC_EnableIRQ(TIMER_1_INST_INT_IRQN);
DL_TimerG_startCounter(TIMER_0_INST);
DL_TimerG_startCounter(TIMER_1_INST);
DL_TimerG_startCounter(PWM_0_INST);
DL_TimerG_startCounter(PWM_1_INST);
DL_SYSTICK_disable();
DL_TimerG_setCaptureCompareValue(PWM_0_INST, 500, GPIO_PWM_0_C0_IDX); //blue
DL_TimerG_setCaptureCompareValue(PWM_0_INST, 500, GPIO_PWM_0_C1_IDX);//green
DL_TimerG_setCaptureCompareValue(PWM_1_INST, 500, GPIO_PWM_1_C0_IDX); //red
tem_low=5.5;
tem_high=10.5;
ill_low=5;
ill_high=10;
4.2摁键检测
为了防止摁键的重复检测以及摁键之间的冲突,我使用了一个75ms的滴答计时器,当发现有摁键摁下的时候便会开始定时器,在定时器定时期间不会再对摁键进行检测。其实就相当于是一个75ms的定时,但是这样做可以不影响主程序的执行。
if(stop_f==0)
{
if(temp3<2700)
{
stop_f=1;
if(temp3>2575)chb_f=1;
else
{
if(temp3>2400)cha_f=1;
else
{
if(temp3>2000&&press_h==0){press_f=1;press_h=1;}
else
{
if(temp3<900&&temp3>800&&key_h==0){key_f=1;key_h=1;}
}
}
}
}
else {key_h=0;press_h=0;}
}
if(press_f==1&&cha_f==1)press_f=cha_f=0;
if(press_f==1&&chb_f==1)press_f=chb_f=0;
if(chb_f==1&&cha_f==1)chb_f=cha_f=0;
if(stop_f=0) DL_SYSTICK_disable();
if(stop_f=1) DL_SYSTICK_enable();
4.3ADC检测
MSPM0L1306只有一个12位的ADC,可以放置多个通道来进行多个模拟数据的检测。当然ADC的中断程序只有一个,所以需要在中断程序中进行不同的通道标志位检测从而知道对应的模拟数据。
void ADC_INST_IRQHandler(void)
{
switch (DL_ADC12_getPendingInterrupt(ADC_INST)) {
case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
temp1_a[temp1_f]=DL_ADC12_getMemResult(ADC_INST, DL_ADC12_MEM_IDX_0);
temp1_f++;
if(temp1_f>19)temp1_f=0;
break;
case DL_ADC12_IIDX_MEM1_RESULT_LOADED:
temp2_a[temp2_f]=DL_ADC12_getMemResult(ADC_INST, DL_ADC12_MEM_IDX_1);
temp2_f++;
if(temp2_f>19)temp2_f=0;
break;
case DL_ADC12_IIDX_MEM2_RESULT_LOADED:
temp3_a[temp3_f]=DL_ADC12_getMemResult(ADC_INST, DL_ADC12_MEM_IDX_2);
temp3_f++;
if(temp3_f>19)temp3_f=0;
break;
default:
break;
}
}
4.4模式选择
由于不同的模式有不同的检测要求,所以需要对各个模式区分开来,并执行对应的程序内容。
switch(mode)
{
case 0:LCD_ShowString(5,22,"mode:auto ",RED,WHITE,12,0);break;
case 1:LCD_ShowString(5,22,"mode:manual ",RED,WHITE,12,0);break;
case 2:LCD_ShowString(5,22,"mode:onlytem",RED,WHITE,12,0);break;
case 3:LCD_ShowString(5,22,"mode:onlyill",RED,WHITE,12,0);break;
case 4:LCD_ShowString(5,22,"mode:setting",RED,WHITE,12,0);break;
}
if(mode==0)
{
temperature = ((float)((float)temp1/4095)*3.30-1.60)/0.0064+10.0;
illumination = temp2 /100 ;
}
if(mode==1)
{
if(press_f)
{
temperature = ((float)((float)temp1/4095)*3.30-1.60)/0.0064+10.0;
illumination = temp2 /100 ;
}
}
if(mode==2)
{
temperature = ((float)((float)temp1/4095)*3.30-1.60)/0.0064+10.0;
illumination = ill_low-1 ;
}
if(mode==3)
{
temperature = (tem_high+tem_low)/2;
illumination = temp2 /100 ;
}
4.5LED调节
由于要求LED的亮度和变化速度可以通过电位计来进行调整,亮度调节可以通过PWM的形式来进行,而变化速度就有点麻烦。首先就是如何对LED的变化速度进行控制,因为不可以像IO口那样要么高要么低,所以需要用一个定时器,让之前的LED的PWM逐渐降低,而要显示的PWM值逐渐升高,而因为全色LED有三个LED,如果让他们同时变化观感,即同时升同时降低就非常糟糕。所以我采用的是记录上一个LED的状态,并与下一个需要变化的状态进行比较,而LED分为四种情况进行变化:上次不亮这次不亮,上次亮这次不亮,上次亮这次亮,上次亮这次不亮。这种方法可能比较繁琐,还希望有人能提出更好的方法。
if(b_f)
{
if(b_l)DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_num, GPIO_PWM_0_C0_IDX); //blue
else DL_TimerG_setCaptureCompareValue(PWM_0_INST, actual_num, GPIO_PWM_0_C0_IDX);
}
else
{
if(b_l)DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_num-actual_num, GPIO_PWM_0_C0_IDX);
else DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, GPIO_PWM_0_C0_IDX);
}
if(g_f)
{
if(g_l)DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_num, GPIO_PWM_0_C1_IDX);//green
else DL_TimerG_setCaptureCompareValue(PWM_0_INST, actual_num, GPIO_PWM_0_C1_IDX);
}
else
{
if(g_l)DL_TimerG_setCaptureCompareValue(PWM_0_INST, pwm_num-actual_num, GPIO_PWM_0_C1_IDX);
else DL_TimerG_setCaptureCompareValue(PWM_0_INST, 0, GPIO_PWM_0_C1_IDX);
}
if(r_f)
{
if(r_l)DL_TimerG_setCaptureCompareValue(PWM_1_INST, pwm_num, GPIO_PWM_1_C0_IDX); //red
else DL_TimerG_setCaptureCompareValue(PWM_1_INST, actual_num, GPIO_PWM_1_C0_IDX);
}
else
{
if(r_l)DL_TimerG_setCaptureCompareValue(PWM_1_INST, pwm_num-actual_num, GPIO_PWM_1_C0_IDX);
else DL_TimerG_setCaptureCompareValue(PWM_1_INST, 0, GPIO_PWM_1_C0_IDX);
}
5.未来的计划建议
MSPM0L1306作为一款去年年底才发布的单片机,还是有很多的地方我没有发挥出来,我希望能在以后的项目中使用这款单片机,继续探索他的更多有趣的地方。