十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

51单片机DS18B20温度采集:单总线时序与Keil工程实战

51单片机DS18B20温度采集:单总线时序与Keil工程实战 简介面向单片机初学者这份DS18B20温度采集实例以C语言实现并配有Proteus仿真电路与Keil工程打开后即可运行和调试。通过学习可掌握单总线通信的复位、存在检测、读写时序理解数字温度传感器的读取原理以及温度值在液晶屏上的显示方式适合课程设计、实验实训和自学入门。压缩包共十四个文件包含源程序、仿真工程、编译配置与输出文件其中代码文件负责驱动传感器和液晶显示仿真相关文件用于电路验证十六进制文件可直接烧录到开发板另有启动文件、列表文件、目标文件、映射文件等可辅助查错与内存分析。整体仅十四KB内容精炼对初学者理解工程结构很有帮助。目前已有四百人学习下载适合需要快速完成温度采集项目的开发者。使用时建议先运行仿真观察效果再对照源码理解时序控制整个流程清晰能够缩短项目开发周期也可作为课程设计或实验项目的可复用基础模板。1. 温度采集这件事卡住多数人的是单总线时序而不是C语言51单片机读取DS18B20这个实验几乎每个嵌入式初学者都做过但真正在Proteus里跑通、在实物上稳定读数的并不多。很多人用开发板附带的例程直接下载现象没问题换了自己画板子或者换一颗传感器就乱码问题往往出在时序延时和总线驱动上。这套“194-温度采集DS18B20”实例包包含完整的Keil C51工程源码、Proteus仿真文件和编译中间文件适合正在学51单片机课程设计、准备电子设计竞赛或者想搞明白单总线协议到底怎么用代码实现的工程师。包里没有花哨的架构就是AT89C51或者STC89C52最典型的最小系统加一个DS18B20温度结果通过LCD1602显示。拆开这份工程能同时看到C语言层面怎么操作单总线以及Keil编译链接之后生成hex文件的完整流程。2. 单总线协议与DS18B20的ROM指令集2.1 单总线为什么能在一条线上完成双向通信DS18B20用的是Maxim的1-Wire单总线协议数据线和电源线可以共用一根线寄生供电模式下甚至只需要两根线连到单片机。但代价是通信时序非常严格每bit的读写在微秒级别。51单片机没有硬件1-Wire控制器必须用普通IO口模拟时序这就要求对延时函数足够敏感。DS18B20上电后默认处于空闲状态主机要发起一次温度转换需要依次发送初始化复位脉冲、ROM命令、功能命令。ROM命令用来选中总线上某一颗具体的传感器如果总线上只挂一颗可以直接发跳过ROM指令0xCC省去64位序列号的匹配过程。实例包里就是单传感器场景所以代码里直接用0xCC跳过ROM匹配。2.2 ROM指令和功能指令速查指令十六进制码作用适用场景跳过ROM0xCC不匹配序列号直接访问总线上的传感器单点测温常用读ROM0x33读取64位序列号多点测温前枚举设备匹配ROM0x55根据序列号选中指定传感器多点测温时必须用温度转换0x44启动一次A/D转换结果存入内部RAM每次测温前调用读暂存器0xBE读取9字节暂存数据含温度值和校验转换完成后调用写暂存器0x4E写入报警阈值和配置寄存器设置分辨率和报警注意0x44转换命令发出后如果采用寄生供电主机必须在随后480微秒内把总线拉高否则传感器无法完成转换。实例工程用的是外部供电这个坑不明显但如果你在自制板上用寄生模式这里要格外小心。2.3 初始化序列才是第一个容易翻车的地方DS18B20的初始化时序分为复位脉冲和存在脉冲两段主机先把总线拉低480到960微秒然后释放总线由上拉电阻把总线拉回高电平。DS18B20检测到这个下降沿后等待15到60微秒然后主动把总线拉低60到240微秒作为应答信号。unsigned char ds18b20_init(void) { unsigned char presence; DQ 0; // 主机拉低总线 delay_us(500); // 复位脉冲低电平保持500us DQ 1; // 释放总线 delay_us(60); // 等待传感器应答窗口 presence DQ; // 读取存在脉冲0则在线 delay_us(400); // 完整结束时序 return presence; }这段代码里presence读到0说明传感器在线读到1说明总线上没有设备。实际调试中如果初始化总是失败优先查两个地方一是500微秒的延时是否准确二是DQ引脚是否配置成了推挽输出。标准51单片机IO口是准双向口读引脚前需要先写1但这里释放总线后直接读是因为准双向口本身有弱上拉逻辑上可行。如果你用的是增强型51开了推挽模式释放总线后读到的状态会受推挽输出影响反而容易出错。2.4 读时序要卡在15微秒的采样点上读时序比写时序更复杂。主机先拉低总线1微秒以上然后释放DS18B20检测到这个起始脉冲后如果当前位是0就把总线拉低如果当前位是1就保持高电平。主机在起始脉冲后15微秒左右读电平就能拿到数据。unsigned char ds18b20_read_bit(void) { unsigned char dat; DQ 0; _nop_(); // 至少1us延时 DQ 1; _nop_(); _nop_(); dat DQ; // 释放后约10us读取 delay_us(50); // 剩余时隙补满60us return dat; }这个读bit函数的关键是采样点位置。nop()在12MHz晶振下约1微秒两个nop之后大约3到4微秒加上读语句自身执行时间采样点落在数据有效的窗口内。如果你换了24MHz晶振两个nop变成0.5微秒采样点会提前可能读到总线还没被DS18B20拉低的瞬间状态。3. Keil工程结构拆解从STARTUP.A51到hex文件的编译链路3.1 实例包里的文件分别是什么角色打开压缩包能看到一堆扩展名各异的文件这些不是冗余垃圾而是Keil C51编译工程的不同阶段产物。搞懂它们的作用排错的时候就知道该看哪个文件。文件后缀作用排错价值.Uv2Keil工程文件双击打开工程工程配置入口.cC语言源码主逻辑所在.A51启动文件汇编写的通常不需要改.hex编译生成的机器码烧录或加载到Proteus.LST列表文件C源码和汇编对照排查底层问题.M51内存映射文件查看寄存器占用和段分配.lnp链接参数文件了解链接过程STARTUP.A51是Keil自动生成的启动代码它的主要工作是清零内部数据存储器和外部RAM然后调用C语言的main函数。这文件在工程里属于“存在但别碰”的类型。如果你在某次实验后发现变量初值不对可以先怀疑是不是STARTUP.A51被改动过。3.2 编译流程中LST和M51文件怎么看当Keil编译完成后每个C源文件会生成对应的.LST文件。这个文件把C语句和对应的汇编指令放在一起例如可以看到DQ 0;被翻译成CLR P1.0还是ANL P1.0, #0FEh取决于引脚定义方式。如果你怀疑编译器优化掉了某条语句打开LST文件搜索对应变量名就能确认。M51文件是内存映射文件它告诉你每个变量被分配在哪个地址。对于51单片机来说data段通常从0x00开始bit段从0x20开始。如果编译时报DATA SEGMENT TOO LARGE打开M51文件能看到哪个函数占用的data空间最多然后决定是否把它改成idata或xdata。3.3 编译产生的hex文件如何加载到ProteusProteus的AT89C51模型支持直接加载hex文件。双击单片机元件在Program File选项里浏览到温度采集DS18B20LCD.hex。注意Proteus的仿真速度默认是实时但DS18B20模型内部有严格的时序模拟如果仿真跑得太快或者太慢可能导致读到的温度乱跳。这时在Proteus右下角调整仿真速度通常设置为20fps左右比较合适。# 如果使用命令行方式编译可以用以下命令 C51 温度采集DS18B20LCD.c DEBUG OBJECTEXTEND L51 温度采集DS18B20LCD.obj STARTUP.obj TO 温度采集DS18B20LCD.abs OH51 温度采集DS18B20LCD.abs命令行编译方式虽然不常用但在CI环境或者批量编译场景下能派上用场。第一条命令把C源码编译成目标文件第二条命令把目标文件和启动文件链接成绝对地址文件第三条命令把绝对地址文件转换成hex格式。中间那些.Bak、.plg文件都是Keil的备份和编译日志不影响最终结果。4. C语言读写DS18B20初始化、温度转换与数值解析4.1 完整的温度读取流程DS18B20每次测温需要经过三步初始化、发转换命令并等待转换完成、发读暂存器命令并读取两个字节温度数据。float ds18b20_get_temperature(void) { unsigned char high, low; int temp_raw; float celsius; ds18b20_init(); // 1. 复位脉冲 存在检测 ds18b20_write_byte(0xCC); // 2. 跳过ROM匹配 ds18b20_write_byte(0x44); // 3. 启动温度转换 while(!DQ); // 4. 等待转换完成 ds18b20_init(); // 5. 再次复位准备读数据 ds18b20_write_byte(0xCC); // 6. 跳过ROM ds18b20_write_byte(0xBE); // 7. 发出读暂存器命令 low ds18b20_read_byte(); // 8. 读取温度低字节 high ds18b20_read_byte(); // 9. 读取温度高字节 temp_raw (high 8) | low; // 拼合成16位整数 celsius temp_raw * 0.0625; // 乘以分辨率系数 return celsius; }这段代码最关键的是while(!DQ);这行。DS18B20在12位分辨率下完成一次转换需要750毫秒转换期间总线被传感器拉低转换完成后总线释放回高。用查询方式等待总线变高可以准确判断转换是否结束。如果你觉得750毫秒太长可以在0x44命令之后直接延时800毫秒效果相同但代码耗时固定。4.2 写时序的参数细节写1和写0的区别在于低电平的持续时间。写0时隙需要拉低60到120微秒写1时隙只需要拉低1到15微秒然后释放。void ds18b20_write_byte(unsigned char dat) { unsigned char i; for(i 0; i 8; i) { DQ 0; _nop_(); if(dat 0x01) // 当前位为1 DQ 1; // 短拉低后释放 delay_us(60); // 保持到时隙结束 DQ 1; // 释放总线 dat 1; // 准备下一位 } }dat 0x01取出最低位然后dat 1让下一位在下次循环中处于最低位。这里必须注意先判断再移位如果把移位写在前面会丢失数据。实际调试中推荐用逻辑分析仪抓总线波形对比写0和写1的脉宽差异。4.3 温度值怎么从二进制变成人能读的数字读取到的16位数据低4位是小数部分高5位是符号位。例如读取到0x0191二进制是0000 0001 1001 0001去掉低4位和符号位得到温度整数部分为25小数部分为0.0625 * 1 0.0625实际温度约25.0625度。// 负数温度的处理方式 if(temp_raw 0x8000) // 符号位为1表示负温度 { temp_raw ~temp_raw 1; // 取补码得到绝对值 celsius temp_raw * 0.0625; celsius -celsius; // 加上负号 }DS18B20用二进制补码表示负温度。比如-0.5度读到的原始值是0xFFF8直接乘以0.0625会得到-0.5但在某些编译器上int类型右移符号位的处理不同所以显式做补码转换更稳妥。标准51的Keil C51编译器int是16位能覆盖DS18B20的-55到125度范围不需要用long。4.4 LCD1602显示部分如何和温度读取协同实例工程里LCD1602承担显示任务涉及写指令、写数据和忙检测三个封装函数。温度刷新频率不宜太高因为DS18B20转换一次就要750毫秒每200毫秒刷新一次LCD没意义。void main(void) { float temp; unsigned char temp_str[16]; lcd_init(); // LCD初始化 while(1) { temp ds18b20_get_temperature(); sprintf(temp_str, Temp: %.2f C, temp); lcd_clear(); lcd_write_string(0, 0, temp_str); } }%.2f格式输出温度到字符串再通过LCD写字符串函数显示。如果你的Keil版本提示float格式化需要printf库支持可以在工程设置里勾选Use MicroLIB或者改成整数定点表示比如把温度乘以100存成整数类型。5. Proteus仿真搭建与排错晶振频率、上拉电阻和时序校准5.1 最小硬件环境怎么搭打开Proteus新建工程放置AT89C51、DS18B20、LCD1602、10k电阻、4.7k上拉电阻、电容和晶振。DS18B20的数据引脚必须接一个4.7k上拉电阻到VCC否则空闲状态下总线电平不确定。晶振选择12MHz和代码里的delay_us延时计算保持一致。连接方式DS18B20的DQ脚接单片机P1.0或P3.7VCC和GND分别接电源和地。LCD1602的RS、RW、EN分别接P2.0、P2.1、P2.2数据D0到D7接P0口。P0口是开漏输出必须接上拉电阻才能驱动LCD数据线这是最常见的Proteus仿真不显示的坑。5.2 仿真常见报错和现象对照表现象可能原因排查方法LCD无显示背光亮P0口没接上拉电阻加10k排阻温度显示85.0度初始化失败读到上电默认值检查复位时序延时温度固定不变单总线被强制拉低检查DQ脚是否误配置为推挽仿真卡死while循环等待转换超时降低仿真速度或加超时退出温度跳动剧烈读时序采样点偏移用示波器抓波形校准延时温度显示85度是DS18B20的典型故障特征。传感器上电后暂存器里的默认值是0x0550对应85度。如果初始化时序不完整读取操作失败程序就会拿到这个默认值。遇到这种情况不用怀疑传感器坏了先查复位脉冲宽度和读时序的采样点位置。5.3 Keil仿真和硬件实测的时序校准方法在Keil中开启软件仿真设置正确的晶振频率后用逻辑分析仪窗口观察P1.0的电平翻转。把DS18B20的写入和读取函数都加断点逐条语句步进对比数据手册上的时序图。复位脉冲要求480到960微秒实测代码里delay_us(500)在优化等级为0时基本准确但如果打开了Optimize for Size编译器可能调整循环结构导致实际延时缩短。遇到这种情况可以用示波器或逻辑分析仪实测然后调整延时参数。5.4 负温度检测和分辨率配置的进阶技巧DS18B20默认12位分辨率0.0625度步进。通过写暂存器指令0x4E可以配置成9到12位9位分辨率转换时间只需93.75毫秒适合快速刷新场景。修改方法发送0x4E后依次写入报警上限TH、报警下限TL、配置寄存器。配置寄存器值为0x7F时对应12位0x5F对应11位0x3F对应10位0x1F对应9位。负温度的显示优化直接读取补码乘以0.0625再取负号会得到浮点数但LCD1602显示浮点需要消耗较多代码空间。工程实践中我常用定点数方案温度值乘以100存成int显示时分别取整数部分和小数部分。比如温度读到0xFFF8补码换算后是-50乘以100得到-5000显示-50.00。这样做既避免了浮点运算的代码开销也避免了sprintf格式化大字符串的问题。本文还有配套的精品资源点击获取
返回列表