基于AT89C52与DS18B20的数字温度计报警显示设计

发布时间:2026/11/2 13:51:37
基于AT89C52与DS18B20的数字温度计报警显示设计

简介:这份毕业论文文档围绕基于单片机的数字温度计设计展开,面向电子、自动化及相关专业需要完成课程设计或毕业设计的学生,提供从选题背景、系统方案到软硬件实现的参考。内容以DS18B20智能温度传感器和AT89C52单片机为核心,涵盖温度测量与报警电路、LED显示模块、0℃~+100℃测量范围及报警上下限设置,并介绍Proteus与Keil开发工具、传感器原理和单片机功能应用。资源包共1个doc文件,约301KB,结构上包含引言、开发工具、系统概述及软硬件电路等章节,便于按论文框架查阅与借鉴。目前已有157人学习下载,适合需要搭建数字温度计方案、梳理论文目录和补充电路设计思路的读者参考。

1. 从一份“看着能跑”的温度计方案说起

做基于单片机的数字温度计,最容易翻车的不是代码写不出来,而是动手之前没把三件事钉死:量程、分辨率、报警方式。这套方案的指标是 0℃~100℃、误差小于 1℃、四位数码管实时显示、上下限可按键设定、超限声光报警,主控用 AT89C52,测温元件用单总线数字温度传感器 DS18B20。它适合正在做单片机课程设计、电子类毕设的同学,也适合需要给小型设备配一个低成本测温报警模块的从业者。整套东西器件少、电路简单,但时序和显示两处特别吃细节,下面按需求、选型、架构、代码、避坑、验收一路拆开讲。

2. 技术选型:为什么放弃热电偶加 ADC,改用一根单总线

2.1 三条测温路线的取舍

传统测温绕不开两件事:把物理量变成电信号,再把电信号变成数字量。热电偶输出的是毫伏级电压,还得做冷端补偿;热电阻(比如铂电阻)要配恒流源,把阻值变化转成电压变化。这两条路都要经过放大、滤波、A/D 转换,最后才轮到单片机算温度。对课程设计和中小批量产品来说,前端电路的器件数量、调试工作量、一致性,全都是成本。

DS18B20 把这一整条链路塞进了一个三脚封装里:敏感元件、A/D、数字接口、寄存器全在内部,单片机读到的直接就是温度值,省掉外部 ADC,也省掉标定环节。

方案前端电路是否需 ADC典型精度器件数量适用场景
热电偶冷端补偿 + 高增益放大 + 滤波需要取决于补偿电路多高温、宽量程工业场合
热电阻 PT100恒流源 + 仪表放大 + 滤波需要高,但需标定多中高精度工业测温
DS18B20一只 4.7k 上拉电阻不需要±0.5℃(-10℃~+85℃)少0℃~100℃ 常规测温

这张表就是选型的依据。量程要求只有 0℃~100℃、误差允许 1℃,DS18B20 的手册指标在这个区间里绰绰有余,把省下来的电路复杂度换成调试时间,是这笔账里最划算的部分。

2.2 DS18B20 的四个参数怎么落到设计指标上

选型不能只看“能用”,要看参数如何映射到验收指标。

量程 -55℃~+125℃ 覆盖了 0℃~100℃ 的要求,还留出余量;分辨率最高 0.0625℃,对应 12 位转换结果,但要注意分辨率不等于精度,显示到 0.1℃ 是合理的,往上再堆小数位没有意义;转换时间与分辨率绑定,9 位约 93.75ms,12 位最长 750ms,这决定了主循环怎么排时间片;单总线接口用一根数据线完成收发,代价是严格的时隙要求,微秒级的拉低、释放、采样窗口都得卡准。

提示:分辨率配置存在暂存器的第 9 字节,上电默认就是 12 位。如果不改配置,读回来的就是 0.0625℃ 的原始值,换算时按 1/16 处理即可。

2.3 主控与开发工具的选择

AT89C52 是 51 内核里最常见的一颗,40 引脚、4 组 8 位并行口、128 字节片内 RAM、两个 16 位定时器、一个全双工串口、5 个中断源,跑这套逻辑完全够。它在这类题目里有一个别的好处:串口可以工作在方式 0,也就是同步移位寄存器模式,RXD 出数据、TXD 出时钟,一个字节直接推进 74LS164,用来驱动数码管能省下大量 I/O。

开发环境是 Keil C51 负责编译调试,Proteus 负责搭虚拟电路跑仿真。两者联调的好处是:不用焊板子就能先把时序和显示逻辑跑通,坏处是仿真对时序过于宽容,很多在实物上必炸的写法在仿真里能跑得漂漂亮亮,这一点在第 5 章会专门说。

3. 总体架构与硬件电路:五个模块、一根单总线和四位数码管的资源账

3.1 模块划分与引脚分配

系统按职责拆成五块:主控最小系统、温度采集、温度显示、按键设定、声光报警。数据流很单向——传感器采集、单片机处理、显示与报警输出,没有回环,调试时可以从任意一段单独验证。

引脚分配是硬件设计的第一个决策点,分配得好,后面写代码不用来回改。

引脚功能连接对象说明
P1.0单总线 DQDS18B20 数据脚外接 4.7k 上拉至 VCC
P3.0 / P3.1RXD / TXD74LS164 数据 / 时钟串口方式 0 输出段码
P3.2按键 K1设置 / 确认低电平有效
P3.3按键 K2上限加 / 下限减低电平有效
P3.4按键 K3切换上下限低电平有效
P2.0报警输出蜂鸣器驱动三极管低电平鸣叫
P2.1报警指示LED高电平点亮
XTAL1 / XTAL2时钟12MHz 晶振 + 两只 30pF 电容决定时序基准
RST复位10uF 电容 + 10k 电阻上电复位高电平有效

分配原则很简单:时序敏感的信号(单总线)独占一位,别和按键共用;显示走串口移位,把并行口留给按键和报警;蜂鸣器、LED 这类负载挂在 P2,避免和数码管段码抢驱动能力。

3.2 最小系统:晶振、复位与 EA 引脚

最小系统三件事必须一次做对。晶振取 12MHz,配两只 30pF 瓷片电容接地,走线尽量短,晶振外壳接地更稳;复位电路用 10uF 电解电容串 10k 电阻接到 RST,上电瞬间电容充电形成足够宽的高电平,配合单片机内部复位逻辑完成初始化;EA 引脚接高电平,让 CPU 从片内程序存储器取指令。

这里有一处极易踩的坑:AT89C52 的片内程序存储器是 8KB,而同系列的 89C51 只有 4KB,两者的头文件和烧写配置不要混用。工程里如果照着别人的 89C51 模板建,程序一旦超过 4KB,编译能过、烧写报错或者运行跑飞,排查方向会完全跑偏。

3.3 DS18B20 接口电路:三线制、4.7k 上拉与寄生供电的取舍

DS18B20 有两种供电方式。三线制是 VDD 接电源、GND 接地、DQ 接单片机,数据线经 4.7k 电阻上拉到 VCC;寄生供电是 VDD 直接接地,靠数据线在总线高电平期间“偷电”存进内部电容。

课程设计和大多数产品场景,建议直接上三线制。原因很实际:寄生供电在启动温度转换和写EEPROM期间需要总线强上拉,得额外加 MOSFET 或者用单片机引脚强行拉高,一旦强上拉时间不够,转换结果就是错的,而且这种错不是每次都出现,属于最难查的那一类。三线制多一根线,换来的是行为可预测。

数据线的 4.7k 上拉电阻不能省,也不能随便换大。阻值太大,上升沿变缓,读时隙采到的是低电平;阻值太小,器件拉低时灌电流过大。4.7k 在 3.3V 和 5V 系统里都是常规取值。

3.4 显示与按键报警电路:74LS164 驱动四位数码管

显示部分是这套方案里最省 I/O 的设计。四位数码管用两片 74LS164 级联,串行输入并行输出,段码从单片机的串口方式 0 一位一位推过去,只占用 RXD、TXD 两根线。串口方式 0 的波特率固定为振荡频率的 1/12,12MHz 下就是 1MHz,推一个字节几十微秒,刷新四位毫无压力。

数码管选共阴还是共阳,决定了段码表和 74LS164 输出电平的对应关系。共阴数码管的段脚是高电平点亮,段码表就按位直接写 1;共阳要取反。这一步选错,现象是所有位显示同一团乱码,看起来像程序错,实际是硬件极性反了。

按键三只,分别负责进入设置、调整数值、切换上下限对象,全部低电平有效,靠 P3 口内部上拉。蜂鸣器用 NPN 三极管驱动,基极串 1k 电阻接 P2.0,单片机输出低电平时导通鸣叫;报警指示灯直接串 470Ω 限流电阻接 P2.1。这两个负载不要直接挂在段码线上,否则鸣叫瞬间的电流突变会把显示拉花。

4. 软件实现:从复位时序到报警比较的四个关键函数

4.1 主循环怎么排:非阻塞状态机比死等更靠谱

新手最常写的结构是“启动转换 → 延时 750ms → 读结果”,逻辑没错,但 750ms 里显示停更、按键无响应,四位数码管会明显闪一下,按键经常按不动。正确做法是把一次采温拆成三个状态,用 1ms 的定时器节拍推着走。

#include <reg52.h> #include <intrins.h> #define ST_IDLE 0 #define ST_CONVERT 1 #define ST_READ 2 unsigned char temp_state = ST_IDLE; unsigned int conv_tick = 0; unsigned char tl, th; int t10; /* 当前温度,单位 0.1℃ */ /* 每 1ms 由定时器中断置位,主循环里调用一次 */ void temp_task(void) { switch (temp_state) { case ST_IDLE: if (ds18b20_reset()) { /* 有应答才启动转换 */ ds18b20_write(0xCC); /* 跳过 ROM,单器件场合够用 */ ds18b20_write(0x44); /* 启动一次温度转换 */ conv_tick = 0; temp_state = ST_CONVERT; } break; case ST_CONVERT: if (++conv_tick >= 800) { /* 12 位最长 750ms,留 50ms 余量 */ temp_state = ST_READ; } break; case ST_READ: if (ds18b20_reset()) { ds18b20_write(0xCC); ds18b20_write(0xBE); /* 读暂存器,先低字节后高字节 */ tl = ds18b20_read(); th = ds18b20_read(); t10 = raw_to_t10(((int)th << 8) | tl); temp_state = ST_IDLE; } break; } }

这段的价值在调度方式:转换的 750ms 不占 CPU,主循环该刷显示刷显示、该扫按键扫按键。conv_tick >= 800里的 800 是经验值,手册给的 750ms 是上限,实际器件在室温下通常更快,但余量必须留,卡着 750 走会在低温段偶发读到上一次的结果。

4.2 三段时序:复位、写字节、读字节

单总线的所有通信都建立在三种时隙上,任何一个拉偏几十微秒,整条链路就哑了。12MHz 晶振下,一个机器周期 1us,用_nop_()凑短延时最可控。

sbit DQ = P1^0; /* 数据线,外接 4.7k 上拉 */ void delay_us(unsigned char n) { while (n--) _nop_(); /* 粗略微秒延时,按实测微调 */ } /* 复位时隙:拉低 >480us,释放后 15~60us 内采样存在脉冲 */ bit ds18b20_reset(void) { bit presence; DQ = 0; delay_us(60); DQ = 1; delay_us(8); presence = DQ; /* 0 表示器件应答 */ delay_us(50); /* 补足整个复位时隙 */ return presence; } /* 写时隙:拉低 1~15us 后送出本位,整段不少于 60us */ void ds18b20_write(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { DQ = 0; _nop_(); _nop_(); DQ = dat & 0x01; /* 写 1 时尽早释放总线 */ delay_us(7); DQ = 1; dat >>= 1; /* 低位先出 */ } } /* 读时隙:拉低 >1us 后释放,15us 内完成采样 */ unsigned char ds18b20_read(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { dat >>= 1; DQ = 0; _nop_(); _nop_(); DQ = 1; _nop_(); _nop_(); if (DQ) dat |= 0x80; delay_us(6); } return dat; }

三个函数的关键点分别是:复位后采样窗口只有 15~60us,采早了器件还没拉低,采晚了脉冲已经过去;写 1 和写 0 的区别在于拉低持续时间,不是电平高低;读操作由主机拉低发起,器件在释放后立刻输出数据位,所以释放之后要马上采样,中间插任何耗时操作都会读到错位的数据。

注意:如果主循环里有中断服务程序,读写时序期间应当关中断或保证中断执行时间远小于时隙宽度,否则一个长中断就能把一次通信打散。

4.3 温度换算:用定点数绕开浮点

12 位模式下,原始值的最低位代表 0.0625℃,也就是 1/16。51 内核做浮点运算又慢又占空间,全部用整数搞定。

/* 原始 16 位值转 0.1℃ 定点数,正负温度都覆盖 */ int raw_to_t10(int raw) { long v; if (raw < 0) { v = -((long)(~raw + 1) * 10 / 16); /* 负温度先取补码再换算 */ } else { v = (long)raw * 10 / 16; /* 1/16℃ = 0.0625℃ */ } return (int)v; }

负温度的处理是这段的唯一难点。DS18B20 返回的是 16 位补码,最高位为 1 时表示零下,不能直接乘除,要先~raw + 1取绝对值再换算,最后补上负号。*10/16的顺序不能颠倒,先乘后除是为了保住精度,反过来会先把 0.0625 截成 0。返回值单位是 0.1℃,显示时整数部分除 10、小数部分模 10,正好对上一次。

4.4 显示驱动、报警比较与按键设定

显示走串口方式 0,一个字节进 74LS164,四位连续推完,段码表按共阴编码。

unsigned char code SEG[] = {0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F}; unsigned char buf[4]; /* 显示缓冲,buf[0] 为最高位 */ void send_seg(unsigned char seg) { SBUF = seg; while (!TI); TI = 0; } /* 每 5ms 调一次,刷完四位;第三位后点亮小数点,形如 25.5 */ void display_refresh(void) { unsigned char i, seg; for (i = 0; i < 4; i++) { seg = SEG[buf[i]]; if (i == 2) seg |= 0x80; send_seg(seg); } }

报警比较放在采温完成之后,逻辑是“到限即报”,用>=和<=而不是>和<,避免温度正好卡在设定值时报警不动作。回差也要留,否则温度在阈值附近抖动时蜂鸣器会响个不停,通常加 0.2℃ 的回差就能压住。

void check_alarm(void) { if (t10 >= t_high || t10 <= t_low) { BUZZER = 0; /* 低电平驱动三极管导通 */ ALARM_LED = 1; } else { BUZZER = 1; ALARM_LED = 0; } }

按键部分只做三件事:20ms 消抖、短按切换设置对象、长按加减数值。上下限用两个int变量存,初值给 5.0℃ 和 60.0℃,设定范围强制夹在 0~100℃ 之间,超出就钳位,避免用户把上限设到下限以下导致逻辑全乱。

5. 避坑与排查:从读到 85℃ 到显示乱码的五类翻车现场

5.1 数据类异常:85℃、全 0、全 0xFF 怎么定位

现象一:上电第一次读到的温度永远是 85℃。原因是 DS18B20 的暂存器上电默认值就是 85℃,第一次转换还没完成就去读了。解决办法是初始化后先启动一次转换、等够时间再读,读完把第一个值丢弃。

现象二:读出来恒为 0。八成是复位函数没有检测到存在脉冲,主机以为器件在,实际总线没人应答。用示波器看 DQ 在复位时隙后有没有被拉低 60~240us,没有就是接线、上拉或者器件坏了。

现象三:读出来恒为 0xFF 或 0x7FFF。这类值说明数据线一直是被上拉的状态,从机根本没驱动总线。先查 GND 是否共地,再查上拉电阻是不是漏焊,最后查写时隙里DQ = dat & 0x01这一句有没有被优化掉。

