
简介面向51单片机学习者和课程设计人群以DS18B20数字温度传感器为主线提供一套覆盖驱动原理、单总线时序到Proteus仿真的完整工程可帮助实现一个带数码管显示、保留两位小数并在超温时蜂鸣器报警的数字温度计。压缩包共20个文件、约554KB核心包含C语言源码、Keil工程与编译生成的hex文件配套DSN/PWI格式的Proteus仿真电路以及介绍原理的Word/PPT文档和仿真使用说明同时保留了工程备份、列表文件、目标文件等中间内容便于对照完整的开发与编译产物。目前已有273人浏览学习。内容详述了DS18B20的工作原理、读写时序、操作指令和温度转换流程通过源码可学习51单片机对单总线器件的驱动写法借助Proteus仿真可反复验证显示与报警逻辑并能直接修改温度阈值和显示方式。文档部分对单总线协议和传感器使用要点做了归纳适合初学者先读文档再对照源码验证也是课设、毕设或单片机入门进阶的实用参考。1. 为什么 51 单片机做 DS18B20 温度采集仍然是经典入门方案温度采集是嵌入式开发里最常见的入门场景之一51 单片机加数字温度传感器 DS18B20 的组合至今仍是课程设计和温控类产品原型里的常客。它的好处很具体单总线只占一个 IO 口数字输出不需要额外 ADCProteus 里能直接仿真两三个小时就能看到真实读数。但这套方案真正的门槛不在接线而在时序——复位脉冲、读时隙、写时隙的宽度和采样点位置稍有偏差就读不到数据。这篇文章围绕 51 单片机 DS18B20 应用软件程序源码、Proteus 仿真实例和文档资料这一套常见组合把时序原理、驱动实现、仿真构建、参数修改和实物移植的排查点一次讲清楚适合做课程设计也适合想看懂源码后改装温控风扇、温控继电器项目的开发者。2. DS18B20 单总线时序与 51 单片机软件实现先把读写时序写对2.1 单总线电气基础与 51 单片机 IO 口的配合方式DS18B20 使用的单总线协议特点是同一根数据线既要传数据又要传命令。平时总线由外部上拉电阻保持在高电平主机或传感器要发起通信时把线拉低产生起始信号。这样的电气结构决定了一个关键事实所有通信时序都是“拉低—释放—采样”的组合谁拉低、什么时候释放、什么时候采样就是驱动 DS18B20 的全部核心。51 单片机的 P1P3 口是准双向口输出低电平时能主动灌入电流释放后引脚靠内部弱上拉恢复高电平所以不用额外硬件也能模拟单总线。需要注意内部上拉能力较弱实物板走线稍长时高电平容易被噪声拖低因此外部仍要接一个 4.7kΩ 上拉电阻。Proteus 仿真里不接上拉电阻往往也能出结果那是因为仿真模型对边沿的容差偏大这一点是后面实物调试踩坑最多的根源。写驱动代码之前先记住数据手册里的这组时序参数后面所有延时函数的取值都以它为基准信号时间说明主机复位脉冲拉低至少 480μs然后释放总线等待应答存在检测脉冲主机释放后 1560μs 内出现DS18B20 拉低总线 60240μs写时隙至少 60μs写 1 和写 0 都要完整占据一个时隙读时隙至少 60μs主机拉低 115μs 后释放并在 15μs 内采样时隙间恢复时间最小 1μs两个时隙之间总线必须回到高电平2.2 用 Keil C51 写复位、读位、写位三个底层驱动函数常见源码里DS18B20 驱动通常拆成三个底层函数复位、写一个位、读一个位。之后的读写字节和读取温度全部在这三个函数之上拼装。下面是一段在 12MHz 晶振、Keil C51 下可正常工作的写法#include reg51.h #include intrins.h sbit DQ P3^7; // DS18B20 数据线接在 P3.7 // 微秒级延时12MHz 下每个 _nop_() 约 1μs void DelayUs(unsigned int us) { while (us--) { _nop_(); // 加上循环跳转耗时整体约 1μs } } // 复位 DS18B20返回总线电平0 表示器件在线 unsigned char DS18B20_Reset(void) { unsigned char presence; DQ 0; // 主机主动拉低总线 DelayUs(500); // 复位脉冲保持 500μs大于 480μs 下限 DQ 1; // 释放总线让上拉电阻恢复高电平 DelayUs(60); // 等待 DS18B20 拉低总线 presence DQ; // 采样读到 0 说明存在应答 DelayUs(300); // 补足整个复位时序长度 return presence; }这段代码的逻辑是先把总线拉低足够长时间让 DS18B20 识别到复位信号释放后DS18B20 会在规定窗口内把总线拉低形成存在检测脉冲。presence DQ这一行把采样电平保存下来返回值 0 表示器件在线1 表示通信链路有问题。最后的DelayUs(300)看似多余实际作用是让整个复位时序完整结束避免紧接着的写命令抢占了错误的时间片。这里有个值得注意的边界释放总线后等待 60μs 再采样对大多数器件和 Proteus 模型都够用但某些批次芯片需要更久实物排错时如果返回值一直是 1把这里改成 80μs 或 100μs 再试。DelayUs循环受 Keil 优化级别影响明显开启高优化后空循环可能被压缩稳妥做法是把延时参数声明为volatile或者干脆用定时器实现。2.3 温度转换命令流程从 Skip ROM 到读取 16 位暂存器读写位的函数拼装完成后温度采集的完整流程分三段复位、发命令、读暂存器。看大多数网上的 51 单片机 DS18B20 源码主流程都长这样// 从 DS18B20 读取温度单位摄氏度 float DS18B20_GetTemperature(void) { unsigned char lo, hi; int tempRaw; DS18B20_Reset(); // 1. 复位并检测存在脉冲 DS18B20_WriteByte(0xCC); // 2. Skip ROM单点采集时跳过地址匹配 DS18B20_WriteByte(0x44); // 3. 启动温度转换 DelayMs(750); // 12 位默认分辨率下最长转换时间 750ms DS18B20_Reset(); // 4. 再次复位 DS18B20_WriteByte(0xCC); // 5. Skip ROM DS18B20_WriteByte(0xBE); // 6. 发出读暂存器命令 lo DS18B20_ReadByte(); // 7. 暂存器第 0 字节温度低 8 位 hi DS18B20_ReadByte(); // 8. 暂存器第 1 字节温度高 8 位 tempRaw (hi 8) | lo; // 拼成 16 位原始值 // 负数温度以补码保存转成有符号数 if (tempRaw 0x8000) { tempRaw tempRaw - 0x10000; } return tempRaw * 0.0625f; // 12 位分辨率下每一位代表 0.0625℃ }命令码 0xCC 是 Skip ROM意思是“不对总线上的传感器做地址匹配直接操作唯一挂载的器件”单点采集必须用它否则带地址匹配逻辑的代码会要求先读 ROM 码。0x44 启动温度转换转换期间 DS18B20 会占有总线并更新内部暂存器。0xBE 是读暂存器命令从第 0 字节开始连续吐出 9 个字节前两个字节分别是温度值的低 8 位和高 8 位。后面的换算需要单独说明DS18B20 的温度寄存器是 16 位有符号数12 位分辨率下低 4 位是小数位每一位权重 0.0625℃。比如原始值 0x0750高位是 0x07、低位是 0x50拼起来等于十进制 1872乘以 0.0625 正好是 12.00℃。用这个值反推仿真结果非常直观Proteus 里设 DS18B20 温度为 25.5℃读到的原始字节就是 0x0198。2.4 仿真中常见的时序错误怎么看Proteus 仿真模型对时序的宽容度明显高于实物时序偏差只要不是太大仿真照样能读出温度这导致很多人在仿真里没发现问题一到实物就死机。我一般会在仿真阶段就做两件事一是把虚拟示波器接到 DQ 线上看复位信号和存在检测脉冲的落点是否在协议窗口内二是把读位函数里的采样点前后加两个 GPIO 翻转用示波器确认采样时总线已经稳定。仿真能通过不代表延时精度没问题只能说明误差还在协议容忍范围内。3. 在 Proteus 中搭建 DS18B20 仿真实例元件选择、接线与调试3.1 搭建最简 DS18B20 温度采集电路需要的元件和接线Proteus 仿真 51 单片机温度采集电路的复杂程度可以做到很低单片机、DS18B20、上拉电阻、LCD 显示再加上晶振和复位电路就足够运行整套源码。下面是元件清单和 Proteus 里对应的搜索关键词元件Proteus 搜索关键词参数/型号用途51 单片机AT89C51 或 AT89C52AT89C52主控芯片加载 hex 程序数字温度传感器DS18B203 引脚直插单总线温度采集上拉电阻RES4.7kΩDQ 信号线接 VCCLCD 液晶屏LM016L16×2 字符显示温度值晶振CRYSTAL12MHz与源码延时函数匹配无源电容CAP22pF×2晶振负载电容电解电容CAP-ELEC10μF复位电路配合画原理图时DS18B20 的三根引脚中中间引脚 DQ 接 P3.7另外两脚分别接 VCC 和 GND上拉电阻从 DQ 引线接到 VCC。LCD 的 RS、RW、E 分别接 P2.0、P2.1、P2.2数据口 D4D7 接 P1.4P1.7这是典型的 4 线驱动接法比 8 线节省 4 个 IO。P0 口在 Proteus 仿真里不用上拉也能驱动 LCD但实物不行这一点后面单独说。3.2 加载 hex 文件、运行仿真并核对初始 85℃ 现象源码在 Keil C51 里编译通过后会生成 hex 文件。在 Proteus 中双击单片机元件在 Program File 属性里选择这个 hex 文件再把 Crystal Frequency 设置成 12MHz。这个频率值必须和源码延时函数针对的晶振一致否则延时会整体偏移。很多 Proteus 仿真 51 单片机项目跑不出温度问题就出在单片机属性页面的频率没设程序实际跑在默认的 1MHz 上时序完全不对。点击运行后最常见的一个现象是屏幕显示 85℃。85℃ 是 DS18B20 上电复位后暂存器里的默认值不是实际温度出现这个读数说明程序执行到了读取这一步但转换可能没完成或者转换命令没发出去。先回到代码里检查主循环是否先调用转换命令、是否等待了足够的转换时间、读取前有没有重新复位。把这三个点确认一遍85℃ 的问题基本能解决一半。如果屏幕乱码或者完全不显示先别查 DS18B20用固定数据测试显示链路。比如临时把raw赋值为 0x0750如果 LCD 正常显示 12.00℃说明显示驱动没问题问题回到单总线通信上。3.3 用 Proteus 虚拟示波器检查单总线波形Proteus 提供虚拟示波器工具在左侧工具栏的 Virtual Instruments 面板中选择 OSCILLOSCOPE把探针接到 DQ 信号线上运行后就能看到单总线的波形变化。看波形时有几个固定落点需要关注复位阶段DQ 应该先被拉低约 500μs释放后出现一个明显的低电平脉冲这就是存在检测脉冲。如果看不到这个脉冲先查 DS18B20 的供电和上拉电阻。读写时隙阶段波形会变成密集的“低—高”跳变每 60μs 左右一个周期。要确认主机释放总线后是否有一小段高电平让上拉电阻恢复且采样点在电平稳定区域而不是在跳变沿上。示波器调出波形之后再把源码里的延时系数逐个加长和缩短观察波形边沿的位置变化这样可以反向验证代码里的延时函数是否真的按注释值工作。这一步在仿真阶段做掉实物阶段会省很多时间。4. 看懂源码后要改的几个参数分辨率、报警阈值与多路采集4.1 通过写暂存器修改分辨率并留意换算系数DS18B20 默认工作在 12 位分辨率转换时间最长 750ms。如果项目要求秒级刷新或者一条总线上挂多个传感器轮流读取750ms 的等待会让整个系统响应变慢。这时可以通过写暂存器命令把分辨率降到 9 位、10 位或 11 位但代价是温度精度随之下降。写暂存器命令的格式是复位、发 0xCC、发 0x4E然后连续写入 TH、TL、配置字节三个数据。这里容易被忽略的是即使不使用报警功能TH 和 TL 也必须写入因为 0x4E 命令要求连续写入完整的三个字节缺一个就会导致后续数据错位。下面是一次完整的写配置过程void DS18B20_SetResolution(unsigned char config) { DS18B20_Reset(); // 复位 DS18B20_WriteByte(0xCC); // Skip ROM DS18B20_WriteByte(0x4E); // 写暂存器命令 DS18B20_WriteByte(0x00); // TH 报警上限未使用置 0 DS18B20_WriteByte(0x00); // TL 报警下限未使用置 0 DS18B20_WriteByte(config); // 配置字节R1/R0 决定分辨率 }配置字节中只用到 R1、R0 两个位其余位保持 1。对应的常用取值如下R1R0分辨率典型转换时间配置字节009 位93.75ms0x1F0110 位187.5ms0x3F1011 位375ms0x5F1112 位750ms0x7F改分辨率后必须同步修改温度换算系数9 位时每一位代表 0.5℃10 位是 0.25℃11 位是 0.125℃12 位是 0.0625℃。我在代码里见过不少只改配置字节、没改换算系数的案例结果整个量程的温度都按比例偏大表面看像传感器坏了其实是系数没跟着变。4.2 报警寄存器 TH/TL 的设置与滞回温控判断DS18B20 内部有报警功能转换结果大于等于 TH 或小于等于 TL 时暂存器里的报警标志位置位主机还能用报警搜索命令 0xEC 一次性找出所有越限的传感器。在简单的温控项目里很多人不用这个内部报警而是在主循环里直接比较温度值再用继电器控制风扇或加热器这时有一个细节值得注意比较逻辑要加滞回。if (temp 40.0f) { RELAY 0; // 温度高于 40℃ 继电器吸合 } else if (temp 35.0f) { RELAY 1; // 温度低于 35℃ 继电器释放 }这段代码里40℃ 是吸合点35℃ 是释放点中间 5℃ 的温差就是滞回区间。如果没有滞回温度在 40℃ 附近波动时继电器会频繁通断触点寿命和电磁干扰都会受影响。基于 51 单片机的温控风扇设计、温控继电器项目核心逻辑都是这个结构改成多级风扇转速只是在继电器控制基础上再加 PWM。4.3 多颗 DS18B20 共线时的 ROM 匹配读取方式一条单总线上挂两个或多个 DS18B20 时不能再用 Skip ROM 命令因为 0xCC 会让所有传感器同时响应数据在总线上打架。正确做法是先读每颗传感器的 64 位 ROM 码再用匹配 ROM 命令 0x55 指定要操作的器件。读取流程是复位发 0x33 读 ROM 命令连续读出 8 字节 ROM 码之后每次操作前先复位发 0x55再把这颗传感器的 8 字节 ROM 码逐字节发出去。匹配成功后后续的温度转换和读暂存器命令只作用在这颗传感器上。换下一颗传感器时重新复位、发 0x55、换另一组 ROM 码。在 Proteus 里仿真多路 DS18B20需要分别设置每颗传感器的 ROM 码这一步比较繁琐但能验证程序里的 ROM 匹配逻辑是否写对。多路轮询的实时性要按这个公式估算单颗 12 位分辨率转换时间 750ms总线上挂 N 颗串行轮询一轮至少 N×750ms。想加快就得降分辨率或者减少每一轮的读取次数。4.4 主循环里用状态机避免 750ms 阻塞在 51 单片机课程设计里最常见的问题是主循环里调用一次温度读取函数就卡住 750ms期间按键扫描、数码管刷新、串口发送全部停顿。避免这种阻塞的常见做法是把温度读取拆成状态机用定时器中断累加转换时间主循环只负责查询状态。if (convertStart) { DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0x44); // 启动转换不等待 convertStart 0; timerCount 0; } if (timerCount 50) { // 定时器每 15ms 中断一次50 次约 750ms DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读取上次转换结果 lo DS18B20_ReadByte(); hi DS18B20_ReadByte(); temp ConvertTemp(lo, hi); LcdShow(temp); convertStart 1; // 开启下一轮转换 }这个结构里主循环每转一圈只执行“启动转换”或“读取结果”中的一个动作单次耗时不超过几毫秒其余时间继续扫描按键和处理其他外设。定时中断只负责给计数器加 1不参与单总线通信避免在 DS18B20 通信过程中被中断插入导致时序错乱。5. 从 Proteus 仿真转到实物板之前先核对这 4 个容易忽略的点5.1 上拉电阻阻值和 P0 口上拉问题实物板上的单总线上拉电阻我一般先放 4.7kΩ总线长度超过 30cm 时再并一个 2.2kΩ这样可以补偿线缆电容带来的边沿变缓。另一个高频坑是 P0 口Proteus 仿真里 P0 口不加上拉电阻也能驱动 LCD但实物上 P0 是开漏输出高电平必须靠外部上拉提供典型做法是接一个 10kΩ 排阻或者 8 个独立 4.7kΩ 电阻。把这两点上拉处理好实物调试至少能少一半黑屏问题。5.2 一上电就读到 85℃ 如何处理85℃ 是 DS18B20 上电后的暂存器默认值实物调试时如果读数稳定在 85.00℃ 且不变化说明程序确实读到了数据但转换结果没有更新。优先检查三处复位函数返回值是否正确、0x44 转换命令发出后是否等待足够时间、读取前是否重新复位。用示波器抓 DQ 引脚时重点看存在检测脉冲是否出现在正确的时间窗口内。如果复位都不成功检查焊接和供电电压DS18B20 在 3.0V5.5V 范围内都能工作但电压波动会影响时序余量。现象优先检查常见原因读数稳定在 85℃复位返回值、转换等待转换未完成或复位失败读数每隔几秒跳一次大值上拉电阻、总线长度总线电容过大边沿变慢负数温度显示乱码温度转换代码补码没有转成有符号数5.3 晶振从 12MHz 换到 11.0592MHz 时延时要重新计算很多项目为了串口波特率准确实物上改用 11.0592MHz 晶振但源码里的延时函数还是按 12MHz 写的。12MHz 下机器周期正好 1μs11.0592MHz 下一个机器周期约 1.085μs所有基于空循环的延时都会比预期长 8.5%。延时长一点通常还能容忍延时偏短才危险。稳妥的做法是把延时拆成“次数计算”和“晶振换算”两层换晶振只改一个宏#define FOSC 11059200UL #define US_COUNT(us) (unsigned int)((12UL * 1000000UL * (us)) / FOSC)这样DelayUs(US_COUNT(480))生成的计数会随 FOSC 自动变化换一种晶振不用到处改延时函数。用定时器延时也可以但注意定时器计数周期同样是机器周期换晶振后初值必须重新算。5.4 用冰水浴和沸水做两点温度校准拿到实物后不要只用手心温度对比做一个最简单的两点校准把探头插入冰水混合物读数应该在 0℃ 附近再放入沸水读数应该在 100℃ 附近。如果两个点偏差方向一致并且数值固定多半是换算系数用错直接修正常数即可如果低温准高温不准通常是 DS18B20 的本征非线性校准结果只对测量区间内有效。校准完成后重新编译烧录再记录一组多点温度误差确认系统性偏差消失。这套流程做完从 Proteus 仿真到实物的整体闭环就算真正走通了。本文还有配套的精品资源点击获取