本文的主要开发内容是对温度传感器的编程驱动以及实现温度报警器功能,温度报警使用有源蜂鸣器实现,首先需要设置温度传感器与MCU的引脚连接。下图为温度传感器的电路原理图、引脚功能定义,所用到的温度传感器是DS18B20,这是一颗温度检测分辨率达0.0625C°且支持单总线通信的传感器芯片。
DS18B20原理图:
有源蜂鸣器原理图:
DS18B20概述:DS18B20是美国DALLAS半导体公司生产的单总线数字温度传感器,其可直接将温度转化成数字信号输出,具有体积小、低功耗、抗干扰能力强、精度高等优点。DS18B20采用1-Wire通信即仅采用一根数据线与微控制器进行通信。该传感器的温度检测范围为-55℃至+125℃。
其工作特性:
独特的1-Wire接口只需要一个端口引脚用于通信。
多路采集能力使得分布式温度采集应用更加简单。
无需外围器件。
可以采用数据线供电,供电范围为3.0V至5.5V。
温度可测量范围为:-55℃到+125℃(-67℉至+125℉)。
内部温度采集精度可以由用户自定义为9-Bit至12-Bit。
12Bit的温度采集精度转换时间最大为750ms。
用户可自定义非易失性的温度报警设置。
报警搜索命令识别并寻址温度超出编程限值的设备(温度报警条件)。
应用于温度控制、工业系统、民用产品、温度传感器或者任何温度检测系统中。
OneWire总线通信机制:
DS18B20需要遵循严格的时序通信才能正确写入、读取数据,如上图所示:初始化DS18B20需要发送不小于480us的低电平信号,然后拉高总线信号,拉高时间不小于15us。在之后480us内及时判断DS18B20的返回脉冲数据,判断器件是否有效。
写时隙时序图:
读时隙时序图:
syscfg配置:
下载链接syscfg.zip
源代码:
#include "ti_msp_dl_config.h" #include <math.h> #define DS18B20_DQ_IN_Read() DL_GPIO_readPins(PORT_TempSensor_PORT,PORT_TempSensor_PIN_TempSensor_PIN) #define delay_us 32 #define NUM_MAXLENGTH 8 uint8_t Num_List[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf};//数码管数字 volatile float temp=0.0; uint8_t Reserved_points=4; //默认保留4位小数 uint8_t temp_level[]={28,29};//三档温度报警 /** * @brief DS18B20时钟上升沿配置 * * @param[in] 外设端口PORTx、延时t1、延时t2 * */ void DS18B20_OneWireRisingEdge_Config(GPIO_Regs* PORTx,uint32_t PINx,uint32_t t1,uint32_t t2){ DL_GPIO_clearPins(PORTx, PINx); delay_cycles(delay_us*t1); DL_GPIO_setPins(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); delay_cycles(delay_us*t2); } /** * @brief 单总线配置为输入模式 * * @param[in] None * */ void DS18B20_DQ_IN(){ //配置通信引脚为输入模式 DL_GPIO_disableOutput(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); DL_GPIO_initDigitalInput(PORT_TempSensor_PIN_TempSensor_IOMUX); DL_GPIO_setPins(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); DL_GPIO_enableOutput(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); } /** * @brief 单总线配置为输出模式 * * @param[in] None * */ void DS18B20_DQ_OUT(){ //配置通信引脚为输出模式 DL_GPIO_disableOutput(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); DL_GPIO_initDigitalOutput(PORT_TempSensor_PIN_TempSensor_IOMUX); DL_GPIO_setPins(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); DL_GPIO_enableOutput(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN); } /** * @brief 复位DS18B20 * * @param[in] None * */ void DS18B20_RESET(){ DS18B20_DQ_OUT(); DS18B20_OneWireRisingEdge_Config(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN,750,60); } /** * @brief 判断DS18B20是否存在 * * @param[in] None * */ uint8_t DS18B20_CHECK(){ uint8_t counters=0; DS18B20_DQ_IN(); while(DS18B20_DQ_IN_Read()&&counters<200){ counters++; delay_cycles(delay_us*1); } if(counters>100) //超时未检测到器件,返回1 return 1; else counters=0; while(!(DS18B20_DQ_IN_Read())&&counters<240){ counters++; delay_cycles(delay_us*1); } if(counters>240) //检测到器件但总线电平持续拉低,返回2 return 2; else // DL_UART_transmitData(UART_0_INST,counters); return 0; //检测到器件且总线电平有效,返回0 } /** * @brief 向DS18B20写入字节数据 * * @param[in] data字节数据 * */ void DS18B20_WRITE_Byte(uint8_t data){ uint8_t byte=data; DS18B20_DQ_OUT(); for(uint8_t i=1;i<=8;i++){ if(byte&0x01){ DS18B20_OneWireRisingEdge_Config(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN,2,60); } else{ DS18B20_OneWireRisingEdge_Config(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN,60,2); } byte=byte>>1; } } /** * @brief 从DS18B20读取比特数据 * * @param[in] None * */ uint8_t DS18B20_READ_Bit(){ uint8_t bit; DS18B20_DQ_OUT(); DS18B20_OneWireRisingEdge_Config(PORT_TempSensor_PORT, PORT_TempSensor_PIN_TempSensor_PIN,2,5); DS18B20_DQ_IN(); delay_cycles(delay_us*12); if(DS18B20_DQ_IN_Read()) bit=1; else bit=0; delay_cycles(delay_us*50); return bit; } /** * @brief 从DS18B20读取字节数据 * * @param[in] None * */ uint8_t DS18B20_READ_Byte(){ uint8_t bit=0; uint8_t byte=0; for(uint8_t i=1;i<=8;i++){ bit=DS18B20_READ_Bit(); byte|=bit<<(i-1); } return byte; } /** * @brief 启用DS18B20 * * @param[in] None * */ void DS18B20_START(void){ DS18B20_RESET(); DS18B20_CHECK(); DS18B20_WRITE_Byte(0xcc); DS18B20_WRITE_Byte(0x44); DS18B20_RESET(); DS18B20_CHECK(); DS18B20_WRITE_Byte(0xcc); DS18B20_WRITE_Byte(0xbe); } /** * @brief 初始化DS18B20 * * @param[in] None * */ uint8_t DS18B20_INIT(void){ DS18B20_RESET(); return DS18B20_CHECK(); } /** * @brief 获取DS18B20温度数据(小数点数据) * * @param[in] None * */ float DS18B20_GetTemp(void){ float temp_value=0.0; uint8_t temp_msb,temp_lsb=0; uint16_t temp=0; DS18B20_START(); //采样温度值 temp_lsb=DS18B20_READ_Byte(); temp_msb=DS18B20_READ_Byte(); temp=(temp_msb<<8)+temp_lsb; if((temp&0xF800)==0xF800){ //高5位为1表示负温度 temp=(~temp)+1; //将补码取反运算+1得到负温值 temp_value=temp*(-0.0625); } else temp_value=temp*0.0625; return temp_value; } /** * @brief HC595时钟上升沿配置 * * @param[in] 外设端口PORTx、延时t1、延时t2 * */ void HC595_CLKRisingEdge_Config(GPIO_Regs* PORTx,uint32_t PINx,uint32_t t1,uint32_t t2){ DL_GPIO_clearPins(PORTx, PINx); delay_cycles(delay_us*t1); DL_GPIO_setPins(PORTx, PINx); delay_cycles(delay_us*t2); } /** * @brief HC595写入字节数据 * * @param[in] byte字节数据 * */ void LED_Segment_WriteByte(uint8_t byte){ for(uint8_t i=1;i<=8;i++){ if(byte&0x80){ DL_GPIO_setPins(PORT_HC595_PORT,PORT_HC595_PIN_HC595_DIO_PIN); } else { DL_GPIO_clearPins(PORT_HC595_PORT,PORT_HC595_PIN_HC595_DIO_PIN); } HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_SCLK_PIN,2,2); byte<<=1; } } /** * @brief 数码管显示1位数字 * * @param[in] Num_one数字,index显示位置 * */ void LED_Segment_Display_Num_One(uint8_t Num_one,uint8_t index){ LED_Segment_WriteByte(Num_List[Num_one]); LED_Segment_WriteByte(1<<index); HC595_CLKRisingEdge_Config(PORT_HC595_PORT,PORT_HC595_PIN_HC595_RCLK_PIN,2,2); } /** * @brief 数码管显示数字处理 * * @param[in] Num数字 * */ void Num_LED_Segment_Process(uint32_t Num){ uint8_t num_lsb=0; uint8_t i; for(i=0;i<NUM_MAXLENGTH;i++) { num_lsb=Num%10; Num/=10; LED_Segment_Display_Num_One(num_lsb,i); if(Num==0)break; } } /** * @brief 数码管显示浮点数处理 * * @param[in] Num数字,Reserved_points保留小数点位数 * */ void Num_LED_Segment_Process_Float(float Num,uint8_t Reserved_points){//处理数码管显示浮点数,Reserved_points为保留小数点后几位 int Num_int=Num*pow(10,Reserved_points+1); if(Num_int%10>=5)//四舍五入 Num_int=Num_int/10+1; else Num_int=Num_int/10; if(Reserved_points==0) Num_LED_Segment_Process(Num_int); else { Num_LED_Segment_Process(Num_int); LED_Segment_Display_point(0x7f,Reserved_points); LED_Segment_Display_point(0xff,Reserved_points); LED_Segment_Display_point(0xff,Reserved_points); } } int main(void) { SYSCFG_DL_init(); NVIC_SetPriority(TIMER_0_INST_INT_IRQN, 2); NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); DL_TimerG_startCounter(TIMER_0_INST);//定时刷新数码管显示 NVIC_SetPriority(TIMER_1_INST_INT_IRQN, 0); NVIC_EnableIRQ(TIMER_1_INST_INT_IRQN); DL_TimerG_startCounter(TIMER_1_INST); while (1) { // Num_LED_Segment_Process_Float(temp,Reserved_points); delay_cycles(delay_us*500); } } /** * @brief 定时器0中断回调函数 * * @param[in] None * */ void TIMER_0_INST_IRQHandler(void) { Num_LED_Segment_Process_Float(temp,Reserved_points); Num_LED_Segment_Process_Float(temp,Reserved_points); Num_LED_Segment_Process_Float(temp,Reserved_points); Num_LED_Segment_Process_Float(temp,Reserved_points); // switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) { // case DL_TIMER_IIDX_ZERO: // Num_LED_Segment_Process_Float(temp,Reserved_points); // break; // default: // break; // } } /** * @brief 定时器1中断回调函数 * * @param[in] None * */ void TIMER_1_INST_IRQHandler(void) { if(DS18B20_INIT()==0){ temp=DS18B20_GetTemp(); if(temp<temp_level[0]){ DL_GPIO_setPins(PORT_BEEP_PORT,PORT_BEEP_PIN__BEEP_PIN); } else if(temp>=temp_level[0]&&temp<temp_level[1]){ DL_GPIO_togglePins(PORT_BEEP_PORT,PORT_BEEP_PIN__BEEP_PIN); } else { DL_GPIO_clearPins(PORT_BEEP_PORT,PORT_BEEP_PIN__BEEP_PIN); } } else{ temp=0.1000; DL_GPIO_setPins(PORT_BEEP_PORT,PORT_BEEP_PIN__BEEP_PIN); delay_cycles(delay_us*1000);//温度传感器不存在、异常,LED_Red不断闪烁 DL_GPIO_togglePins(PORT_LED_Red_PORT,PORT_LED_Red_PIN_LED_Red_PIN); } // switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) { // case DL_TIMER_IIDX_ZERO: // Num_LED_Segment_Process_Float(temp,Reserved_points); // break; // default: // break; // } }
温度报警器实现思路是,通过编程驱动DS18B20获取当前环境温度数据,判断温度是否低于28C°,则蜂鸣器保持静音。温度处于28~29C°,蜂鸣器发出间隔报警声音。温度大于29C°,蜂鸣器发出连续报警声音。温度数据的获取由定时器1中断完成,在作者课程二任务一中有介绍过定时器中断的工作机制。数码管温度值刷新显示由定时器0中断完成。在温度报警器系统运行期间,每次温度检测均需判断温度传感器是否存在,如果器件不存在或响应异常,那么红色LED会不断闪烁,以提示系统运行出现故障。
效果图: