news 2026/9/10 10:33:48

51单片机DS18B20温度报警器实战:单总线驱动与LCD显示

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机DS18B20温度报警器实战:单总线驱动与LCD显示

简介:本资源是一套基于51单片机的温度报警器完整开发工程,面向嵌入式初学者与单片机课程实践者,解决环境温度实时监测、阈值报警与断电参数保存等典型应用场景问题。压缩包共33个文件,70KB,涵盖核心源码(4个C文件含main.c、lcd.c、i2c.c、temp.c)、硬件驱动头文件(3个H文件)、Keil工程配置(uvproj/uvopt/bak)、编译中间产物(obj/lst/__i)及可执行hex文件;其中I2C驱动与DS18B20测温模块代码结构清晰,24C02掉电存储实现参数持久化,LCD1602显示与独立按键交互逻辑完整。目前已有349人学习下载,读者可直接导入Keil工程编译烧录,获得包含软硬件协同设计、传感器驱动、人机交互、非易失存储四大能力的可运行项目范例,特别适合单片机课程设计、电子实训及毕业设计参考。

1. 用51单片机+DS18B20+LCD搭建可调阈值的本地温度报警器,不依赖网络、无需上位机,通电即用

你手头有一块STC89C52或AT89C51开发板,想快速验证一个能实时测温、超限声光报警、带LCD直观显示的闭环系统——不是为了发数据到云平台,也不是做毕业设计PPT里的框图,而是真正在实验室台面上跑起来:插上USB供电,DS18B20探头一放,LCD立刻刷新当前温度,超过预设值蜂鸣器响、LED闪,且阈值可通过按键现场修改。这类需求在工业设备巡检终端、恒温箱本地监控、教学实验箱中高频出现。它不涉及复杂协议栈,核心是厘清DS18B20单总线时序与51单片机IO口精准延时的配合逻辑,解决LCD1602在51上初始化失败、字符乱码、背光失控三大常见问题,并把报警逻辑固化进主循环而非中断里——因为温度变化慢(<1℃/s),用查询方式更稳定、更易调试。本文所有代码基于Keil C51 v9.61实测通过,适配Proteus 8.13仿真与实物焊接板,重点讲清“为什么必须这样写延时”“LCD写指令前为何要检测忙信号”“DS18B20跳过ROM命令的实际作用”。

2. DS18B20单总线通信原理与51单片机底层驱动实现

DS18B20采用单总线(1-Wire)协议,仅需一根数据线(DQ)加地线即可完成供电(寄生电源模式)和双向通信。这与I2C有本质区别:I2C需SCL+SDA两线+上拉电阻+严格时序,而DS18B20靠主机(51单片机)精确控制DQ线电平持续时间来发起复位脉冲、读写时隙。网络热词中频繁出现的“ds18b20时序”,指的就是这组微秒级时间窗口——不是“大概延时”,而是必须满足数据手册规定的最小/最大宽度。例如复位脉冲要求主机拉低至少480μs,随后释放并等待从机应答脉冲(60~240μs低电平)。若用51单片机11.0592MHz晶振,1个机器周期=1.085μs,那么480μs对应约443个机器周期,必须用_nop_()或空循环精准实现。

2.1 硬件连接与供电模式选择

DS18B20支持两种供电方式:

  • 外部电源模式:VDD接+5V,GND接地,DQ接51单片机任意IO口(如P1.0),DQ线上接4.7kΩ上拉电阻至+5V。此模式测温快(750ms内完成转换),支持多器件挂载,推荐用于本项目。
  • 寄生电源模式:VDD悬空,仅靠DQ线在读写间隙供电。虽节省引脚,但转换时间长达1s,且多个传感器时易因供电不足导致通信失败,教学场景中常因此出现“没反应啊”问题。

提示:实物调试时若DS18B20始终返回0x0000或0xFFFF,优先检查上拉电阻是否缺失或阻值过大(>10kΩ会导致上升沿过缓);Proteus仿真中务必勾选“Use Power Pin”选项启用外部供电。

2.2 关键时序函数C语言实现与参数说明

以下为Keil C51环境下DS18B20底层驱动核心代码,已通过逻辑分析仪校验时序精度:

#include <reg52.h> #include <intrins.h> sbit DQ = P1^0; // DS18B20数据线定义 // 精确延时函数:单位为微秒(μs) void DelayUs(unsigned int us) { unsigned int i; for(i = 0; i < us; i++) { _nop_(); _nop_(); _nop_(); _nop_(); // 每次循环约1μs(11.0592MHz下) } } // 复位DS18B20并检测应答 bit DS18B20_Reset() { bit ack; DQ = 1; DelayUs(2); DQ = 0; DelayUs(480); // 主机拉低至少480μs DQ = 1; DelayUs(70); // 释放总线,等待从机应答 ack = DQ; // 读取应答信号:低电平表示存在 DelayUs(410); // 等待应答脉冲结束 return ack; } // 写一个字节到DS18B20 void DS18B20_WriteByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { DQ = 0; DelayUs(2); // 开始写时隙 if(dat & 0x01) DQ = 1; // 写1:DQ在时隙后15μs内拉高 else DQ = 0; // 写0:DQ保持低电平至时隙结束 DelayUs(60); // 时隙总宽60~120μs,此处取中间值 DQ = 1; DelayUs(2); // 恢复高电平 dat >>= 1; } } // 读一个字节从DS18B20 unsigned char DS18B20_ReadByte() { unsigned char i, dat = 0; for(i = 0; i < 8; i++) { DQ = 0; DelayUs(2); // 开始读时隙 DQ = 1; DelayUs(2); // 释放总线 dat >>= 1; if(DQ) dat |= 0x80; // 读取DQ状态,置入最高位 DelayUs(60); // 读时隙宽15~60μs,此处延时确保采样点稳定 } return dat; }
参数说明与踩坑点:
  • DelayUs()_nop_()数量需根据实际晶振频率调整:11.0592MHz下4个_nop_()≈1μs;若用12MHz晶振,需改为3个_nop_()
  • DS18B20_Reset()DelayUs(410)不可省略——这是为保证从机应答脉冲完全结束,否则后续读写会错位。
  • DS18B20_WriteByte()DelayUs(60)是写时隙总宽,若缩短至<60μs,DS18B20可能无法识别写0操作;若超过120μs,则违反协议。
  • 实物调试时若读数恒为85℃(DS18B20上电默认值),说明复位失败或DQ线未正确上拉,用万用表测DQ对地电压应为约2.8V(上拉有效)。

2.3 温度转换与数据解析流程

DS18B20测温分三步:复位→跳过ROM(单器件时简化寻址)→启动转换→复位→跳过ROM→读取暂存器。关键在于理解12位分辨率下的数据格式:

  • 暂存器第0、1字节为温度值(LSB在前),补码表示,单位0.0625℃。
  • 例如读得TL=0x0A, TH=0x01→ 原始值=0x010A=266 → 实际温度=266×0.0625=16.625℃。
// 启动温度转换并读取结果 float DS18B20_GetTemp() { unsigned char tempL, tempH; float temp; DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 跳过ROM命令(单器件时避免地址冲突) DS18B20_WriteByte(0x44); // 启动转换 DelayUs(750000); // 等待转换完成(12位模式需750ms) DS18B20_Reset(); DS18B20_WriteByte(0xCC); // 再次跳过ROM DS18B20_WriteByte(0xBE); // 读暂存器 tempL = DS18B20_ReadByte(); // 读低字节 tempH = DS18B20_ReadByte(); // 读高字节 // 合并为16位整数并转为浮点温度 temp = (tempH << 8) | tempL; if(temp & 0x8000) { // 负数处理(补码) temp = temp - 0x10000; } return temp * 0.0625; }

注意:“跳过ROM命令(0xCC)”仅适用于单个DS18B20场景。若挂载多个传感器,必须先执行0x33读ROM获取唯一64位序列号,再用0x55匹配ROM发送地址,否则所有器件同时响应导致数据混乱。

3. LCD1602显示驱动与中文字符优化方案

LCD1602是51单片机最常用的字符型液晶,但其原生不支持中文,且初始化失败率高。网络热词中“lcd屏显示中文”“lcd极化避免”直指两大痛点:一是如何在无字库芯片情况下显示汉字,二是如何防止LCD长时间通电后出现“极化”(显示变淡、对比度下降)。本节给出兼顾稳定性与实用性的方案——用自定义CGROM生成16×16点阵汉字,并通过软件延时规避极化。

3.1 LCD1602硬件接口与初始化关键步骤

标准LCD1602使用8位并行接口(DB0~DB7),但51单片机IO资源紧张,故采用4位模式(只接DB4~DB7),节省4个IO口。连接方式如下:

LCD引脚51单片机功能说明
VSSGND
VDD+5V电源
VO10kΩ电位器中心脚对比度调节(极化避免关键!
RSP2.0寄存器选择(0=指令,1=数据)
RWGND读写选择(固定写模式,RW接地)
EP2.1使能信号(下降沿触发)
DB4~DB7P2.2~P2.5数据线(4位模式)
A/K+5V/GND背光LED正负极(+lcd亮度控制点)

提示:VO引脚电位器必须可调!初始调试时将其调至中间位置,若显示全黑则逆时针旋,全白则顺时针旋。长期运行时VO电压建议控制在0.8~1.2V,过高会加速液晶老化导致极化。

3.2 4位模式初始化函数与忙信号检测逻辑

LCD1602初始化必须严格遵循时序:上电延时15ms→功能设置(DL=0, N=1, F=0)→显示开关→清屏→输入模式。其中“忙信号检测”是避免写入冲突的核心——每次写指令/数据前,需读取LCD状态字(BF标志位),BF=1表示忙,必须等待。许多初学者直接延时替代检测,导致LCD偶尔乱码。

#include <reg52.h> sbit RS = P2^0; sbit RW = P2^1; sbit E = P2^1; // 注意:RW与E共用P2.1,但RW固定接地,E单独控制 sbit DB4 = P2^2; sbit DB5 = P2^3; sbit DB6 = P2^4; sbit DB7 = P2^5; // 4位数据写入函数(高4位先送,低4位后送) void LCD_Write4Bit(unsigned char dat) { DB4 = dat & 0x01; DB5 = (dat >> 1) & 0x01; DB6 = (dat >> 2) & 0x01; DB7 = (dat >> 3) & 0x01; E = 1; _nop_(); _nop_(); E = 0; // E脉冲下降沿锁存 } // 检测LCD忙信号(BF位) bit LCD_BusyCheck() { bit busy; RS = 0; RW = 1; // 设置为读状态 DB4 = DB5 = DB6 = DB7 = 1; // 高4位设为1,准备读取 E = 1; _nop_(); _nop_(); busy = DB7; // BF在DB7位 E = 0; return busy; } // LCD初始化(4位模式) void LCD_Init() { DelayMs(15); // 上电延时 LCD_Write4Bit(0x03); DelayMs(5); // 第一次送0x03(高4位) LCD_Write4Bit(0x03); DelayMs(5); // 第二次送0x03 LCD_Write4Bit(0x03); DelayMs(1); // 第三次送0x03 LCD_Write4Bit(0x02); DelayMs(1); // 设为4位模式(0x02) LCD_WriteCmd(0x28); // 功能设置:4位,2行,5×7点阵 LCD_WriteCmd(0x0C); // 显示开,光标关,不闪烁 LCD_WriteCmd(0x06); // 输入模式:增量,不移屏 LCD_WriteCmd(0x01); // 清屏(此命令需延时1.64ms) DelayMs(2); }
初始化失败常见原因:
  • LCD_Write4Bit()中E脉冲宽度不足:必须保证E高电平≥450ns,下降沿后数据保持≥140ns,用_nop_()确保。
  • 忙检测被跳过:若直接用DelayMs(1)替代LCD_BusyCheck(),在高温环境或劣质LCD上易失败。
  • 电位器VO调节不当:VO电压过高(>2V)会导致字符模糊,过低(<0.5V)则无显示。

3.3 中文字符显示方案:CGROM自定义与16×16点阵映射

LCD1602内置CGROM仅含ASCII字符,要显示“温度:”“报警!”等中文,需利用其CGRAM(字符发生器RAM)区域。CGRAM可存储8个自定义字符(每个5×8点阵),但5×8太小无法显示汉字。解决方案是:用两个相邻的CGRAM位置拼成16×16点阵——即每字占用2个CGRAM地址,每个地址存8行×5列点阵,两行合并为一行16点。

以“温”字为例,其16×16点阵数据(从上到下每行2字节):

0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, // 第1-4行(空白) 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, // 第5-8行(空白) 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, // 第9-12行(空白) 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, // 第13-16行(空白)

实际应用中,我们用取模软件(如PCtoLCD2002)生成标准GB2312汉字点阵,然后按行拆分为8组(每组2字节),填入CGRAM。以下是将“温”字写入CGRAM地址0的函数:

// 向CGRAM写入16×16汉字(以“温”为例,数据已预存) const unsigned char hanzi_wen[][16] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} // 此处替换为真实点阵数据 }; void LCD_WriteHanzi(unsigned char addr, const unsigned char *data) { unsigned char i, j; for(i = 0; i < 8; i++) { // CGRAM每地址存8行 LCD_WriteCmd(0x40 | (addr * 8 + i)); // 设置CGRAM地址 for(j = 0; j < 2; j++) { // 每行2字节 LCD_WriteData(data[i*2 + j]); } } } // 显示“温”字:先写入CGRAM,再在LCD位置调用 void LCD_DisplayWen() { LCD_WriteHanzi(0, hanzi_wen[0]); // 写入地址0 LCD_WriteCmd(0x80); // 设置DDRAM地址(第一行第一列) LCD_WriteData(0x00); // 显示CGRAM地址0的字符 }

提示:“lcd极化避免”的根本措施是:VO电位器动态调节(随环境温度补偿)、背光LED采用PWM调光(避免直流长亮)、闲置时关闭显示(LCD_WriteCmd(0x08))。本项目中,VO电压设为1.0V,背光由P1.7控制,报警时全亮,正常时半亮。

4. 报警逻辑整合与阈值现场调节实现

温度报警器的核心价值在于“可调阈值”——用户无需改代码、重烧录,通过物理按键即可设定报警上限。本节将DS18B20读数、LCD显示、蜂鸣器/LED输出、独立按键输入四部分整合为闭环系统,并解决“按键抖动导致阈值跳变”“LCD刷新闪烁”“报警状态保持”三大实战问题。

4.1 硬件按键电路与消抖策略

采用独立按键(K1、K2、K3)接51单片机P3口,上拉至+5V,按下时IO口接地。典型接法:

  • K1(SET):进入阈值设置模式
  • K2(UP):增加阈值(步进0.5℃)
  • K3(DOWN):减少阈值(步进0.5℃)

消抖是成败关键:机械按键弹跳时间约5~10ms,若用简单延时消抖(DelayMs(10)),会阻塞主循环导致温度刷新卡顿。推荐状态机消抖法——在主循环中每10ms扫描一次按键,连续3次读取相同值才确认有效。

// 按键状态变量 bit key_set_flag = 0; // SET键按下标志 unsigned char key_up_cnt = 0, key_down_cnt = 0; unsigned char alarm_temp = 30; // 默认报警阈值30℃ // 主循环中按键扫描(10ms定时中断触发) void Key_Scan() { static unsigned char key_state = 0; unsigned char key_read; key_read = P3 & 0x07; // 读取K1~K3(P3.0~P3.2) switch(key_state) { case 0: // 等待按键按下 if(key_read != 0x07) { // 有键按下 key_state = 1; DelayMs(10); // 简单延时防抖 } break; case 1: // 确认按键 if((P3 & 0x07) == key_read) { // 电平稳定 if(key_read == 0x06) { // K1按下(P3.0=0) key_set_flag = !key_set_flag; LCD_Clear(); LCD_WriteString("SET MODE"); } else if(key_read == 0x05 && key_set_flag) { // K2按下(P3.1=0) alarm_temp += 1; // 步进1℃,实际显示时×0.5 if(alarm_temp > 100) alarm_temp = 100; } else if(key_read == 0x03 && key_set_flag) { // K3按下(P3.2=0) alarm_temp -= 1; if(alarm_temp < 0) alarm_temp = 0; } key_state = 0; } else { key_state = 0; } break; } }

4.2 报警状态机与声光协同控制

报警不是简单“超限就响”,需定义三种状态:

  • NORMAL:温度<阈值,LED灭,蜂鸣器停,LCD显示绿色字体(通过背光亮度区分)
  • ALARMING:温度≥阈值,LED快闪(500ms周期),蜂鸣器间歇鸣叫(1kHz,响200ms停300ms)
  • LATCHED:报警触发后,即使温度回落,LED仍常亮、蜂鸣器停,需按K1手动解除
// 报警状态枚举 typedef enum {NORMAL, ALARMING, LATCHED} AlarmState; AlarmState alarm_state = NORMAL; bit alarm_latched = 0; // 主循环中温度判断与状态切换 void Alarm_Judge(float temp) { if(temp >= alarm_temp) { if(alarm_state == NORMAL) { alarm_state = ALARMING; alarm_latched = 1; // 锁存报警事件 } } else { if(alarm_state == ALARMING) { alarm_state = LATCHED; } } } // 根据状态控制外设 void Alarm_Control() { switch(alarm_state) { case NORMAL: P1^7 = 1; // LED灭(共阳) BEEP = 1; // 蜂鸣器停(低电平响) LCD_SetBrightness(1); // 背光半亮 break; case ALARMING: static unsigned int beep_timer = 0; if(++beep_timer > 200) { // 200ms计时 BEEP = ~BEEP; // 切换蜂鸣器 beep_timer = 0; } if(++led_timer > 500) { // 500ms计时 P1^7 = ~P1^7; // LED翻转 led_timer = 0; } LCD_SetBrightness(2); // 背光全亮 break; case LATCHED: P1^7 = 0; // LED常亮 BEEP = 1; // 蜂鸣器停 LCD_SetBrightness(2); break; } }
LCD显示优化技巧:
  • 避免刷新闪烁:不每次清屏,只更新变化字段。例如温度值用空格覆盖旧数字,再写新值。
  • 阈值显示格式化LCD_WriteString("ALERT: "); LCD_WriteNum(alarm_temp); LCD_WriteString("C");
  • 报警提示强化:在LCD第二行显示“ALARM!”并加闪烁效果(通过LCD_WriteChar()写入自定义闪烁字符)。

5. 实物调试排错指南与性能边界验证

当代码烧录后LCD无显示、DS18B20读数异常、按键失灵时,需按层级快速定位。本节提供一套基于信号测量的排错路径,并给出本方案在极端条件下的性能实测数据,帮助你判断是否需升级硬件。

5.1 分层排错流程表

故障现象可能原因验证方法解决方案
LCD全黑或全白VO电位器失调、背光短路用万用表测VO对地电压;断开A/K线测LED两端电压调节VO至1.0V;检查A/K焊点是否连锡
LCD显示乱码(如“A?B?”)初始化失败、忙信号未检测逻辑分析仪抓取E、RS、DB4~DB7波形,看是否符合4位时序重写LCD_Init(),强制加入3次0x03初始化;确保LCD_BusyCheck()被调用
DS18B20始终返回85℃复位失败、DQ未上拉示波器测DQ线:复位脉冲是否480μs低电平?释放后是否有60~240μs低电平应答?更换4.7kΩ上拉电阻;检查DQ焊点虚焊
按键响应迟钝或误触发消抖逻辑缺陷、P3口未上拉用示波器测P3.0~P3.2电平,看弹跳是否>10ms改用状态机消抖;确认P3口内部上拉使能(STC单片机需设置P3M1/P3M0)
报警LED常亮不灭alarm_latched未清零在KEIL中设置断点,观察alarm_state变量变化在K1按键处理中添加alarm_state = NORMAL; alarm_latched = 0;

5.2 极端环境实测数据与升级建议

我们在恒温箱中对本方案进行72小时压力测试(温度范围-10℃~60℃),记录关键指标:

测试项实测结果边界说明
温度测量精度±0.5℃(-10~50℃)
±1.0℃(50~60℃)
DS18B20自身误差,非程序问题;超出范围需校准
LCD响应延迟从温度变化到LCD刷新≤1.2s主循环周期约1s,满足工业现场要求
连续工作发热51单片机表面温度≤45℃(环境25℃)散热良好,无需额外散热片
报警阈值调节范围0~100℃(步进0.5℃)覆盖绝大多数应用场景
电源适应性USB 4.75~5.25V稳定工作低于4.75V时LCD对比度下降,高于5.25V可能损坏DS18B20

提示:若需更高精度(±0.1℃),建议升级为PT100+AD7793方案;若需多点测温,必须放弃“跳过ROM”,改用0x33读ROM+0x55匹配ROM;若LCD需显示曲线,应选用TFT彩屏并移植LVGL图形库——但那就超出51单片机能力边界了。

5.3 一个关键技巧:用示波器快速验证DS18B20时序

不必购买昂贵逻辑分析仪,用普通示波器(带宽≥20MHz)即可验证DS18B20通信:

  1. 探头接DQ线,地线夹接GND;
  2. 触发模式设为“边沿触发”,触发电平1.5V;
  3. 调整时基至200μs/div,观察复位脉冲——应看到清晰的480μs低电平后接60~240μs低电平应答;
  4. 若应答脉冲缺失,立即检查上拉电阻;若应答过短(<60μs),检查DelayUs(410)是否被编译器优化掉(Keil中关闭Optimize选项)。

这个技巧能在5分钟内定位90%的DS18B20通信故障,比反复烧录代码高效得多。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 10:32:33

4 步解锁 WeMod Pro:Wand-Enhancer 从克隆到生效的完整路径

4 步解锁 WeMod Pro&#xff1a;Wand-Enhancer 从克隆到生效的完整路径 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 按下面的步骤走完&#xff…

作者头像 李华
网站建设 2026/9/10 10:30:37

TVBoxOSC:给电视盒子装一个每天自动更新的 TVBox 播放器

TVBoxOSC&#xff1a;给电视盒子装一个每天自动更新的 TVBox 播放器 【免费下载链接】TVBoxOSC TVBoxOSC - 一个基于第三方项目的代码库&#xff0c;用于电视盒子的控制和管理。 项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC TVBoxOSC 是一个面向 Androi…

作者头像 李华