资讯详情

资讯详情

DS18B20单总线时序详解与51单片机驱动实现

1. 为什么DS18B20在51单片机圈子里经久不衰1.1 一颗传感器撑起半个温度采集场景DS18B20这颗芯片在51单片机爱好者圈子里几乎是人手一颗的存在。它是一颗单总线数字温度传感器测温范围覆盖-55℃到125℃在-10℃到85℃区间内精度可以做到±0.5℃分辨率支持9到12位可调。封装形式常见的有TO-92直插、防水探头、贴片SOIC三种不管是洞洞板搭电路还是PCB打样都能找到合适的封装。它最核心的特点是把模拟温度信号在芯片内部就完成了模数转换直接输出数字量通过一根数据线完成与单片机的全部通信。这意味着不需要外部ADC、不需要运放调理电路、不需要考虑参考电压漂移单片机只要用一个普通IO口就能读到温度值。对于51单片机这种资源紧张的平台来说省掉一个ADC芯片和配套的模拟电路成本和布线复杂度都大幅下降。适合阅读这篇内容的人包括正在学习51单片机外设驱动的新手、需要做多点温度采集的电子爱好者、想搞懂单总线时序到底怎么写的嵌入式初学者以及被DS18B20时序折磨过想彻底理清楚的老哥。我会从硬件原理讲到通信时序再落到51单片机上的具体代码实现把每个环节的“为什么”都讲透。1.2 单总线到底省了什么又带来了什么单总线1-Wire协议是DS18B20最鲜明的标签。一根数据线加一根地线供电还可以从数据线上“偷”过来这就是所谓的寄生供电模式。相比I2C需要两根线、SPI需要四根线单总线在引脚资源上确实占优势。但代价也很明显所有通信时序都靠软件延时来模拟没有硬件外设帮你自动处理时钟和数据采样对时序精度的要求就落到了代码上。51单片机没有硬件单总线控制器所以必须用GPIO加软件延时来实现复位脉冲、存在脉冲、写时隙、读时隙这些基本操作。每一个时隙的持续时间都在微秒级别而51单片机在不同晶振频率下的机器周期又不一样这就导致同一套延时函数换一个晶振频率就跑不通。很多人第一次调DS18B20失败十有八九是时序没对准。理解了这一点后面所有的时序分析和代码实现就都有了落脚点我们本质上是在用软件精确控制一根IO线的高低电平变化去模拟一个严格的通信协议。2. 硬件连接与电气特性拆解2.1 引脚定义与两种供电模式DS18B20通常有三个引脚VDD、DQ数据线、GND。TO-92封装面对平面从左到右依次是GND、DQ、VDD。防水探头一般引出三根线红VDD、黑GND、黄或白DQ。供电方式有两种外部供电模式VDD接3.0V到5.5VDQ通过一个4.7kΩ上拉电阻接到VDD。这种模式下芯片供电充足转换期间总线可以空闲适合对时序容错要求高的场合。寄生供电模式VDD直接接地芯片从DQ线在上拉电阻作用下获取能量内部有一个小电容在总线高电平时储能。这种模式省一根电源线但在温度转换期间总线必须保持强上拉否则供电不足会导致转换结果错误。对于51单片机5V系统我一般建议新手直接用外部供电模式少一个变量少一个坑。等调通了再尝试寄生供电。2.2 上拉电阻为什么是4.7kΩDQ线是开漏输出结构芯片只能把线拉低不能主动拉高。所以必须外接一个上拉电阻把线在高电平时拉到VDD。电阻值的选择是个权衡电阻太小总线电容充电快上升沿陡但芯片拉低时灌电流大功耗高长期可能损伤IO口。电阻太大上升沿变缓在长线缆或大寄生电容场合可能导致采样错误。4.7kΩ是经过大量实践验证的折中值。如果线缆较长超过1米可以适当减小到3.3kΩ甚至2.2kΩ来加快上升沿。如果总线上挂了多个DS18B20也要考虑总线电容增加的影响。注意上拉电阻必须接不接的话总线永远无法回到高电平通信根本建立不起来。我见过有人忘了接上拉查了半天以为是代码问题。2.3 多器件挂载与ROM寻址单总线理论上可以挂载多个DS18B20每个芯片内部有一个64位光刻ROM前8位是家族码DS18B20固定为0x28中间48位是唯一序列号最后8位是CRC校验。挂多个器件时单片机先发ROM搜索命令读取所有器件的ROM码然后通过匹配ROM命令逐个寻址。对于51单片机来说挂一个器件时可以直接跳过ROM操作发0xCC命令省去搜索流程。挂多个器件时就需要完整的ROM搜索算法代码量会大不少。新手建议先从单器件调起。3. 通信时序的底层逻辑与参数计算3.1 复位脉冲与存在脉冲通信的握手每一次完整的通信都以复位脉冲开始。单片机把DQ线拉低至少480微秒然后释放总线拉高等待15到60微秒后DS18B20会主动把总线拉低60到240微秒作为存在脉冲Presence Pulse告诉单片机“我在线”。这里的关键参数是操作最短典型最长单片机拉低复位480μs500μs960μs单片机释放后等待15μs30μs60μsDS18B20拉低存在脉冲60μs120μs240μs单片机在释放总线后约30微秒采样DQ线如果读到低电平说明有器件响应如果读到高电平说明总线上没有器件或器件没响应。为什么复位脉冲要这么长因为单总线没有独立的时钟线复位脉冲相当于一个“唤醒信号”足够长的低电平让所有挂在总线上的器件都能识别到通信开始。太短了器件可能来不及反应。3.2 写时隙0和1的区别在持续时间写时隙分为写0和写1两种写1单片机拉低总线1到15微秒然后释放总线保持高电平直到时隙结束总时长至少60微秒。写0单片机拉低总线60到120微秒然后释放总线至少恢复1微秒。DS18B20在单片机拉低总线后的15到60微秒窗口内采样总线电平。如果此时是低电平就判定为写0如果是高电平就判定为写1。这里有个容易搞混的点写1和写0的区别不在于拉低与否而在于拉低的持续时间。写1是“短拉低”写0是“长拉低”。每次写时隙之间需要至少1微秒的恢复时间。3.3 读时隙单片机主动发起器件控制数据读时隙也是单片机先拉低总线至少1微秒然后释放。DS18B20会在拉低开始后的15微秒内把数据放到总线上如果是0它继续拉低如果是1它释放总线让上拉电阻把线拉高。单片机在拉低开始后约15微秒处采样总线读取数据。整个读时隙的持续时间至少60微秒两次读时隙之间也需要至少1微秒恢复。读时隙和写时隙在起始阶段很像都是单片机拉低再释放。区别在于写时隙是单片机控制拉低时长来编码数据读时隙是单片机只负责发起数据由DS18B20控制。3.4 时序参数与晶振频率的换算51单片机的延时通常用空循环实现。以12MHz晶振为例一个机器周期是1微秒12个时钟周期。如果用的是11.0592MHz晶振一个机器周期约1.085微秒。这就是为什么换晶振后延时函数要重新调整。假设我们用12MHz晶振写一个延时函数void delay_us(unsigned int us) { while(us--) { _nop_(); _nop_(); _nop_(); _nop_(); } }这个函数的实际延时取决于编译器的优化和具体指令周期。更可靠的做法是用示波器实测或者用定时器来产生精确延时。但在DS18B20这种微秒级时序里用定时器开销太大一般还是用空循环加实测校准。实操心得我习惯在延时函数里放不同数量的_nop_()然后用示波器抓DQ线的波形看复位脉冲是不是500微秒左右写时隙是不是60微秒左右。调准一次之后记下参数以后同频率晶振直接复用。4. 51单片机上的完整驱动实现4.1 硬件接线与IO口配置以STC89C52为例假设DQ接在P3.7口。接线如下P3.7接DS18B20的DQP3.7通过4.7kΩ电阻接VCC5VDS18B20的VDD接5VDS18B20的GND接GND51单片机的IO口是准双向口作为输出时可以直接拉低作为输入时需要先写1。在操作DQ线时每次读之前要先确保口线写1否则读到的永远是0。#include reg52.h #include intrins.h sbit DQ P3^7; void delay_us(unsigned int us) { while(us--) { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } }这个延时函数在12MHz晶振下大约每循环一次1微秒左右具体需要实测校准。4.2 复位与存在检测代码unsigned char ds18b20_reset(void) { unsigned char presence; DQ 0; delay_us(500); // 拉低至少480us DQ 1; delay_us(30); // 等待15-60us presence DQ; // 采样0表示有器件响应 delay_us(400); // 等待存在脉冲结束 return presence; }返回值0表示检测到DS18B20返回1表示没有器件响应。这个函数是每次通信的入口如果这里就失败了后面都不用谈。4.3 写字节与读字节的实现void ds18b20_write_byte(unsigned char dat) { unsigned char i; for(i 0; i 8; i) { DQ 0; _nop_(); DQ dat 0x01; // 写1则拉高写0则保持低 delay_us(60); // 整个时隙至少60us DQ 1; _nop_(); _nop_(); dat 1; } } unsigned char ds18b20_read_byte(void) { unsigned char i, dat 0; for(i 0; i 8; i) { DQ 0; _nop_(); DQ 1; _nop_(); _nop_(); dat 1; if(DQ) dat | 0x80; delay_us(60); DQ 1; } return dat; }写字节时先拉低至少1微秒然后根据当前位是0还是1决定释放还是继续保持低。读字节时先拉低至少1微秒再释放然后在15微秒内采样。注意读字节函数里采样之后要等时隙结束再操作下一位。如果采样完立刻开始下一个时隙可能破坏当前时隙的完整性。4.4 温度转换与读取流程完整的温度读取流程复位检测存在脉冲发送0xCC跳过ROM发送0x44启动温度转换等待转换完成12位分辨率下最多750毫秒复位检测存在脉冲发送0xCC跳过ROM发送0xBE读取暂存器连续读取两个字节低字节在前高字节在后组合成16位数据并换算成温度float ds18b20_read_temp(void) { unsigned char tl, th; int temp; float result; ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0x44); delay_ms(750); ds18b20_reset(); ds18b20_write_byte(0xCC); ds18b20_write_byte(0xBE); tl ds18b20_read_byte(); th ds18b20_read_byte(); temp (th 8) | tl; result temp * 0.0625; return result; }0.0625是12位分辨率下的温度分辨率因为12位对应的最小刻度是1/16摄氏度。如果配置成9位、10位、11位分辨率这个系数要相应调整。5. 常见问题排查与避坑经验5.1 复位无响应从硬件查到软件复位后读不到存在脉冲是最常见的问题。排查顺序建议排查项可能原因解决方法上拉电阻未接或阻值过大接4.7kΩ到VDD供电VDD未接或电压不足确认3.0-5.5V供电接线DQ与GND接反对照引脚定义检查延时复位脉冲太短用示波器确认≥480μsIO口读之前未写1读操作前确保DQ1晶振频率与延时函数不匹配重新校准延时我遇到过最隐蔽的一次是杜邦线接触不良DQ线时通时断现象是偶尔能读到偶尔读不到。换了一根线就好了。所以排查硬件问题时先换线换电阻排除物理连接问题再查代码。5.2 温度值固定不变或明显错误如果读到的温度一直是85℃或者某个固定值通常有几个原因转换未完成就读取12位分辨率需要750毫秒如果延时不够就读读到的是上一次的结果或者默认值85℃。数据字节顺序搞反低字节在前高字节在后搞反了温度值会完全不对。分辨率配置与实际换算不匹配如果配置成9位分辨率但用0.0625换算结果会偏小。负温度处理负温度时高字节的高5位全是1直接按无符号数处理会得到很大的正值。需要判断符号位并做补码转换。5.3 多器件挂载时的冲突挂多个DS18B20时如果都用0xCC跳过ROM所有器件会同时响应数据线冲突导致读取失败。必须用ROM搜索算法逐个读取序列号然后用0x55匹配ROM命令单独寻址。ROM搜索算法比较复杂涉及位碰撞检测和搜索路径记录。新手建议先用单器件调通再逐步增加器件数量。5.4 长线缆导致的信号完整性问题线缆超过1米后分布电容增大上升沿变缓可能导致时序采样错误。解决方法减小上拉电阻到2.2kΩ或3.3kΩ使用屏蔽线并单端接地降低通信速率但DS18B20时序是固定的只能通过减小上拉电阻改善在DQ线上串联一个100Ω左右的电阻抑制振铃实操心得我在一个多点测温项目里用了3米长的普通排线一开始读十次错三次。后来把上拉电阻从4.7kΩ换成2.2kΩ错误率降到几乎为零。再后来换成屏蔽线就完全稳定了。5.5 寄生供电模式下的特殊注意事项寄生供电时VDD接地芯片靠DQ线储能。温度转换期间总线必须强上拉否则供电不足。具体做法是在发送0x44命令后把DQ线直接拉高而不是靠上拉电阻保持至少750毫秒。51单片机可以把IO口配置成强推挽输出STC系列支持或者用一个三极管把DQ线拉到VDD。如果寄生供电下温度转换结果总是85℃或者随机值八成是转换期间总线没有强上拉。6. 时序调试的实用技巧与工具6.1 用示波器抓波形是最快的调试方式调DS18B20时序示波器比万用表有用一百倍。把探头接在DQ线上触发方式设为下降沿就能看到完整的复位脉冲、存在脉冲、写时隙和读时隙波形。对照数据手册的参数表一眼就能看出哪个时隙长了哪个短了。如果没有示波器可以用逻辑分析仪几十块钱的那种就够用。再不行就用LED指示在复位检测到存在脉冲后点亮一个LED读温度失败时点亮另一个LED通过LED状态缩小问题范围。6.2 延时函数的校准方法延时函数的精度直接决定时序是否合规。校准方法写一个循环调用延时函数的测试程序比如延时1000次delay_us(100)在延时前后翻转一个IO口用示波器测量IO口高电平持续时间计算实际每次延时的时间调整循环体内的_nop_()数量比如目标100微秒实测120微秒说明每次循环多了0.2微秒减少两个_nop_()再测。6.3 分步验证的调试策略不要一次性写完整个驱动再调试。分步验证第一步只写复位函数用LED指示是否检测到存在脉冲第二步写一个字节函数发送0xCC用示波器看波形是否符合写时隙规范第三步写读字节函数读取暂存器的默认值应该是0x50和0x05之类的固定值第四步完整温度转换和读取每一步验证通过再进入下一步这样出问题时范围小好定位。6.4 常见时序偏差对照表现象可能偏差调整方向复位无响应复位脉冲480μs增加拉低延时写数据器件无反应写时隙60μs增加时隙总时长读数据全0采样太早或太晚调整采样点位置读数据全1总线未拉低或器件未响应检查拉低操作偶发错误时隙间恢复时间不足增加1-2μs恢复7. 从单点测温到多点组网的扩展思路7.1 单总线挂载多点的布线建议多个DS18B20挂同一条总线时建议采用菊花链或星型拓扑但星型拓扑在分支较长时反射严重。更好的做法是总线走直线每个器件就近挂接分支长度尽量短。总线末端可以加一个100Ω左右的终端电阻减少反射。每个器件的ROM码需要预先读取并存储运行时通过匹配ROM逐个寻址。读取ROM码可以用搜索算法也可以一个一个单独接上读取后记录。7.2 与51单片机其他外设的配合温度数据读出来后常见的后续处理包括通过数码管或LCD显示通过串口发送到上位机根据温度值控制继电器或风扇存储到EEPROM做历史记录51单片机的串口和定时器资源有限如果温度采集和串口发送同时进行要注意时序冲突。DS18B20的750毫秒转换等待期间正好可以用来处理显示刷新或串口发送提高CPU利用率。7.3 低功耗场景下的考虑电池供电的场景下DS18B20的待机功耗很低但温度转换期间电流会增大。如果采用寄生供电转换期间总线强上拉会消耗较多电流。低功耗设计时可以考虑降低分辨率到9位转换时间缩短到94毫秒间歇性采集采集间隔用单片机休眠外部供电模式下转换期间切断VDD这些策略需要根据具体项目权衡精度、速度和功耗。DS18B20这颗芯片虽然老但它的单总线时序是学习嵌入式通信协议非常好的切入点。把它的时序彻底搞明白再去理解I2C、SPI这些有硬件控制器的协议会发现底层逻辑是相通的。我在实际项目中用DS18B20做过温室大棚多点测温、鱼缸温控、设备机箱温度监测每次调试时序的过程都差不多但每次都能加深一点对时序边界的理解。最后分享一个小技巧把调通的延时函数和读写函数单独放在一个头文件里换项目时直接复制只需要根据晶振频率微调延时参数能省下大量重复调试的时间。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →