ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

51单片机DS18B20温度传感器与数码管动态显示系统全解析

51单片机DS18B20温度传感器与数码管动态显示系统全解析 简介本资源是一套面向嵌入式初学者与单片机课程实践者的51单片机温度测量与显示完整工程聚焦DS18B20数字温度传感器驱动与共阳/共阴数码管动态扫描显示两大核心技能。项目覆盖单线通信协议实现、温度数据解析、BCD转换、定时器中断调度及人机界面刷新等典型开发环节适用于课程设计、实训项目或竞赛基础模块开发。压缩包共25个文件含5个头文件.h定义硬件接口与函数原型4个C/C源文件.c/.cpp实现传感器读取与显示逻辑2个说明文档.txt提供使用指引另有.hex烧录文件、.exe上位机测试程序及VC6工程配置文件.dsw/.dsp便于直接编译、下载与联调验证。资源包大小为6.19MB结构清晰、模块分离明确已有571人学习下载是掌握51单片机外设协同控制的高实用性入门范例。1. 项目概述一个经典的51单片机温度显示系统如果你手头有一块51单片机开发板一个DS18B20温度传感器再加上几个数码管想做一个实时显示温度的小玩意儿那你找对地方了。这个“51单片机DS18B20数码管显示程序.zip”项目几乎是每个单片机初学者都会经历的“必修课”。它麻雀虽小五脏俱全涵盖了单片机开发中最核心的几个环节传感器通信、数据处理和动态显示。别看它简单能把温度稳定、准确地从传感器“搬”到数码管上中间涉及的单总线协议、数码管动态扫描、数据处理算法每一个环节都藏着不少门道。我当年第一次做这个项目时传感器读出来的温度要么是85度默认值要么就乱跳数码管也闪烁得厉害。后来折腾了好几天才把时序、消影、滤波这些细节搞清楚。这个项目之所以经典就是因为它能让你把书本上的理论比如GPIO操作、定时器中断真正用到解决实际问题中。通过它你能深刻理解什么是“时序”什么是“实时性”以及如何让不同的硬件模块协同工作。接下来我就把这个项目的里里外外、从原理到代码、从连线到调试掰开揉碎了讲清楚让你不仅能复现更能理解背后的“为什么”。2. 核心硬件与通信协议深度解析2.1 主角一DS18B20温度传感器DS18B20是Dallas现Maxim公司的一款单总线数字温度传感器。它的“单总线”特性是核心意味着只需要一根数据线DQ即可完成与单片机的双向通信同时这根线还能为传感器提供寄生供电当然也支持外部供电。它的测温范围是-55°C到125°C在-10°C到85°C范围内精度为±0.5°C对于大多数日常应用绰绰有余。单总线协议的精髓在于严格的时序。所有通信都由单片机主机发起通过控制DQ线的高低电平持续时间来发送逻辑“1”或“0”传感器从机则在特定的时间窗口内采样或回应。协议主要包括初始化复位脉冲存在脉冲、写时序和读时序。注意DS18B20对时序要求极其苛刻尤其是在不同的单片机主频下。12MHz晶振的51单片机和11.0592MHz的其指令周期微秒数有差异直接照抄延时函数可能会导致通信失败。必须根据你的实际主频精确计算并调整延时。内部存储与分辨率DS18B20内部有一个9字节的暂存器。我们最关心的是前两个字节它们组成了16位的温度寄存器。这个寄存器值是以补码形式存储的。例如读取到的16进制数据为0x0191二进制0000 0001 1001 0001。其高5位是符号位如果为0表示正温度为1表示负温度补码形式。剩下的11位是数值位。DS18B20默认分辨率是12位即0.0625°C/LSB。所以将读取到的16位整数数据转换为实际温度的公式是温度 读取值 × 0.0625。对于0x0191计算过程是(0x0191 401) * 0.0625 25.0625°C。2.2 主角二数码管及其驱动原理数码管本质上是由多个发光二极管LED封装在一起的显示器件。常见的有1位、2位、4位等。每个数码管由7个段a, b, c, d, e, f, g和1个小数点dp组成这就是“七段数码管”。共阴与共阳这是数码管最重要的分类。共阴数码管是所有LED的阴极连接在一起作为公共端COM阳极独立。要点亮某一段需要给该段对应的引脚高电平公共端接低电平。共阳则相反所有阳极连在一起接VCC阴极独立要点亮某段需给该段引脚低电平。在编程前必须用万用表蜂鸣档或查看数据手册确认你手头数码管的类型这决定了你段码表的数据和驱动逻辑。动态扫描显示当我们有多个数码管比如4位一体时如果为每个数码管单独分配驱动引脚会占用大量IO口。动态扫描解决了这个问题。其原理是利用人眼的视觉暂留效应约24ms快速轮流点亮每一个数码管。在同一时刻实际上只有一个数码管被点亮但由于切换速度非常快通常1-5ms切换一位人眼看到的就是所有位同时稳定显示。实现动态扫描需要两组信号段选和位选。段选控制当前要点亮哪些段即显示什么数字所有数码管的段选线是并联的。位选控制当前哪个或哪几个数码管被选中可以显示。通常通过一个三极管如PNP型8550用于共阳NPN型8050用于共阴来控制公共端的通断。例如要显示“25.1”流程是段选送出“2”的编码位选打开第一位数码管延时2ms关闭所有位选段选送出“5”的编码位选打开第二位数码管延时2ms以此类推。只要整个循环时间小于20ms显示就没有闪烁感。2.3 51单片机作为控制核心在这个项目中51单片机通常是STC89C52或其兼容型号扮演着大脑的角色。它需要完成以下几项核心任务严格按照单总线协议与DS18B20通信通过一个GPIO口如P3.7模拟出初始化、写1/0、读1/0的精确时序。温度数据处理将读取到的原始16位数据转换为实际的温度值浮点数或整数小数部分并进行必要的滤波处理如中值滤波、均值滤波以消除跳动。数码管动态扫描驱动利用定时器中断产生固定的时间基准如2ms在中断服务程序中依次刷新每一位数码管实现稳定无闪烁的显示。主程序调度协调以上任务例如每隔1秒读取一次温度并更新显示缓冲区。3. 系统整体设计与软件架构3.1 硬件连接图与引脚分配一个典型的4位数码管显示温度的连接方案如下以共阳数码管为例DS18B20:VDD: 接电源3.3V或5V建议5V时串联一个4.7K电阻。DQ: 接单片机P3.7同时通过一个4.7K的上拉电阻接到VCC。这个上拉电阻至关重要它为单总线在空闲时提供了确定的高电平。GND: 接地。4位共阳数码管:段选线 (a, b, c, d, e, f, g, dp): 分别连接到单片机P0口的8个引脚P0.0-P0.7。注意P0口内部无上拉电阻作为输出时需要外接上拉电阻排通常用1K或10K排阻否则无法输出高电平。位选线 (COM1, COM2, COM3, COM4): 分别通过一个1K的限流电阻连接到四个PNP三极管如8550的发射极。三极管的集电极接VCC基极通过一个1K电阻分别连接到单片机P2口的低四位P2.0-P2.3。当P2.x输出低电平时三极管导通对应的数码管公共端获得高电平VCC该位数码管被选中。实操心得很多开发板已经集成了数码管驱动电路。你需要做的是查看原理图确认你的板子上数码管是共阴还是共阳段选和位选分别接到了单片机的哪个端口。盲目接线是失败的第一步。3.2 软件流程与模块化设计清晰的软件架构是项目稳定的基础。建议将程序模块化主程序 (main.c)负责初始化定时器、中断、变量然后进入一个无限循环。循环内可以以较低频率如每秒一次调用温度读取函数并将处理好的温度值填入显示缓冲区。DS18B20驱动模块 (ds18b20.c/.h)DS18B20_Init(): 初始化函数发送复位脉冲检测存在脉冲。DS18B20_WriteByte(): 向传感器写入一个字节命令或数据。DS18B20_ReadByte(): 从传感器读取一个字节。DS18B20_GetTemperature(): 完整的温度读取流程。包括初始化 - 发送跳过ROM命令0xCC因为总线上通常只有一个传感器- 发送启动温度转换命令0x44- 等待转换完成对于12位分辨率最大需750ms可采用查询或延时- 再次初始化 - 发送跳过ROM命令 - 发送读取暂存器命令0xBE- 连续读取9个字节我们只关心前两个- 将两个字节组合成16位整数 - 调用温度转换函数。DS18B20_TempConvert(): 将16位整数转换为实际温度值。这里有一个技巧为了在整数运算能力有限的51单片机上处理小数常将温度值放大10倍或100倍用整数表示。例如25.0625°C可以表示为250表示25.0°C或2506表示25.06°C。数码管显示模块 (seg.c/.h)SEG_CodeTable[]: 数码管段码表0-9可能还有负号、字母等。共阳和共阳的段码是相反的。例如数字‘0’的段码a-g顺序共阴是0x3F共阳则是0xC0。Display_Buffer[4]: 显示缓冲区数组。每一位对应一个数码管要显示的数字0-1515可用于表示熄灭或自定义字符。Timer0_Init(): 初始化定时器0设置为2ms中断用于动态扫描。Timer0_ISR(): 定时器0中断服务程序。在此函数中实现位选切换和段选数据送出。维护一个位选索引变量每次中断指向下一位循环往复。数据处理模块 (data.c/.h)Temp_Filter(): 温度滤波函数。最简单的可以是连续读取3-5次去掉最大最小值后取平均能有效消除偶尔的跳变。TempToDisplay(): 将放大后的温度整数如250表示25.0°C分解为个位、十位、小数位等并填入Display_Buffer。例如250分解为百位2显示‘2’十位5显示‘5’个位0显示‘0’同时需要决定小数点的位置。4. 核心代码实现与难点剖析4.1 DS18B20单总线时序的精确模拟这是整个项目的第一个难点。51单片机通常用C语言编写但精确的微秒级延时必须用内联汇编或基于循环的_nop_()空操作来实现。Keil C中提供了intrins.h头文件里面有_nop_()函数。下面以12MHz晶振为例一个机器周期1us给出关键时序的代码框架// 延时约5微秒 void Delay5us() { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } // 延时约60微秒 void Delay60us() { unsigned char i 30; while (--i); } // DS18B20初始化复位 bit DS18B20_Init() { bit ack; DQ 1; Delay5us(); DQ 0; // 主机拉低总线至少480us Delay500us(); // 自定义的约500us延时函数 DQ 1; // 主机释放总线上拉电阻拉高 Delay60us(); // 等待15-60us后DS18B20会拉低总线60-240us作为应答 ack DQ; // 读取存在脉冲低电平有效 Delay500us(); // 等待存在脉冲结束 return ~ack; // 如果ack为0表示存在返回1 } // 向DS18B20写一位 void DS18B20_WriteBit(bit b) { DQ 0; // 主机拉低总线开始写时序 Delay5us(); // 拉低时间大于1us DQ b; // 在15us内将目标电平送到总线 Delay60us(); // 保持电平整个写时序至少60us DQ 1; // 释放总线为下一次通信做准备 // 两次写操作之间需要至少1us的恢复时间 } // 从DS18B20读一位 bit DS18B20_ReadBit() { bit b; DQ 0; // 主机拉低总线至少1us Delay5us(); DQ 1; // 主机释放总线 Delay5us(); // 等待约15us让DS18B20的数据稳定到总线上 b DQ; // 采样总线电平 Delay60us(); // 读完位后等待至少45us满足整个读时序60us以上 return b; }为什么时序这么严格因为DS18B20作为从设备它只在主机产生特定时序后的一个很窄的时间窗口内输出数据。如果主机采样太早或太晚都会读到错误的值。例如读时序主机拉低总线1us后必须释放然后在15us内采样这个时间点必须卡准。4.2 数码管动态扫描与消影处理动态扫描的代码相对直观但有一个常见问题鬼影/重影。现象是当前位显示的数字会微弱地出现在其他不显示的位上。产生原因当段选数据改变而位选信号还没来得及改变时错误的段码会短暂地出现在上一个或下一个数码管上。例如从显示第一位“2”切换到第二位“5”时流程是关闭第一位位选 - 段选数据从“2”的编码改为“5”的编码 - 打开第二位位选。如果在“关闭位选”和“改变段选”之间没有延时或者IO口电平变化需要时间那么在关闭第一位但段选还是“2”的瞬间第二位可能已经被短暂选中显示出微弱的“2”。解决方案消影。在切换位选前先将段选数据设置为“全灭”对于共阳数码管段码为0xFF共阴则为0x00。代码示例如下// 在定时器中断服务程序中 void Timer0_ISR() interrupt 1 { // 1. 先关闭所有位选消影 WEI1 1; WEI2 1; WEI3 1; WEI4 1; // 假设高电平关闭三极管共阳 // 或使用位操作关闭所有位如 P2 | 0x0F; // 2. 送出当前位要显示的段码 P0 SEG_CodeTable[Display_Buffer[seg_index]]; // 3. 打开当前位的位选 switch(seg_index) { case 0: WEI1 0; break; // 选中第一位 case 1: WEI2 0; break; case 2: WEI3 0; break; case 3: WEI4 0; break; } // 4. 指向下一位准备下次中断显示 seg_index; if(seg_index 4) seg_index 0; }4.3 温度数据的处理与显示优化直接从DS18B20读出的温度值需要转换和格式化。假设我们采用温度值放大100倍用整数表示即分辨率0.01°C。int Get_TemperatureValue() { // 返回放大100倍的温度值如2501代表25.01°C unsigned int temp; unsigned char low_byte, high_byte; int temperature; // ... 执行DS18B20读取流程获得low_byte和high_byte ... temp high_byte; temp 8; temp | low_byte; // 判断正负温度 if((temp 0xF800) 0xF800) { // 高5位为1负数 temp ~temp 1; // 取补码得到原码绝对值 temperature -(int)(temp * 0.0625 * 100); // 计算并放大100倍加负号 } else { // 正数 temperature (int)(temp * 0.0625 * 100); } return temperature; } void Update_DisplayBuffer(int temp_val) { // temp_val是放大100倍的值 unsigned char is_negative 0; unsigned int abs_temp; if(temp_val 0) { is_negative 1; abs_temp -temp_val; } else { abs_temp temp_val; } // 分解例如 abs_temp 2501 - 25.01 Display_Buffer[0] (abs_temp / 1000) % 10; // 十位2 Display_Buffer[1] (abs_temp / 100) % 10; // 个位5 Display_Buffer[2] (abs_temp / 10) % 10; // 十分位0 Display_Buffer[3] abs_temp % 10; // 百分位1 // 处理负号和小数点 if(is_negative) { // 在最高位显示负号需要自定义段码例如0xBF共阳时‘-’的编码 Display_Buffer[0] 16; // 假设段码表第16项是‘-’ } // 我们通常只在第二位和第三位之间显示小数点显示25.1或25.01 // 这需要在段码送出时将小数点段dp的位也考虑进去。 // 一种常见做法是在段码表中每个数字准备两份编码一份带小数点一份不带。 // 或者在送段码时根据当前位索引决定是否与上0x7F共阳点亮dp或或上0x80共阴。 }5. 系统调试与常见问题排查实录5.1 DS18B20通信失败排查这是新手遇到最多的问题。现象通常是读回的温度一直是85°C上电默认值或0xFF。检查硬件连接上拉电阻DQ线必须接一个4.7KΩ的上拉电阻到VCC没有它总线无法被拉高。电源确保VDD和GND连接正确。如果使用寄生供电在强干扰环境下可能不稳定建议改为外部供电VDD接3.3V/5V。线长单总线对布线敏感线太长或干扰大会导致通信失败。尽量使用短导线。检查时序延时晶振频率确认你的单片机晶振是12MHz还是11.0592MHz。不同的频率一个_nop_()和循环延时的实际时间不同。如果程序是从12MHz抄的而你的板子是11.0592MHz延时可能就不准了。用示波器或逻辑分析仪这是最直接的调试方法。观察DQ线上的波形看复位脉冲的低电平时间是否大于480usDS18B20回应的存在脉冲低电平是否在60-240us之间。写/读时序的各个时间点是否符合数据手册要求。检查代码逻辑初始化返回值确保DS18B20_Init()函数正确检测到了存在脉冲。如果一直返回0说明根本没检测到传感器。命令顺序完整的读取流程是初始化 - 写0xCC跳过ROM- 写0x44开始转换- 延时等待转换 - 再次初始化 - 写0xCC - 写0xBE读暂存器- 连续读9字节。顺序不能错特别是每次发送命令前是否需要重新初始化要查数据手册。对于单个传感器常用跳过ROM命令简化流程。5.2 数码管显示异常排查全不亮或部分不亮共阴/共阳搞错这是最可能的原因。检查你的段码表是针对共阴还是共阳编写的。用万用表二极管档测量确认数码管类型。驱动能力不足51单片机IO口输出电流有限通常10-20mA。如果直接驱动多个LED段如显示数字‘8’电流可能不够导致亮度暗或不亮。务必使用三极管或专用驱动芯片如74HC573锁存器、ULN2003达林顿管来增强驱动能力。限流电阻段选线上必须串联限流电阻通常100-1KΩ否则会烧毁LED或单片机IO口。显示乱码或数字错误段码表错误对照数码管引脚图检查你的段码表a-g, dp顺序是否正确。不同厂家封装的引脚顺序可能不同。硬件连接与代码不匹配检查代码中P0口段选的位定义是否与实际硬件连接一一对应。例如代码认为P0.0控制a段但实际电路接的是b段。显示闪烁或有重影扫描间隔过长如果定时器中断间隔设得太长比如20ms才切换一位那么每位点亮时间间隔太长人眼就能察觉到闪烁。建议每位显示时间1-5ms4位数码管整个扫描周期控制在4-20ms以内。未消影严格按照“先关位选 - 送段码 - 开位选”的顺序并在关位选后、送新段码前加入极短的延时或直接送灭码消影码。5.3 温度值跳动大或不准确软件滤波DS18B20本身有一定噪声。在Get_TemperatureValue()函数中不要只读一次就相信它。可以连续读取5次去掉一个最大值和一个最小值然后对剩下的3次取平均值。这会显著稳定显示值。电源噪声如果单片机系统和传感器共用同一个存在噪声的电源比如有电机、继电器等大电流设备频繁开关会影响传感器精度。可以尝试给DS18B20的VDD加一个0.1uF的瓷片电容进行退耦。传感器接触不良或损坏焊接时温度过高、时间过长可能损坏传感器。确保焊接良好没有虚焊。6. 项目优化与功能扩展思路当基础功能稳定实现后你可以考虑以下优化和扩展让这个小项目更具挑战性和实用性增加负温度显示完善代码中对负温度补码的处理和显示逻辑让系统能显示零下的温度。实现温度报警设置一个高温和低温阈值当温度超过范围时让一个LED闪烁或蜂鸣器鸣叫。这涉及到比较判断和外部中断或查询。添加按键设置功能通过两个按键加、减来设置报警阈值并将阈值保存到单片机的EEPROM中如STC单片机内部的EEPROM掉电不丢失。这需要学习按键消抖、菜单状态机和EEPROM读写。改用LCD1602液晶显示用字符液晶替代数码管可以显示“Temp: 25.06 C”这样的字符串更直观。你需要学习LCD1602的4位或8位并行接口驱动。通过串口上传数据将温度数据通过单片机的串口UART发送到电脑用串口助手查看或者发送给Wi-Fi模块如ESP8266上传到物联网平台。这为远程监控打下了基础。低功耗设计如果用于电池供电可以让DS18B20完成温度转换后进入休眠模式单片机也进入空闲模式或掉电模式定时唤醒读取温度以极大降低系统功耗。这个项目就像一把钥匙帮你打开了单片机世界的大门。把每一个细节都琢磨透遇到的每一个问题都彻底解决你的收获将远不止一个温度显示器。从时序模拟到中断调度从硬件调试到软件滤波这些经验会沉淀为你解决更复杂嵌入式问题的底层能力。我个人的体会是嵌入式开发没有捷径就是一个个引脚、一行行代码、一次次调试积累起来的。当你看到数码管稳定地显示出准确的室温时那种成就感就是最好的回报。本文还有配套的精品资源点击获取
返回列表