ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C51单片机驱动1-Wire总线与DS18B20传感器实战指南

C51单片机驱动1-Wire总线与DS18B20传感器实战指南 简介本资源是一套面向嵌入式初学者与8051单片机开发者的1-Wire总线通信实战代码包聚焦于单总线协议在温度传感等低功耗场景中的底层实现。资源以C51语言为核心完整呈现主控端如8051对DS18B20等典型1-Wire器件的初始化、ROM搜索、命令发送、时序控制及CRC校验等关键环节适用于Keil uVision开发环境下的硬件调试与协议理解。压缩包共17个文件含1个核心C源文件ledtest.c、1个汇编实现模块wireasm.asm、1个1-Wire专用头文件ledtest.h、1个可烧录hex文件及多个编译中间产物obj/lst/m51和工程配置备份Uv2.Bak、Opt.Bak整体仅49KB轻量易导入。已有317人学习下载配套www.pudn.com.txt提供来源说明目录结构体现典型Keil工程组织逻辑便于读者逐层剖析时序细节、比对C与ASM混合编程策略并快速复现单总线多节点通信功能。1. 项目概述从“one.zip”到C51的1-Wire总线实战最近在整理老项目的资料库翻出了一个名为“one.zip”的压缩包。这个文件名简单到让人会心一笑里面装的正是我多年前用C51单片机折腾1-Wire总线时攒下的一堆代码、原理图和调试笔记。1-Wire总线这个由达拉斯半导体现属美信发明的单线通信协议以其极简的硬件连接一根数据线加地线和独特的寄生供电能力在温湿度传感、电子标签、设备认证等场景中经久不衰。尽管如今ARM Cortex-M内核大行其道但在一些对成本极度敏感、功能专一的小型设备上经典的8051内核单片机我们常说的C51依然有其用武之地。这个“one.zip”项目就是如何在资源有限的C51平台上稳定、可靠地实现1-Wire总线通信的完整实践记录。它不仅仅是一份代码更是一套包含了时序精准控制、抗干扰设计、以及针对DS18B20这类经典器件驱动的心法。如果你正在或即将在51系列单片机上对接1-Wire器件那么这份从老硬盘里挖出来的经验或许能帮你避开我当年踩过的那些坑。2. 1-Wire总线协议核心精要与C51适配挑战1-Wire协议的精髓在于它仅用单根数据线就实现了双向数据通信和对从设备的供电。这根线上挂载的所有设备都通过一个唯一的64位ROM ID进行寻址支持多设备挂载。通信完全由主机Master即我们的C51单片机发起和控制通过严格的时间槽来区分数位“0”、“1”以及复位、存在脉冲等命令。2.1 协议基础时序就是一切1-Wire通信的基石是精确的微秒级时序。所有操作都始于一个由主机发出的复位脉冲至少480µs的低电平随后主机释放总线并切换到接收模式。在线路上的1-Wire从设备会在等待15-60µs后回应一个存在脉冲60-240µs的低电平。主机检测到这个脉冲便确认总线上有设备就绪。随后的数据读写操作均以时间槽为单位。每个时间槽通常持续60-120µs用于传输1比特数据写“1”时间槽主机拉低总线1-15µs然后释放剩余时间保持高电平。写“0”时间槽主机拉低总线至少60µs最多120µs然后释放。读时间槽主机拉低总线1-15µs后释放然后在短暂的采样窗口通常在起始下降沿后15µs内读取总线电平。高电平为“1”低电平为“0”。注意上述时间参数是DS18B20等经典器件的典型值不同厂商、型号的1-Wire器件可能存在细微差异务必以具体器件的数据手册为准。2.2 C51平台的特殊挑战与应对思路在STM32等现代MCU上我们可以轻松使用硬件定时器产生精准延时甚至利用外部中断或IO口翻转功能来捕获信号。但在传统的C51如AT89C51、STC89C52上我们面临几个现实挑战主频较低且可变常见C51工作在11.0592MHz或12MHz每个机器周期1µs或约1µs。延时需要靠软件循环实现精度受编译器优化和中断影响。无高精度硬件定时器虽然有两个16位定时器但用于µs级延时开销过大且可能影响其他功能。中断响应延迟如果系统中开启了中断在读写1-Wire的关键时序窗口内若发生中断可能导致时序超差通信失败。因此在C51上实现1-Wire驱动的核心思路是编写高度优化的、可重入的微秒级延时函数并在执行关键1-Wire时序操作时短暂关闭全局中断EA0。同时IO口应配置为准双向模式这是51单片机IO的默认模式具有弱上拉能力与1-Wire总线要求相符。3. C51驱动1-Wire总线的核心代码实现与解析下面我将结合“one.zip”中的核心代码模块拆解如何在C51上构建一个稳健的1-Wire驱动层。我们以驱动最常见的DS18B20数字温度传感器为例。3.1 硬件连接与IO口定义假设使用P3.7口作为1-Wire总线。// 1-Wire 总线引脚定义 sbit DQ P3^7; // 数据线硬件上DQ引脚需要接一个4.7kΩ的上拉电阻至VCC这是1-Wire总线标准所要求的用于确保总线在空闲时处于高电平状态并为寄生供电的从设备提供能量。3.2 微秒级延时函数的实现这是整个驱动的精度基础。由于C51指令周期固定我们可以用_nop_()空操作1个机器周期和循环来构建延时。/** * brief 微秒级延时 (适用于12MHz晶振1机器周期1us) * param t: 需要延时的微秒数对于12MHzt即us数。 * 注意此函数本身有调用开销需校准。 */ void Delay_us(unsigned char t) { while (t--) { _nop_(); _nop_(); _nop_(); _nop_(); // 根据实际测试调整_nop_数量以校准 } }实操心得这个延时函数非常不精确因为它受编译器优化和循环开销影响。更可靠的方法是使用定时器。但对于简单应用可以通过示波器或逻辑分析仪调整循环体内的_nop_()数量进行粗略校准。在“one.zip”的后期版本中我改用定时器0工作在模式28位自动重装来产生一个基准的10µs或50µs中断在此基础上构建更精确的延时。这是从“能用”到“稳定”的关键一步。3.3 1-Wire复位与存在检测这是每次通信会话的开始。/** * brief 1-Wire 复位脉冲并检测存在脉冲 * retval 1: 有设备响应0: 无设备响应或总线错误 */ bit OW_Reset(void) { bit presence_pulse; EA 0; // 关闭全局中断防止时序被干扰 DQ 0; // 主机拉低总线产生复位脉冲 Delay_us(480); // 保持低电平至少480us DQ 1; // 释放总线主机切换为接收 Delay_us(70); // 等待15-60us后采样这里取70us保证安全 presence_pulse DQ; // 采样总线电平低电平表示有存在脉冲 Delay_us(410); // 等待存在脉冲周期结束 (最少480us - 70us) EA 1; // 重新开启中断 return !presence_pulse; // 如果采样到低电平(presence_pulse0)则返回1有设备 }关键点解析EA0/EA1包裹关键时序操作这是C51上提高1-Wire通信可靠性的黄金法则。否则一个意外的定时器或串口中断就可能导致拉低或采样时间偏差几十微秒导致失败。延时参数480、70、410这些数值是基于12MHz晶振和粗略软件延时校准后的结果。你需要根据自己系统的实际主频和延时函数精度进行调整。返回值逻辑存在脉冲期间从机会拉低总线所以主机采样到低电平DQ0表示有设备函数返回1。3.4 单比特读写操作所有字节的读写都建立在单比特操作上。/** * brief 向1-Wire总线写入1比特数据 * param bit_value: 要写入的比特值0或1 */ void OW_WriteBit(bit bit_value) { EA 0; DQ 0; // 主机拉低总线启动时间槽 _nop_(); _nop_(); // 极短延时约2us (12MHz) DQ bit_value; // 如果是写1则释放总线写0则保持低电平 Delay_us(60); // 保持低电平时间对于写0是60us写1则很快释放 DQ 1; // 释放总线结束时间槽 // 两个时间槽之间需要至少1us的恢复时间此处用短延时 _nop_(); _nop_(); EA 1; } /** * brief 从1-Wire总线读取1比特数据 * retval 读取到的比特值0或1 */ bit OW_ReadBit(void) { bit read_value; EA 0; DQ 0; // 主机拉低总线启动读时间槽 _nop_(); _nop_(); // 极短延时约2us DQ 1; // 主机释放总线准备采样 _nop_(); _nop_(); _nop_(); _nop_(); // 等待约4us建立采样窗口 read_value DQ; // 在下降沿后约15us内采样 Delay_us(55); // 等待读时间槽剩余时间结束 EA 1; return read_value; }为什么读和写函数看起来这么像因为1-Wire的读操作也是由主机发起的。主机先拉低总线1-15µs发出“读命令”然后释放并采样。从设备会在主机拉低总线后决定是否将总线拉低来回应“0”。所以OW_ReadBit函数开头拉低又释放的动作本质上是在发起一个“读时间槽”的请求。3.5 字节读写函数构建有了单比特读写字节操作就是循环。/** * brief 向1-Wire总线写入1字节数据 * param byte_value: 要写入的字节 */ void OW_WriteByte(unsigned char byte_value) { unsigned char i; for (i 0; i 8; i) { OW_WriteBit(byte_value 0x01); // 先写最低位 (LSB first) byte_value 1; } } /** * brief 从1-Wire总线读取1字节数据 * retval 读取到的字节 */ unsigned char OW_ReadByte(void) { unsigned char i, byte_value 0; for (i 0; i 8; i) { byte_value 1; // 先右移 if (OW_ReadBit()) { byte_value | 0x80; // 如果读到1则设置最高位 } } return byte_value; }注意事项1-Wire协议规定先传输字节的最低位LSB First。OW_WriteByte中byte_value 0x01和OW_ReadByte中先右移再置位最高位的写法正是遵循了这一规则。这是很多初学者容易出错的地方。4. 驱动DS18B20温度传感器的完整应用流程有了底层读写函数我们就可以按照DS18B20的命令序列来操作了。一次完整的温度转换和读取流程如下4.1 初始化与ROM命令复位与存在检测调用OW_Reset()确认传感器在线。发送ROM命令如果总线上只有一个DS18B20可以使用跳过ROM命令0xCC直接对总线上所有设备进行操作。这是单设备场景下最常用的命令简化了流程。OW_WriteByte(0xCC); // 跳过ROM命令4.2 发送功能命令与读取数据发送功能命令紧接着发送启动温度转换命令0x44。OW_WriteByte(0x44); // 启动温度转换此时DS18B20开始进行温度转换。对于12位分辨率默认转换时间最长为750ms。在此期间主机可以释放总线去做其他事情或者通过OW_ReadBit()读取DQ线如果为低则表示转换未完成为高则表示完成也可用延时等待。再次初始化转换完成后需要再次发起复位和存在检测序列。发送读取命令再次发送跳过ROM命令0xCC然后发送读暂存器命令0xBE。OW_Reset(); OW_WriteByte(0xCC); // 跳过ROM OW_WriteByte(0xBE); // 读暂存器命令读取温度数据连续读取9个字节DS18B20暂存器共9字节。温度值存储在前两个字节LSB和MSB。unsigned char temp_data[9]; for(i 0; i 9; i) { temp_data[i] OW_ReadByte(); }数据解析将两个字节组合成一个16位有符号整数。DS18B20的温度数据以1/16°C为LSB。int raw_temp (temp_data[1] 8) | temp_data[0]; // 合成16位数据 float temperature; if (raw_temp 0x8000) { // 判断是否为负数 raw_temp ~raw_temp 1; // 取补码 temperature -((float)raw_temp * 0.0625); } else { temperature (float)raw_temp * 0.0625; // 0.0625 1/16 }temp_data[1]是高字节temp_data[0]是低字节。0.0625是分辨率。4.3 寄生供电模式下的强上拉操作如果DS18B20采用寄生供电即VDD引脚接地完全依靠数据线在空闲时通过上拉电阻供电在执行温度转换(0x44)或拷贝暂存器到EEPROM(0x48)等耗电较大的操作时必须在命令发出后的10µs内将总线通过一个MOSFET强拉到VCC提供更大电流并持续到操作完成。这在C51上通常通过一个额外的IO口控制MOSFET实现。sbit POWER_CTRL P1^0; // 控制强上拉MOSFET的引脚 // 发送启动转换命令后 OW_WriteByte(0x44); POWER_CTRL 1; // 开启强上拉 Delay_ms(750); // 等待转换完成时间根据分辨率而定 POWER_CTRL 0; // 关闭强上拉重要提示如果使用外部电源为DS18B20供电VDD接3.3V或5V则不需要强上拉操作通信可靠性会更高。这是提升系统稳定性的一个关键设计选择。5. 多设备挂载与ROM ID搜索算法当一根1-Wire总线上挂有多个设备如多个DS18B20时就需要通过唯一的64位ROM ID来区分它们。这涉及到更复杂的搜索ROM算法命令0xF0。5.1 搜索算法原理简述搜索算法是一个基于“冲突”的递归过程。主机通过“读”操作让所有设备在同一时间槽内回复自己ROM ID的某一位。如果所有设备在该位都回复相同的值0或1主机就得到该位的确定值。如果有的设备回复0有的回复1就发生了“冲突”。主机此时必须做出“选择”先向0方向搜索还是向1方向搜索。通过记录这些选择点主机可以遍历总线上所有设备的ROM ID。5.2 C51上的简化实现考量完整的搜索算法实现起来代码量较大且对时序和状态记录要求高。在资源紧张的C51上有更实用的替代方案单设备跳过ROM如果应用确定只有单一设备直接使用0xCC跳过ROM命令这是最简单可靠的。已知ROM ID直接寻址如果设备ROM ID已知可通过编程器或一次性搜索后存入EEPROM则可以使用匹配ROM命令0x55后跟64位ROM ID直接与特定设备对话。unsigned char rom_code[8] {0x28, 0xFF, 0x...}; // 已知的ROM ID OW_Reset(); OW_WriteByte(0x55); // 匹配ROM命令 for(i0; i8; i) { OW_WriteByte(rom_code[i]); } // 接下来发送功能命令...仅在上电时搜索一次在系统初始化时执行一次完整的搜索算法将找到的所有ROM ID存入数组。之后的正常操作中使用“匹配ROM”命令进行寻址。这样可以避免在每次测温时都进行耗时的搜索。对于大多数C51应用方案2或3更为可行。完整的搜索算法代码较长在“one.zip”中有一个实现版本其核心是维护一个“上次分歧位”的变量并递归地尝试两条路径。由于篇幅所限这里不展开全部代码但其关键在于正确处理读回数据中的冲突位并管理好搜索路径的回溯。6. 调试技巧、常见问题与稳定性优化在实际焊接电路和编写代码时问题总会不期而至。以下是我从“one.zip”项目的调试笔记中总结出的精华。6.1 硬件问题排查清单现象可能原因排查方法始终无存在脉冲1. 接线错误DQ、GND2. 上拉电阻未接或损坏3. 传感器损坏4. 电源问题寄生供电时电流不足1. 用万用表检查通断。2. 确认4.7kΩ上拉电阻正确连接至VCC。3. 更换传感器测试。4. 尝试改为外部供电VDD接3.3V/5V。偶尔通信失败数据乱码1. 时序不精确2. 总线受到干扰长线、靠近电机等3. 中断干扰关键时序4. 寄生供电模式下未加强上拉1. 用逻辑分析仪抓取波形对比DS18B20时序图。2. 缩短总线长度使用双绞线远离干扰源。3. 确认在OW_ReadBit、OW_WriteBit等函数中关闭了中断EA0。4. 在转换命令后增加强上拉电路并控制。读回的温度值固定为85°C或0°C85°C是上电默认值0°C可能是读取错误。通常是因为复位/读写时序不符合要求导致DS18B20没有正确执行命令。重点检查OW_Reset和OW_WriteBit/OW_ReadBit中的延时参数。使用逻辑分析仪是最高效的手段。6.2 软件层面的稳定性优化增加重试机制任何一次OW_Reset()或读写操作失败都应加入重试逻辑。例如连续进行3次复位检测只有两次成功才认为初始化成功。bit OW_Reset_With_Retry(unsigned char retries) { while(retries--) { if(OW_Reset()) { return 1; // 成功 } Delay_ms(2); // 失败后稍作延迟再试 } return 0; // 全部重试失败 }CRC校验DS18B20暂存器的第9字节是前面8字节的CRC校验码。在读取温度数据后应计算前8字节的CRC并与读回的第9字节对比。如果不匹配则丢弃本次数据并重试。这能有效发现因干扰导致的传输错误。“one.zip”里包含了一个针对DS18B20的CRC8查表法计算函数。精准延时校准放弃不可靠的纯软件循环延时。使用定时器中断来产生一个基准时间单元如10µs。所有1-Wire延时都基于这个基准单元进行计数精度和可移植性大幅提升。总线恢复在连续多次通信失败后可以尝试让主机连续输出多个480µs以上的低电平脉冲类似长复位这有助于将可能“卡住”的从设备状态机拉回初始状态。6.3 逻辑分析仪你的最佳搭档没有逻辑分析仪调试1-Wire就像蒙着眼睛走路。一个廉价的USB逻辑分析仪如Saleae Logic 8克隆版配合DSView等软件可以直观地看到复位脉冲、存在脉冲、每一个读写时间槽的电平变化。将抓取的波形与数据手册中的时序图严格对比是定位时序问题最快、最直接的方法。你可以清晰地看到你的“15µs低电平”在实际波形中是10µs还是20µs采样点是否在有效窗口内。7. 从C51到现代MCU的思考与项目扩展虽然本项目聚焦于C51但1-Wire协议的思想是通用的。在现代的STM32、GD32等ARM Cortex-M MCU上实现方式有了更多选择GPIO模拟思路与C51完全相同但得益于更高的主频和更精准的定时器如SysTick延时可以做得更精确。同时利用GPIO的“位带”操作可以像操作C51的sbit一样直接操作单个IO非常方便。外部中断检测可以利用MCU的外部中断功能在主机拉低总线启动读时间槽后配置中断在下降沿或上升沿触发从而更精准地捕获从设备的响应减少软件延时的依赖。定时器PWM与输入捕获更高级的用法是利用定时器的PWM模式产生精确的复位脉冲和写时间槽利用输入捕获模式来测量存在脉冲的宽度和读时间槽的响应。这几乎将CPU完全解放出来。“one.zip”项目虽然基于古老的C51平台但它所涉及的精确时序控制、抗干扰设计、协议层状态机实现、以及调试方法论是嵌入式开发中普适的技能。理解了这个在资源受限环境下的实现当你转移到功能更强大的平台时不仅能轻松移植更能理解那些高级外设如UART in smart-card mode, I2C等为何要如此设计。最后如果你打算深化这个项目可以考虑添加更多的1-Wire器件支持如DS2431 EEPROM、DS2413 IO扩展器甚至尝试用C51做一个简单的1-Wire网络嗅探器那将会是对协议理解的又一次升华。本文还有配套的精品资源点击获取
返回列表