现象四:温度跳变剧烈、偶尔出现 0.0625 的整数倍异常值。这是典型的时序被打断,中断里跑了长任务,或者主循环里有延时被临时加大。做法是读写期间关中断,或者把时序函数的延时用示波器逐段校准一次。

5.2 显示与按键环节:乱码、闪烁、按不动

数码管整屏乱码,先分清是“全亮同一段”还是“随机跳”。全亮同一段基本是共阴共阳选反,或者段码表整体取反;随机跳则是 74LS164 的时钟和数据接反,段码被当成时钟推了进去。这类问题在仿真里往往显示正常,因为 Proteus 对移位寄存器的容错比实物高,血泪经验是:仿真通过不代表接线对,实物必须重新核一遍引脚。

显示闪烁,多半是刷新周期太长,超过 20ms 人眼就能察觉;也可能是刷新被 750ms 的转换延时阻塞,参考 4.1 的状态机改法。

按键按不动,除了消抖参数给得太狠,还有一个隐蔽原因:按键扫描放在主循环里,而主循环里有个几百毫秒的阻塞,按键刚好落在盲区。把扫描周期固定到 10ms 一次,配合 20ms 消抖窗口,基本就稳了。

5.3 文档与仿真类问题:前后不一致最要命

有一类坑不在电路也不在代码,在文档本身。设计要求里写“LED 模块显示”,正文的显示电路又出现了 1602 字符液晶的引脚说明,两套方案在论文里并存,答辩时被问到“你到底用的哪个”,很容易答不上来。通常的做法是:以实物为准,把另一种方案作为“可选扩展”单独说明,正文所有框图、程序清单、测试数据统一到同一个显示方案上,附录的程序清单必须和正文描述一致。

仿真和实物脱节是另一类。Proteus 里 DS18B20 的模型对时序容忍度高,4.7k 上拉漏了照样能读数;实物上则直接读不到。所以仿真阶段只验证逻辑和显示,时序类问题必须在实物上用电位器调出来的温度、用冰水混合物做对照,才算真过。

6. 验收与答辩:六项指标的实测方法与五个必答问题

验收不看程序有多少行,只看指标能不能复现。下面这张表是我一般会先填一遍的东西,填不满就说明方案还没收口。

测试项测试方法判定标准
量程冰水混合物取 0℃,沸水取 100℃0~100℃ 内均能正常显示,不溢出
误差与标准温度计同点比对,取 0℃、25℃、50℃、80℃ 四点各点偏差不超过 1℃
分辨率观察显示末位变化能稳定分辨 0.1℃
报警下限设定下限后降温触发到限即鸣叫并点亮指示灯,回差内不抖动
报警上限设定上限后升温触发同上,且两路互不干扰
掉电重启断电 10s 后重新上电显示正常,报警阈值回到默认值或正确恢复

测试技巧上,0℃ 用冰水混合物比用冰箱靠谱得多,冰水共存时温度稳定;100℃ 用沸水要注意当地气压,海拔高的地方沸点不到 100℃,误差会被误判成系统误差,可以在报告里注明测试环境。三点校准做下来,如果误差都在 1℃ 以内,量程和精度这两项就算立住了。

答辩的问题是可以预判的,通常就这几个方向:为什么不用热电偶或热电阻,回答落在“省掉 ADC 和标定环节,量程精度匹配”;单总线一根线怎么同时收发,回答讲清时隙分工和主机拉低发起;分辨率 0.0625℃ 为什么显示只到 0.1℃,回答区分分辨率和精度;如果读不到数据怎么排查,按电源、上拉、时序、器件四层往下说;误差来源有哪些,回答器件本身 ±0.5℃、参考温度计误差、测点位置差异三块。

第一次带这套方案上答辩,我被问到“程序里那个 750ms 的延时,显示为什么没卡住”,当场翻代码才发现自己写的是阻塞版本,只好承认是后来改的。从那以后我每次做带传感器采样的题目,都强制先把主循环的时间片画成表,再动键盘写第一行代码——顺序反了,后面全是返工。希望这套结构能帮你少走一遍。

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