一、功能需求
用DS18B20实现环境温度的采样
用数码管显示: 显示一位小数, 例如 23.6
实现高温蜂鸣器报警: 超过28.0蜂鸣器响
二、分析
数码管显示, 蜂鸣器报警, 这些功能在视频教程里都已经讲过。此次作业,关键在于与DS18B20通讯,采集实时的环境温度到mcu。
三、原理分析
(1)图形化配置
配置:
选择PA17作为通信口
蜂鸣器配置,选择PA3驱动蜂鸣器,因为该蜂鸣器为低电平驱动,所以配置时拉高该io口
DS1B20的通信协议为单总线,也就是只靠一根线来进行通信。那么这个IO口就要不断改变输出输入模式,查找资料后,写成宏定义的方式
#define DS18B20_OutPut_Mode() {DL_GPIO_initDigitalOutput(GPIO_DS18B20_DQ_IOMUX);} #define DS18B20_InPut_Mode() {DL_GPIO_initDigitalInput(GPIO_DS18B20_DQ_IOMUX);}
为了简化通讯引脚的状态读取,以及置高低电平,使用宏定义:
#define DS18B20_DQ_OUT(x) ((x)?(DL_GPIO_setPins(GPIO_DS18B20_PORT, GPIO_DS18B20_DQ_PIN)) : (DL_GPIO_clearPins(GPIO_DS18B20_PORT, GPIO_DS18B20_DQ_PIN))) #define DS18B20_DQ_IN ( DL_GPIO_readPins(GPIO_DS18B20_PORT,GPIO_DS18B20_DQ_PIN) )
(2)1-wire协议的实现
1)初始化序列:
1-wire单总线协议通讯总是以初始化序列开始,包含一个复位脉冲和一个存在脉冲
按手册上序列图实现代码如下:
/** * @brief 复位DS18B20 * @param 无 * @retval 无 */ static void ds18b20_reset(void) { DS18B20_OutPut_Mode(); DS18B20_DQ_OUT(0); /* 拉低DQ,复位 */ delay_us(750); /* 拉低750us */ DS18B20_DQ_OUT(1); /* DQ=1, 释放复位 */ delay_us(15); /* 延迟15US */ }
接着是等待存在脉冲,即回应信号。实现代码如下:
/** * @brief 等待DS18B20的回应 * @param 无 * @retval 0, DS18B20正常 * 1, DS18B20异常/不存在 */ uint8_t ds18b20_check(void) { uint8_t retry = 0; uint8_t rval = 0; while ((DS18B20_DQ_IN>0) && (retry < 200)) /* 等待DQ变低, 等待200us */ { retry++; delay_us(1); } if (retry >= 200) { rval = 1; } else { retry = 0; while (!(DS18B20_DQ_IN>0) && (retry < 240)) /* 等待DQ变高, 等待240us */ { retry++; delay_us(1); } if (retry >= 240) rval = 1; } return rval; }
组合一下,就是初始化序列的完整代码:
/** * @brief 初始化DS18B20的IO口 DQ 同时检测DS18B20的存在 * @param 无 * @retval 0, 正常 * 1, 不存在/不正常 */ uint8_t ds18b20_init(void) { ds18b20_reset(); return ds18b20_check(); }
2)读写序列:
先实现读取一个bit的时序 ,循环8次就可实现读取一个字节。注意,协议总是最低位先行
/** * @brief 从DS18B20读取一个位 * @param 无 * @retval 读取到的位值: 0 / 1 */ static uint8_t ds18b20_read_bit(void) { uint8_t data = 0; DS18B20_DQ_OUT(0); delay_us(2); DS18B20_DQ_OUT(1); delay_us(12); DS18B20_InPut_Mode(); if (DS18B20_DQ_IN>0) { data = 1; } delay_us(50); return data; } /** * @brief 从DS18B20读取一个字节 * @param 无 * @retval 读到的数据 */ static uint8_t ds18b20_read_byte(void) { uint8_t i, b, data = 0; for (i = 0; i < 8; i++) { b = ds18b20_read_bit(); /* DS18B20先输出低位数据 ,高位数据后输出 */ data |= b << i; /* 填充data的每一位 */ } return data; }
接下来是写出一个字节的时序,代码如下:
/** * @brief 写一个字节到DS18B20 * @param data: 要写入的字节 * @retval 无 */ static void ds18b20_write_byte(uint8_t data) { uint8_t j; for (j = 1; j <= 8; j++) { if (data & 0x01) { DS18B20_DQ_OUT(0); /* Write 1 */ delay_us(2); DS18B20_DQ_OUT(1); delay_us(50); } else { DS18B20_DQ_OUT(0); /* Write 0 */ delay_us(50); DS18B20_DQ_OUT(1); delay_us(2); } data >>= 1; /* 右移,获取高一位数据 */ } }
3)与DS18B20使用1-wire协议通讯,读取温度值
按照说明书,实现温度转换的功能如下:
/** * @brief 开始温度转换 * @param 无 * @retval 无 */ static void ds18b20_start(void) { ds18b20_reset(); ds18b20_check(); ds18b20_write_byte(0xcc); /* skip rom */ ds18b20_write_byte(0x44); /* convert */ }
温度转换指令发送后,采集温度后DS18B20就会返回温度值的高低字节,mcu转入接收模式,获取这两个字节。完整的获取一次温度值的过程,包装成一个完整的函数:
/** * @brief 从ds18b20得到温度值(精度:0.1C) * @param 无 * @retval 温度值 (-550~1250) * @note 返回的温度值放大了10倍. * 实际使用的时候,要除以10才是实际温度. */ short ds18b20_get_temperature(void) { uint8_t flag = 1; /* 默认温度为正数 */ uint8_t TL, TH; short temp; ds18b20_start(); /* ds1820 start convert */ ds18b20_reset(); ds18b20_check(); ds18b20_write_byte(0xcc); /* skip rom */ ds18b20_write_byte(0xbe); /* convert */ TL = ds18b20_read_byte(); /* LSB */ TH = ds18b20_read_byte(); /* MSB */ if (TH > 7) {/* 温度为负,查看DS18B20的温度表示法与计算机存储正负数据的原理一致: 正数补码为寄存器存储的数据自身,负数补码为寄存器存储值按位取反后+1 所以我们直接取它实际的负数部分,但负数的补码为取反后加一,但考虑到低位可能+1后有进位和代码冗余, 我们这里先暂时没有作+1的处理,这里需要留意 */ TH = ~TH; TL = ~TL; flag = 0; /* 温度为负 */ } temp = TH; /* 获得高八位 */ temp <<= 8; temp += TL; /* 获得底八位 */ /* 转换成实际温度 */ if (flag == 0) { /* 将温度转换成负温度,这里的+1参考前面的说明 */ temp = (double)(temp+1) * 0.625; temp = -temp; } else { temp = (double)temp * 0.625; } return temp; }
主函数中
#include "ti_msp_dl_config.h" #include "HC595.h" #include "Delay.h" #include "DS18B20.h" int8_t num=10; short value; int main(void) { SYSCFG_DL_init(); ds18b20_init(); NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN); DL_TimerG_startCounter(TIMER_0_INST); while (1) { LED8_Display (); } } //定时实现10到0递减 void TIMER_0_INST_IRQHandler(void) { value=ds18b20_get_temperature(); LED[2]=(uint8_t)(value*1.0/100)%10; LED[1]=(uint8_t)(value*1.0/10)%10; LED[0]=(uint8_t)(value*1.0/1)%10; if(value>=290) DL_GPIO_clearPins(GPIO_Beep_PORT,GPIO_Beep_Beep_PIN ); else DL_GPIO_setPins(GPIO_Beep_PORT,GPIO_Beep_Beep_PIN); DL_GPIO_togglePins(GPIO_LED_PORT,GPIO_LED_LED1_PIN); }
四、下载验证
设定报警温度为29.0度,编译下载测试,一切正常。
b站视频如下:
https://www.bilibili.com/video/BV1Bt421N7Ls/?share_source=copy_web&vd_source=3969f4161d002b07aea10c26424c9e9c