首页 / 资讯中心 / 文章详情

单片机多点温度检测系统设计:DS18B20单总线原理与实现

单片机多点温度检测系统设计:DS18B20单总线原理与实现 ★ FEATURED ARTICLE
简介基于单片机多点温度检测系统设计资料面向电子、自动化等专业的课程设计与单片机入门学习者完整呈现以AT89C51为核心的二路DS18B20温度采集与1602LCD显示方案。压缩包内为1个doc文档大小约199KB虽为单文件但结构完整涵盖绪论、系统设计、硬件模块解析主控制器/显示/温度采集、DS18B20内部结构与测温原理、软件设计、系统仿真、设计总结及参考文献并在文档末尾附有源程序代码可直接用于方案验证与二次开发。内容包含原理框图、存储器映像、温度数据格式等关键图表对DS18B20的64位ROM、高速暂存存储器、配置寄存器分辨率设置、温度补码换算等均有详细说明便于对照数据手册理解单总线通信时序与多点测温实现思路。该资料已有1650人浏览学习适合毕业设计、课程报告或项目预研时作为整体参考。1. 多点温度检测从“一个探头走天下”到看懂每个角落的温度做温度监测的人十有八九被同一个问题坑过现场布置了十几个测点电脑屏幕上却只有一个温度值或者各测点的数据相互矛盾今天这个点偏高明天那个点偏低排故排到怀疑人生。我在帮某工厂做设备间温控改造时也踩过这个坑——最开始时省事只在门口挂了一个探头结果设备发热集中区域温度超限了半小时值班员都没察觉。基于单片机多点温度检测系统设计含代码与原理图要解决的就是这件事用一个单片机、一根总线、一串数字温度传感器把分散在设备间、仓库、管道或实验室各个角落的温度统一读回来并给每个测点配上独立的数值与告警。它适合正在做课程设计的高校学生、想给老旧设备加温度监测的现场工程师也适合准备小批量做智能温控产品的开发者。下面我按自己做过的方案把原理、电路、代码和坑一条条讲清楚。2. 先定总线拓扑再选单片机多点测温的方案骨架2.1 为什么是单总线线最少、距离够、成本最低多点温度测量的技术方案不止一种。最常见的有模拟量方案热电阻或热电偶加变送器再进ADC、I2C总线方案、SPI总线方案以及1-Wire单总线方案。在真正动手画原理图之前这个选择会决定整块板的复杂度和后续排故难度所以得先讲清楚。模拟量方案测量精度高、抗干扰强但成本也高每路都需要一套信号调理和ADC采样前端线缆要求也高12个测点就得拉12对线现场施工量不小。I2C和SPI是板级总线的思路适合传感器离主控很近的场合一旦要拉线到几米、几十米外I2C的地址冲突和SPI的片选信号线膨胀问题就会让工程变复杂。单总线1-Wire是Dallas半导体定义的协议只需要一根数据线所有传感器并联在这根线上主机通过时序寻址访问每个设备。它的突出优势是线缆少、成本低、拓扑灵活现场布线就是一根两芯或三芯线沿着测点走一圈非常省事。在具体器件选择上多点测温基本绕不开DS18B20这个数字温度传感器。它的测温范围是-55℃到125℃在-10℃到85℃区间精度标称±0.5℃12位分辨率下温度分辨率为0.0625℃。更关键的是每个DS18B20出厂时都烧录了一个64位的全球唯一序列号ROM码主机通过这个码就能区分总线上不同的传感器。一条总线上理论上最多可以挂几十个DS18B20工程上我一般控制在15到20个以内这样时序余量和供电余量都更充裕。所以选型的结论是测点分散、线缆距离在几米到几十米以内、刷新周期只要几秒到十几秒的场合单总线方案在物料成本、施工难度和代码复杂度上都是最优解。如果你的测点超过30个、距离超过50米或者现场有大功率变频器等强干扰源我才会考虑分区域拉多条总线或者换模拟量方案——后者是另一个话题这里不展开。2.2 主机选型不是所有单片机都适合做1-Wire时序多点测温系统对主控的要求和普通的“读个传感器”项目不太一样。核心瓶颈在于1-Wire协议的时序是以微秒计的复位脉冲480μs写时隙60μs读时隙60μs采样窗口15μs。这要求单片机在干等时序期间不能被中断频繁打断IO翻转速度也要够快。所以选单片机时我主要看三个指标主频是否够高、定时器资源是否充裕、IO是否支持开漏模式。我在这类项目里基本在两个方向上选一个是经典的增强型51内核单片机比如国内某系列主频能到24MHz以上指令周期快开发环境成熟非常适合作课程设计和样机验证另一个是ARM Cortex-M0/M3内核的国产单片机比如某系列主频48~72MHz定时器和串口资源丰富适合后续要加显示屏、按键、联网模块或者RS485通信的场景。两个方向我都实际用过这里给一个选型对比表选型维度增强型51ARM Cortex-M0/M3选择依据主频24~40MHz48~72MHz1-Wire时序要求微秒级精确主频越高余量越大定时器数量3~5个5~8个一路做轮询调度一路做看门狗一路做串口波特率生成Flash/RAM8~64KB / 1~4KB64~128KB / 8~20KB决定能否缓存全部测点历史数据开漏IO部分支持普遍支持1-Wire总线是线与结构必须开漏输出开发成本低直接写寄存器中等可用寄存器或库课设和验证选前者量产选后者我个人的习惯是课程设计、毕业设计、样机验证用增强型51就足够了几块钱一片下载调试方便如果这个系统将来要作为产品交付带RS485、离线记录、蓝牙配网那就直接上Cortex-M0别在51上硬堆外设后面维护成本会翻倍。还有一条硬件前提必须强调单片机的IO要配置成开漏模式准双向口。因为1-Wire总线是线与结构主机拉低表示写0释放总线靠外接上拉电阻恢复高电平。如果IO配置成推挽输出传感器应答拉低总线时主机还在持续输出高电平轻则通信失败重则长时间灌电流损坏端口。这个细节我在原理图阶段就会标注好代码里也会在初始化函数里显式配置。3. 原理图设计传感器接口和电源电路直接决定测量结果3.1 传感器接口电路上拉电阻、ESD保护和连接器的取值原理图是整个系统最先出问题的地方而且很多问题不是逻辑错误是硬件本底噪声。多点测温系统的原理图核心是一根数据线的接口电路。我先把最小电路连接方式描述出来照着连就行数据线DQ从单片机开漏IO引出接一个上拉电阻到电源3.3V或5V然后串联一个100Ω电阻再接一个ESD保护二极管到地和电源最后到接线端子。每个DS18B20的DQ引脚直接并联在这根总线上VDD和GND从端子单独引出。这个电路里三个器件各有讲究。首先是上拉电阻的取值为什么默认4.7kΩ而不是1kΩ或10kΩ因为1-Wire总线的数据传输靠总线电容和上拉电阻构成RC充放电回路。上拉电阻越大总线恢复高电平越慢时序窗口越危险上拉电阻太小长线反射和过冲都会变大。我的经验是短距离小于2米用4.7kΩ最稳妥中距离2到10米改成2.2kΩ超过10米先用1kΩ试同时软件里把通信速率降低——具体做法是拉长时隙延时后面代码部分会讲。串联的100Ω电阻很多人会漏掉。它的作用有两个一是限制传感器引脚上的瞬态电流尤其热插拔的时候二是和总线分布电容一起形成低通滤波滤掉高频干扰。如果现场有变频器或继电器这类干扰源这个100Ω电阻还可以换成磁珠高频抑制效果更好。ESD保护二极管选双向或单向都行注意结电容要小于50pF否则会把总线时序拖垮。连接器这里我不建议用2.54mm排针了事。多点测温要拉线出去现场施工可能反复插拔接线端子至少选3.81mm间距的螺钉端子保证牢靠。如果传感器分布范围很大建议在每个测点的端子旁加一个100pF电容到地这个电容能有效抑制总线反射——很多入门设计都不会放它但长线场景它比多加一个上拉电阻更管用。3.2 电源电路寄生供电还是外部供电以及压降怎么算原理图阶段第二个大坑是供电方式。DS18B20支持两种供电模式寄生供电两根线DQGND和外部供电三根线VDDDQGND。寄生供电的好处是少一根线但前提是主机在温度转换期间要给总线提供足够强的上拉否则转换到一半传感器内部电容掉电转换结果直接乱掉表现为“读出来是85℃”或“偶尔读出错误值”。我明确建议所有超过3个测点、线长超过2米的多点系统一律用外部供电三根线别省这一根。外部供电电路上还有两个细节。第一每个DS18B20的VDD和GND之间需要就近放一个100nF的去耦电容。原理图上只有三个引脚很多人就省了。但实际上总线上几十米线缆的分布电感很大传感器转换时内部电流突变会在VDD上产生毛刺这个毛刺会耦合到DQ线上导致读取数据偶尔错位。加个100nF电容毛刺直接被吸收掉。第二是电源线的压降计算。我举个例子24AWG线大约0.2平方毫米直流电阻约每米0.08Ω一个DS18B20正常工作时电流约1.5mA转换期间约1mA。如果挂16个测点、总线长度20米电源回路的长度要算40米去回总电阻约3.2Ω。16个传感器同时转换时总电流约24mA压降约3.2Ω×24mA77mV。这个值看着不大但如果线更细、测点更多或者供电电压本身就是3.3V再叠加母线上的纹波传感器供电就已经在临界边缘了。所以我的原则是多点系统的电源不要从单片机板上的3.3V取单独用一路5V稳压每个传感器端的电压落在3.0~5.5V范围内。如果距离长就把供电线加粗到18AWG压降能降一个数量级。原理图画到这个程度硬件部分的方案就定型了。接下来进入代码环节——先跑通单总线时序再做ROM扫描和轮询调度。4. 代码实现单总线时序、ROM扫描与轮询调度4.1 最小驱动复位、读时隙、写时隙的C代码硬件板子回来以后第一步是用一个最小工程验证总线能通。我给出一段基于增强型51内核单片机的C代码逻辑本身在普通51和ARM Cortex-M0上通用。这段代码能实现1-Wire最底层的三个动作复位、写一位、读一位。#include stc8.h // 替换为你实际使用的单片机头文件 #include intrins.h #define DQ_PIN P3_2 // 数据线接到P3.2 // 微秒级延时按主频24MHz调整 void delay_us(unsigned short us) { while (us--) { _nop_(); _nop_(); _nop_(); } } // 总线引脚模式切换开漏输出/高阻输入 void DQ_OUT(void) { P3M1 ~(1 2); P3M2 ~(1 2); // 设置为准双向口开漏 } void DQ_IN(void) { P3M1 | (1 2); P3M2 ~(1 2); // 设置为高阻输入 } // 复位主机拉低480us以上释放后检测存在脉冲 unsigned char ds18b20_reset(void) { unsigned char presence 0; DQ_OUT(); DQ_PIN 0; delay_us(500); // 复位脉冲至少480us DQ_PIN 1; // 释放总线 delay_us(20); // 等待传感器应答窗口 DQ_IN(); presence DQ_PIN; // 存在脉冲时读到0 delay_us(60); // 读完后延时补足复位时序 DQ_OUT(); return (presence 0) ? 1 : 0; } // 写一位主机拉低根据bit决定何时释放 void ds18b20_write_bit(unsigned char bit) { DQ_OUT(); DQ_PIN 0; if (bit) { delay_us(6); // 写1短拉低后释放 DQ_PIN 1; delay_us(60); // 保持整个时隙 } else { delay_us(60); // 写0全程拉低 DQ_PIN 1; delay_us(6); } } // 读一位主机拉低1us后释放在采样窗口读取 unsigned char ds18b20_read_bit(void) { unsigned char bit 0; DQ_OUT(); DQ_PIN 0; _nop_(); // 约1us DQ_PIN 1; DQ_IN(); _nop_(); // 约1us后采样 bit DQ_PIN; delay_us(60); // 读完整个时隙 DQ_OUT(); return bit; }这段代码里几个关键参数要按实际主频调整。delay_us(500)对应复位脉冲至少480μs留了余量delay_us(20)是释放总线后等待传感器拉低总线的时间DS18B20的存在脉冲在复位脉冲结束后15~60μs内出现取20μs比较居中写1时隙拉低6μs后释放写0时隙全程拉低60μs——这些时序窗口和器件手册是对得上的。编写时最好把整个时序放在关中断的临界区里防止定时器中断插入后把微秒级时序拉长这一点后面避坑章节还会细讲。把上述代码编译烧录后在main函数里循环调用ds18b20_reset()正常接好总线会返回1。如果返回0大概率是硬件问题上拉电阻没焊、传感器极性接反、或者IO没配置成开漏模式。先解决这个后面什么都好说。4.2 ROM搜索算法与多点温度轮询单总线上每个DS18B20的ROM码都不同主机要读某个传感器的温度得先知道它完整的64位ROM码。最可靠的方式是器件手册里的ROM搜索算法对每一位读两次判断该位是0还1同时用状态记录冲突分支通过多轮遍历枚举出全部设备。这个算法代码量不大但状态变量要理清楚。我给一个常见的实现骨架#define MAX_DEVICES 16 unsigned char rom_codes[MAX_DEVICES][8]; // 存储每个设备的64位ROM unsigned char device_count 0; // 搜索总线上所有设备返回设备数量 unsigned char ds18b20_search_all(void) { unsigned char last_discrepancy 0; unsigned char done 0; unsigned char branch_point 0; unsigned char dev_idx 0; unsigned char bit_idx 0; unsigned char byte_idx 0; while (!done dev_idx MAX_DEVICES) { ds18b20_reset(); ds18b20_write_byte(0xF0); // SEARCH ROM命令 branch_point 0; for (bit_idx 0; bit_idx 64; bit_idx) { unsigned char bit_a ds18b20_read_bit(); unsigned char bit_b ds18b20_read_bit(); unsigned char direction 0; if (bit_a 0 bit_b 0) { // 该位存在冲突两个设备在此位值不同 if (bit_idx last_discrepancy) { direction (rom_codes[dev_idx][bit_idx / 8] (bit_idx % 8)) 1; } else if (bit_idx last_discrepancy) { direction 1; } else { direction 0; branch_point bit_idx; } } else if (bit_a 0 bit_b 1) { direction 0; } else if (bit_a 1 bit_b 0) { direction 1; } else { return 0; // 总线异常无设备响应 } if (direction) { rom_codes[dev_idx][bit_idx / 8] | (1 (bit_idx % 8)); } else { rom_codes[dev_idx][bit_idx / 8] ~(1 (bit_idx % 8)); } ds18b20_write_bit(direction); } // 后8位是CRC校验通过才登记设备 if (crc8_check(rom_codes[dev_idx][0], 8)) { dev_idx; } else { return 0; } last_discrepancy branch_point; if (last_discrepancy 0) done 1; } device_count dev_idx; return dev_idx; }这个搜索算法的核心是last_discrepancy和branch_point。第一次遍历全部冲突位选0方向记录最后遇到的冲突位第二次遍历到相同冲突位时选1方向这样不断回溯最终枚举完所有设备。实际使用中要注意如果总线上某个传感器接触不良CRC校验会失败这个时候我建议直接返回0而不是跳过——因为很有可能有设备在总线上处于不稳定状态先处理硬件再继续。ROM扫描通过后多点温度轮询就简单了。流程是对某个ROM码发匹配命令0x55再发启动转换命令0x44等待转换完成12位分辨率约750ms然后发读暂存器命令0xBE把9字节数据读回来做CRC校验和温度换算。下面是读取一个测点的完整代码// 读取指定ROM码对应传感器的温度单位0.0625℃ int read_temp_by_rom(unsigned char *rom) { unsigned char data[9] {0}; unsigned char crc 0; unsigned char i; int16_t raw; int temp; ds18b20_reset(); ds18b20_write_byte(0x55); // MATCH ROM for (i 0; i 8; i) { ds18b20_write_byte(rom[i]); } ds18b20_write_byte(0x44); // 启动温度转换 delay_ms(750); // 12位分辨率典型转换时间 ds18b20_reset(); ds18b20_write_byte(0x55); // 再次匹配同一ROM for (i 0; i 8; i) { ds18b20_write_byte(rom[i]); } ds18b20_write_byte(0xBE); // 读暂存器 for (i 0; i 9; i) { data[i] ds18b20_read_byte(); // 读9字节 } crc crc8_check(data, 8); // 校验前8字节 if (crc ! data[8]) { return -1000; // 错误码CRC校验失败 } raw (data[1] 8) | data[0]; // 拼接原始12位数据 if (data[4] 0x80) { // 符号位判断 raw (raw 0x07FF) - 1; // 负温度取补码 temp (int)(~raw 0x07FF); temp temp * 625; temp -temp; // 负温度单位0.0625°C } else { temp raw * 625; // 正温度单位0.0625°C } return temp; }这里几个细节要说明delay_ms(750)是12位分辨率下从发转换命令到读取暂存器的最小等待时间如果改成9位或10位分辨率可以缩短到几百毫秒但温度分辨率也会变粗我一般默认12位不动。返回的temp单位是0.0625℃比如返回1600表示100.0℃。用-1000作为错误返回码能有效区分真实的0℃读数。data[4]是配置寄存器第6、5位决定分辨率读出来可以确认传感器当前精度设置。轮询调度的主循环建议这样组织先把所有测点的ROM码遍历一遍逐个发启动转换命令全部发完后再统一等待750ms最后逐个读取结果。这样总耗时从“测点数×750ms”变成“750ms读取时间”16个测点能从12秒缩短到3秒以内。这里有个和直觉相反的点给所有传感器同时发转换命令比逐个发逐个读更快而且不会增加总线冲突风险——因为MATCH ROM已经指定了目标设备其他传感器不会响应。5. 避坑与排查多点测温最常见的5个坑5.1 上电读出来全是85℃换了好几个传感器都一样现象系统一上电串口打印温度所有测点全是85.0℃过几秒也不变化换传感器也没用。原因85℃是DS18B20上电后暂存器里的默认值。寄存器复位值为0x0550换算出来就是85.0℃。这其实不是故障而是主机在传感器还没有完成第一次温度转换前就把暂存器读了出来。常见于代码里发完0x44后没有延时或者延时时间不够又或者上电后第一条命令就是读温度而没发转换命令。解决保证每次读温度前都按“复位→匹配ROM→0x44→延时750ms→复位→匹配ROM→0xBE”的顺序执行。另外检查上电后第一次复位是否稳定如果数据线上电平不稳可以在上电后加一个200ms延时再执行第一次复位。至少等一轮转换周期别在while循环里连续读。5.2 总线只挂了12个点却有3个测点温度跳动现象系统跑一段时间后个别测点的温度值乱跳用示波器看数据线发现波形边缘有很多毛刺重新插拔一下传感器能好一会儿。原因大概率是传感器用了寄生供电或者外部供电但线缆过长导致转换期间供电电压跌落。跳动通常发生在温度转换发起的那一刻——传感器内部对电容充电电流突变耦合到DQ线上把时序波形搞乱了。解决改成外部供电每个传感器端加100nF去耦电容。如果已经是外部供电就把电源线加粗或提高供电电压到5V。软件上也可以加滤波连续读三次取中位数但这是治标别把软件滤波当成硬件问题的后悔药。5.3 同一个ROM码在总线上出现了两次现象更新固件后重新扫描总线发现设备数比实际挂载数量少枚举结果里有重复的ROM码。原因一些成本更低的兼容传感器芯片ROM码并不是严格全球唯一不良品可能重复。也可能是用了拆机件内部EEPROM被改过。解决第一次扫描时除了做CRC校验还要做ROM码去重。如果发现重复只能换传感器没有软件解法。采购时尽量选正规货源收到货先全量扫描一遍把重复的挑出来这一步能省后面大量排查时间。5.4 加了定时器中断后所有测点都读不到了现象代码加了按键扫描定时中断或串口接收中断后1-Wire通信立即失效去掉中断就正常。原因1-Wire时序对微秒级窗口敏感定时器中断服务函数可能占用几十上百微秒正好把写时隙或读时隙打断时序错乱。解决把ds18b20_reset()、read_bit、write_bit这几个底层函数放在关中断临界区内进入轮询时序前关中断完成后开中断。注意临界区要覆盖整个时序操作而不是只保护单片命令。如果中断来源很重要比如串口通信就在中断里置标志位、缓冲数据等总线操作完再处理。5.5 改了上拉电阻后通信反而更差现象把4.7kΩ上拉换成1kΩ后短距离出现偶发通信失败示波器看到波形过冲。原因上拉电阻减小总线恢复速度快了但阻抗降低导致反射和过冲增大1-Wire的输入阈值可能被高频振铃穿破。特别是在线缆分布电容较大的情况下RC网络的品质因数变了。解决中等距离优先改2.2kΩ只有超过10米才降到1kΩ同时软件里把时隙延时长一点。如果还是不行在总线末端加一个100pF电容到地吸收反射。改完一定要用示波器看总线波形确认上升沿过冲不超过电源电压20%再继续。6. 进阶CRC校验、掉线标记与上下位机联调多点测温系统走到能稳定读数只是第一步。我一般在交付前还会加一层可靠性和可维护性的设计这个环节直接决定这套系统值不值得长期用。第一个进阶项是给每个测点加上掉线标记。读取温度时如果连续三次CRC校验失败就把该点标记为“掉线”在显示界面上用特殊符号提示而不是显示一个错乱的温度值。这一步成本很低但价值很大现场维护的人一眼就能看出哪个探头坏了不用拿万用表逐点量。实现方式就是在读温度函数的错误码处做计数器累积连续失败才确认掉线避免瞬时干扰误报。第二个进阶项是把“启动转换”和“读取结果”拆成两个阶段。前面提过不要在read_temp_by_rom函数里又发转换又读结果而是先给所有测点统一发0x44启动转换然后统一等待750ms再逐个读取暂存器。这样可以让单片机在750ms的转换窗口期内干别的事情比如刷新OLED、处理按键、发送状态帧。我实测过16个测点的主循环周期从12秒缩短到3秒以内系统的实时感完全不一样。第三个进阶项是上下位机的通信协议设计。如果系统要接触摸屏或上位机建议在串口输出里用固定帧格式帧头0xA5、功能码、测点数、每个测点的温度值高字节和低字节、状态字节、最后两个字节CRC16校验。温度值放大10倍传输比如25.4℃发254避开浮点数51单片机上省内存上位机也省解析时间。联调时先用串口助手看裸数据确认帧格式对再对接上位机不要上来就灌给协议库那样排查效率太低。最后分享一个我自己的习惯每次拿到一批传感器第一件事不是直接焊到系统板上而是用一块万能板搭最小测试电路把整批传感器逐个扫描ROM、连续读三遍温度把数据记录下来再上系统。这一步能筛掉绝大多数体质差的器件也顺便验证了采购批次的一致性。吃过一次亏以后就把这个流程固定下来了——那批传感器里有三个ROM码重复上系统后怎么排查都少一个点最后逐个扫ROM才发现是器件本身的问题。从那以后每次新批次传感器都先过一遍这个测试流程基本没再遇到过因器件本体导致的大型排查事故。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站