【TI MSPM0 MCU 焕新大作战】+课程3任务1:实现温度报警器
这次的任务是实现温度报警,关键点在于实际检测环境温度。活动提供了经典的Dallas18B20传感器。但凡搞单片机的,恐怕没几个人不知道Dallas18B20。一个原因是Dallas18B20是很经典的一个温度传感器、例子多,另外一个原因可能还和18B20的特殊通讯协议有关。在我的单片机学习过程中,大概也是唯一一次采用单总线通讯方式收发数据的经历。
18B20的资料,不多讲,网上可以找到的例子一大把。本次实验提供的是模组方式,已经配置好了外围电路,只需要用杜邦线简单连接到开发板上就行。本次实验为了方便显示调试信息和测试结果,除了之前用到的数码管模块,还加入了OLED显示。
OLED显示屏耗电量低,需要显示什么数据一次传递便好,不需要不停扫描。
开发板的电路原理图:
整个电路的简易结构图如下:


DS18B20的一些资料:
对于报警处理,我用的方式是:在采集到温度后,显示到OLED和数码管模块上,然后和警戒线比较、判断,超出报警线,驱动蜂鸣器报警。和18B20的通讯过程,要注意通讯时序的问题。
所有和18B20有关的处理代码,都放在主文件中了。18B20数据通讯接口,初始化时配置为输出,但在运行过程中根据通讯需要动态设置为输入或者输出口模式。
让DS18B20进行一次温度的转换,那具体的操作就是:
1、主机先作个复位操作,
2、主机再写跳过ROM的操作(0xCC)命令,
3、然后主机接着写个转换温度的操作命令(0x44),后面释放总线至少一秒,让DS18B20完成转换的操作。在这里要注意的是每个命令字节在写的时候都是低字节先写,例如CCH的二进制为11001100,在写到总线上时要从低位开始写,写的顺序是“0、0、1、1、0、0、1、1”。整个转换过程大概需要125ms的样子。
转换完成后,立即读取转换结果,这个处理同样需要三个步骤:
1、主机发出复位操作并接收DS18B20的应答(存在)脉冲。
2、主机发出跳过对ROM操作的命令(0xCC)。
4、主机发出读取RAM的命令(0xBE),随后主机依次读取DS18B20发出的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可。同样读取数据也是低位在前的。这两个数据,低8位在前,高八位在后,处理的时候要注意。从功能上讲,18B20读出来的数据,是包括负温度数据的。但测试室温场合,不可能有负数,所以在数据处理上可以更简单一些。
程序的处理逻辑如下:
1、定时器处理完成时间计数,
2、主程序的循环中检测定时周期信号,收到信号后,启动温度检测。将检测到的数据进行转换,显示到OLED上,同时转换为数码管模块可以显示的形式。
3、主程序的循环中不断显示温度数据到数码管上。
主处理代码:
int main(void) {
uint32_t pos=0, i=0, cnt=0, flag=0;
char temp_char[20]={'\0'};
char alert_flag=0;
/* Power on GPIO, initialize pins as digital outputs */
SYSCFG_DL_init();
// 设置定时器0中断优先级
NVIC_SetPriority(TIMER_0_INST_INT_IRQN, 1);
// 允许定时器0中断
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
// 启动定时器计数
DL_TimerG_startCounter(TIMER_0_INST);
//DL_GPIO_setPins(GPIOA_PORT, GPIOA_USER_LED_1_PIN | GPIOA_USER_LED_2_PIN | GPIOA_USER_LED_3_PIN | GPIOA_USER_TEST_PIN);
closeBeep();
OLED_Init();
OLED_Clear(0);
GUI_ShowCHinese(0, 16, (uint8_t *)"温度:", 16, 1);
cnt_1s_flag=0;
while (1) {
// 每秒读取一次温度
if (cnt_1s_flag==1) {
// 设置标志,表示读取传感器的处理进行中
cnt_1s_flag = 2;
// 获取温度
t=Ds18b20GetTemp();
// 如果温度大于报警线,则报警;恢复正常后,停止报警
if (t > TEMP_ALARM_H1) {
openBeep();
} else {
closeBeep();
}
if (old_t != t) {
old_t = t;
// 将温度转换为字符串
if (t > 0) {
sprintf(temp_char, "%3.1f", t);
} else {
sprintf(temp_char, "%-3.1f", t);
}
// 在OLED上显示温度数据
GUI_ShowString(48, 16, (uint8_t *)" ", 16, 1);
GUI_ShowString(48, 16, (uint8_t *)temp_char, 16, 1);
// 利用数码管模块显示温度。显示之前,先做转换处理,
// 1、将小数点和前面数字结合
// 2、所有要显示的数据,转换为数码管上显示用的笔段码
pos=0;
cnt=0; // 显示数据位数,为了调整显示数码管的位置用,忽略的话,是从最前面开始显示
for (i=0; i<8; i++) {
if (temp_char[i]=='\0') {
disp_segdat[pos]='\0';
break;
}
if (temp_char[i]=='-') {
// 负号对应的笔段码
disp_segdat[pos]=0xBF;
// 调整保存位置为下一个数码管的
pos++;
cnt++;
} else if (temp_char[i]=='.') {
// 前一个字符的段位码数据加上小数点位
disp_segdat[pos-1]=disp_segdat[pos-1] & 0x7F;
} else if (temp_char[i]>='0' && temp_char[i]<='9') {
// 数字0-9,取得字段码数据
disp_segdat[pos]=TAB_SEG[temp_char[i]-48];
// 调整保存位置为下一个数码管的
pos++;
cnt++;
}
}
}
// 清除1秒标志
cnt_1s_flag = 0;
//
}
// 发送字段码数据给数码管显示模块
// 方式1:循环一次只显示一个数码管,显示有闪烁
smpos=(smpos+1)%8;
if (disp_segdat[smpos]=='\0') {
smpos=-1;
} else {
// 显示的位置,可以根据cnt的值(显示数据的总位数)调整到任意位置
DispSegData(smpos, disp_segdat[smpos]);
delay_cycles(ms1*SMSEG_DISP_TIMES);
}
DL_GPIO_togglePins(GPIOA_PORT, GPIOA_USER_LED_3_PIN);
}
}定时器的中断处理代码:
// Timer0的中断处理,按照配置,每10ms完成一次中断,累计100次,得到1S的周期
void TIMER_0_INST_IRQHandler(void) {
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
case DL_TIMERG_IIDX_ZERO:
// 定时器0完成一次中断
if (cnt_1s_flag == 2) {
// 读取温度数据时,停止计算秒的计数
cnt_10ms=0;
} else if (cnt_1s_flag == 0) {
// 累计50次,500mS
cnt_10ms=(cnt_10ms+1)%50;
if (cnt_10ms==0) {
// 达到1秒钟
if (cnt_1s_flag == 0) {
cnt_1s_flag=1;
}
//DL_GPIO_togglePins(GPIOA_PORT, GPIOA_USER_LED_3_PIN);
}
}
break;
case DL_TIMERG_IIDX_LOAD:
case DL_TIMERG_IIDX_CC0_DN:
case DL_TIMERG_IIDX_CC1_DN:
case DL_TIMERG_IIDX_CC0_UP:
case DL_TIMERG_IIDX_CC1_UP:
case DL_TIMERG_IIDX_OVERFLOW:
default:
break;
}
}程序编好后,下载到开发板,接好各个部件,测试效果如下:
数码管的显示处理不好,闪烁明显,但不影响正确显示温度。
DS18B20处理部分的代码:
void Ds18b20Rst(void) {
// 设置TEMP_IO口为输出模式
DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
// 主机发送复位脉冲480us-960us
delay_us(750);
DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(15);
}
/*等待18b20响应
* 返回1:未检测到18b20
* 返回0:存在
*/
char Ds18b20Check(void) {
char retry=0;
// DL_GPIO_disableOutput(GPIOA, GPIOA_DS18B20_DIO_PIN);
// DL_GPIO_initDigitalInputFeatures(GPIOA_DS18B20_DIO_IOMUX,
// DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
// DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
//
// while(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)>0 && retry<200) {
// retry++;
// delay_us(1);
// }
// if(retry>=200) {
// return 1;
// }
// retry=0;
//
// while(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)==0 && retry<240) {
// retry++;
// delay_us(1);
// }
// if(retry>=240) return 1;
delay_us(125);
return 0;
}
/*从18b20读取一个位
* 返回值1/0
*/
char Ds18b20ReadBit(void) {
char data;
// 设置TEMP_IO口为输出模式
DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(2);
DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
// 设置TEMP_IO口为输入模式
DL_GPIO_disableOutput(GPIOA, GPIOA_DS18B20_DIO_PIN);
DL_GPIO_initDigitalInputFeatures(GPIOA_DS18B20_DIO_IOMUX,
DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
DL_GPIO_HYSTERESIS_DISABLE, DL_GPIO_WAKEUP_DISABLE);
delay_us(12);
if(DL_GPIO_readPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN)>0)
data=1;
else
data=0;
delay_us(50);
return data;
}
/*从18b20读取一个字节
* 返回值:读到的数据
*/
char Ds18b20ReadByte(void) {
char i,j,dat;
dat=0;
for(i=1;i<=8;i++) {
j=Ds18b20ReadBit();
dat=(j<<7)|(dat>>1);//低位在前
}
return dat;
}
/*写一个字节到Ds18b20
* dat:要写入的字节
*/
void Ds18b20WriteByte(char dat) {
char i;
char temp;
DL_GPIO_initDigitalOutputFeatures(GPIOA_DS18B20_DIO_IOMUX,
DL_GPIO_INVERSION_DISABLE, DL_GPIO_RESISTOR_PULL_UP,
DL_GPIO_DRIVE_STRENGTH_LOW, DL_GPIO_HIZ_DISABLE);
DL_GPIO_enableOutput(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
for(i=1;i<=8;i++) {
temp=dat&0x01;
dat=dat>>1;
//write 1
if (temp>0) {
DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(2);
DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(60);
} else {
DL_GPIO_clearPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(60);
DL_GPIO_setPins(GPIOA_PORT, GPIOA_DS18B20_DIO_PIN);
delay_us(2);
}
}
}
/*开始温度转换
*
*/
void Ds18b20Start(void) {
Ds18b20Rst();
Ds18b20Check();
// if (Ds18b20Check()>0) {
// GUI_ShowString(0, 0, (uint8_t *)"Start NG", 8, 1);
// return;
// }
Ds18b20WriteByte(0XCC);
Ds18b20WriteByte(0X44);
}
/*从Ds18b20得到温度值
* 精度0.1c
* 返回值:温度值(-550-1250)
*/
float Ds18b20GetTemp() {
char temp;
char TH=0,TL=0;
short tem;
float t;
Ds18b20Start();
Ds18b20Rst();
// if (Ds18b20Check()==0) {
// GUI_ShowString(0, 0, (uint8_t *)"GetTemp NG", 8, 1);
// return 999;
// }
Ds18b20Check();
Ds18b20WriteByte(0XCC);
Ds18b20WriteByte(0XBE);
TL=Ds18b20ReadByte();//LSB
TH=Ds18b20ReadByte();//MSB
GUI_ShowNum(20, 8, TH, 3, 8, 1);
GUI_ShowNum(50, 8, TL, 3, 8, 1);
if(TH>7) {
TH=~TH;
TL=~TL;
temp=0;//the temperature is negative
} else
temp=1;// the temperature is positive
tem=TH;//高八位
tem<<=8;
tem+=TL;//低八位
t=((float)tem*0.0625);
if(temp)
return t;
else
return -t;
}
我要赚赏金
