蓝桥杯单片机备赛指南- 第十二讲:DS18B20 温度传感器
一、 DS18B20 硬件原理基础
DS18B20 是一款常用的数字温度传感器,其核心特点是采用**单总线(OneWire)**接口,仅需一根数据线即可实现双向通信。
1. 单总线通信(OneWire)
- 硬件连接:数据线DQ 通常需要接一个4.7kΩ 上拉电阻。在蓝桥杯板子上,P1.4 引脚连接到DS18B20 的DQ 引脚。
- 通信特点:
- 单主多从:一个MCU 可以挂载多个传感器(通过64位ROM ID 区分)。
- 时序严格:所有通信都由主机(单片机)发起,对延时精度要求极高(微秒级)。
- 空闲状态:总线空闲时为高电平。
2. 核心时序
DS18B20 的操作严格遵循以下步骤:
- 初始化(Reset & Presence):主机拉低总线至少480us (复位脉冲),然后释放;等待DS18B20 拉低总线60-240us (存在脉冲)。
- 写时序(Write):
- 写0:主机拉低总线60us 以上。
- 写1:主机拉低总线1-15us,然后释放(被上拉电阻拉高)。
- 读时序(Read):主机拉低总线1us 后释放,在15us 内读取总线电平。
3. 温度转换流程(常用)
对于单个DS18B20,标准的操作流程如下:
- 初始化。
- 跳过ROM (Skip ROM, 0xCC):因为总线上只有一个设备,不需要匹配ID。
- 开始转换(Convert T, 0x44):启动温度测量。
- 延时:等待转换完成(12位精度最大需750ms,但在显示循环中通常不需要死等)。
- 初始化。
- 跳过ROM (0xCC)。
- 读取暂存器(Read Scratchpad, 0xBE):读取温度数据(低字节在前,高字节在后)。
二、 底层驱动详解(onewire.c&onewire.h)
底层驱动的核心在于微秒级延时的控制。
1. 延时函数
单总线对时间非常敏感,必须使用经过校准的延时函数。
C
// 软件延时函数,适配单总线时序voidDelay_OneWire(unsignedintt){unsignedchari;while(t--){for(i=0;i<12;i++);// 根据晶振频率调整循环次数 (STC15 @ 12MHz)}}2. 初始化函数
发送复位脉冲并检测存在脉冲。
C
// DS18B20初始化bitInit_DS18B20(void){bit initflag=0;DQ=1;Delay_OneWire(12);DQ=0;Delay_OneWire(80);// 拉低总线至少 480usDQ=1;Delay_OneWire(10);initflag=DQ;// 读取存在脉冲 (0代表存在)Delay_OneWire(5);returninitflag;}3. 写字节函数(Write_DS18B20)
发送8 位数据,低位先行。
C
voidWrite_DS18B20(unsignedchardat){unsignedchari;for(i=0;i<8;i++){DQ=0;// 拉低总线,开始写时序DQ=dat&0x01;// 写入最低位 (若为1则释放总线,由上拉电阻拉高)Delay_OneWire(5);// 保持状态 (约 50us)DQ=1;// 释放总线,准备下一位dat>>=1;}Delay_OneWire(5);}4. 读字节函数(Read_DS18B20)
读取8 位数据,低位先出。
C
unsignedcharRead_DS18B20(void){unsignedchari;unsignedchardat;for(i=0;i<8;i++){DQ=0;// 拉低总线,启动读时序dat>>=1;// 准备接收下一位DQ=1;// 释放总线,准备读取if(DQ)// 检测总线电平{dat|=0x80;// 如果是高电平,将最高位置1 (随后会移位到正确位置)}Delay_OneWire(5);// 等待时隙结束}returndat;}5. 温度读取完整函数(Read_T)
这通常是在.c中封装好的高层函数,供main调用。
C
// 读取温度的完整流程floatRead_T(){unsignedcharlow,high;unsignedinttemp;floatt;// 1. 启动转换Init_DS18B20();Write_DS18B20(0xCC);// 跳过ROMWrite_DS18B20(0x44);// 开始转换// 注意:这里如果加延时会阻塞显示,通常利用主循环间隔代替延时,或者连续读取// 蓝桥杯比赛中,为了代码简单,常在读取前先发起一次转换,读的是上一次的结果// 2. 读取数据Init_DS18B20();Write_DS18B20(0xCC);Write_DS18B20(0xBE);// 读暂存器low=Read_DS18B20();high=Read_DS18B20();// 3. 数据合成temp=(high<<8)|low;// 4. 转换为实际温度 (DS18B20默认12位精度,分辨率0.0625)t=temp*0.0625;returnt;}三、 功能设计要求(PDF还原)
本讲任务是设计一个温度报警装置,具备温度显示、参数设置及精度调节功能。
1. 数码管显示界面
- 温度显示界面
显示当前采集的温度数据。
- 格式:
C(标识符) +温度值(保留一位小数) +C(单位)。 - 示例:
C熄灭熄灭26.7C(代表26.7℃)。 - 参数设置界面
显示当前设置的温度上下限。
- 格式:
P(标识符) +TMAX+TMIN。 - 示例:
P熄灭熄滅30熄灭02(上限30,下限02)。 - 交互:选中单元格以0.5秒/次的速度闪烁。
- 精度设置界面
设置DS18B20 的采集精度(9-12位)。
- 格式:
A(标识符) +精度等级。 - 示例:
A熄灭熄灭熄灭熄灭1(等级1)。
2. 按键模块
- S12 (界面切换):在温度显示-> 参数设置-> 精度设置之间循环。
- S13 (参数切换):在参数设置界面,切换选择TMAX 或TMIN。
- S14 (加):
- 参数界面:当前温度参数+1℃。
- 精度界面:精度等级+1。
- S15 (减):
- 参数界面:当前温度参数-1℃。
- 精度界面:精度等级-1。
3. LED 指示与亮度控制(PWM)
- 报警指示:
- L1 亮:温度> TMAX (高温)。
- L2 亮:TMIN ≤ 温度≤ TMAX (正常)。
- L3 亮:温度< TMIN (低温)。
- L4 亮:参数设置错误(如TMAX < TMIN)。
- 亮度等级(PWM调光):
- 高温状态:亮度等级1 (最暗,如占空比30%)。
- 正常状态:亮度等级2 (中等,如占空比60%)。
- 低温状态:亮度等级3 (最亮,如占空比90%)。
四、 程序设计与代码逻辑(main.c)
本程序的难点在于浮点数显示和PWM 亮度控制。
1. 全局变量管理
C
floatTemperature=0;// 存储实时温度 (浮点型)u8 T_Limit[2]={30,20};// [0]上限, [1]下限u8 T_Limit_Set[2]={0};// 设置时的缓存u8 LED_PWM=0;// 当前目标亮度 (占空比阈值)u8 Level=0;// 亮度等级状态记录2. 温度获取与显示处理(main&Seg_Proc)
由于Read_T耗时较长(几毫秒到几百毫秒),绝对不能在定时器中断中调用,必须放在主循环中。
C
// 主循环voidmain(){// ... 初始化代码 ...while(1){// 在主循环中读取温度,避免影响中断响应Temperature=Read_T();Key_Proc();Seg_Proc();LED_Proc();}}// 数码管处理voidSeg_Proc(){// 技巧:温度显示处理 (乘以10转为整数显示)// 例如 26.7 -> 267,这样可以用取余操作分离出每一位longtemp_disp=Temperature*10;switch(Seg_Mode){case0:// 温度显示Seg_Buf[0]=12;// 'C'Seg_Buf[3]=temp_disp/100;// 十位Seg_Buf[4]=(temp_disp/10)%10;// 个位Seg_Buf[5]=temp_disp%10;// 小数一位Seg_Point[4]=1;// 点亮第4位的小数点 (26.7)Seg_Buf[7]=12;// 'C'break;case1:// 参数设置Seg_Buf[0]=13;// 'P'// 利用 Flash_Flag 实现闪烁Seg_Buf[3]=(Flash_Flag&&T_Limit_Set_Index==0)?10:T_Limit_Set[0]/10;Seg_Buf[4]=(Flash_Flag&&T_Limit_Set_Index==0)?10:T_Limit_Set[0]%10;Seg_Buf[6]=(Flash_Flag&&T_Limit_Set_Index==1)?10:T_Limit_Set[1]/10;Seg_Buf[7]=(Flash_Flag&&T_Limit_Set_Index==1)?10:T_Limit_Set[1]%10;break;}}3. PWM 亮度控制逻辑(LED_Proc&Timer0)
这是本讲的进阶考点。利用定时器中断产生一个高频计数器,模拟PWM 波形。
逻辑层(
LED_Proc):根据温度设置LED_PWM的阈值。C
voidLED_Proc(){if(Temperature>T_Limit[0]){// 高温 -> 亮度等级1 (假设30%亮度,即PWM<3时亮)LED_PWM=3;LED_Buf[0]=1;LED_Buf[1]=0;LED_Buf[2]=0;}elseif(Temperature<T_Limit[1]){// 低温 -> 亮度等级3 (假设90%亮度,即PWM<9时亮)LED_PWM=9;LED_Buf[0]=0;LED_Buf[1]=0;LED_Buf[2]=1;}else{// 正常 -> 亮度等级2 (假设60%亮度)LED_PWM=6;LED_Buf[0]=0;LED_Buf[1]=1;LED_Buf[2]=0;}// 错误检测 L4if(T_Limit[0]<T_Limit[1])LED_Buf[3]=1;elseLED_Buf[3]=0;}驱动层(
Timer0_Isr):C
// 定时器中断 (假设 1ms 触发一次)voidTimer0_Isr(void)interrupt1{staticu8 pwm_count=0;pwm_count++;if(pwm_count>=10)pwm_count=0;// 产生 0-9 的周期// ... 正常的数码管扫描代码 ...if(++LED_Pos==8)LED_Pos=0;// LED扫描指针// PWM 核心算法// 逻辑:如果当前计数 < 设定阈值,则点亮;否则熄灭// 阈值 LED_PWM 越大,点亮的时间占比越长,亮度越高if(LED_Buf[LED_Pos]&&pwm_count<LED_PWM){// 调用底层驱动点亮LED_Disp(LED_Pos,1);}else{// 熄灭 (PWM 低电平期间)LED_Disp(LED_Pos,0);}}
五、 学习小结
- 时序是生命线:DS18B20 对延时非常敏感,移植代码时务必检查
Delay_OneWire是否适配当前单片机的晶振频率(蓝桥杯STC15 为12T 模式下12MHz)。 - 非阻塞读取:
Read_T内部有延时或等待,尽量不要在定时器中断中调用,否则会干扰数码管扫描和按键消抖,造成显示闪烁或按键失灵。 - 浮点转定点:在数码管显示小数时,将
float乘以10 或100 转换为long或int进行处理,可以避免使用昂贵的sprintf函数,且逻辑更清晰。