H40 基于 51 单片机做微波炉控制器:DS18B20 测温、LCD1602 显示带正负号与两位小数,四个按键设定 0~10 分钟时长和 20/30/50℃ 三档温度,启动后必须到温才开始累计时间,到点自动停机闪灯响铃,附 Keil 程序、Proteus 仿真与设计报告。

01 设计任务
功能介绍:
使用51单片机设计一个微波炉控制器,DS18B20 测温 + LCD1602 显示 + 四键操作,要求如下:
1、温度实时显示:LCD 第二行显示当前温度,带正负号和两位小数(如 +25.00℃),后面跟已运行时间 0min;
2、设定时间:按"设置时间"键循环加,0~10 分钟循环,第一行显示 S1: 1min;
3、设定温度:按"设置温度"键在 20℃ / 30℃ / 50℃ 三档之间循环,显示 S2:20℃;
4、启动与完成:按"启动"键开始,绿灯亮;只有实际温度达到设定温度后,时间才开始累计;到达设定时长自动停机,红灯闪烁 + 蜂鸣器响,按"清零或停止"解除;
5、温度滤波:连续三次采样值相同时才采用,读数不跳。
特别注意:这份程序有一个别处都没出现过的逻辑——"到温才开始计时"。看这句:
每一拍先拿当前温度和设定温度相比,达标后才去比对已运行时长,时长够了就把完成标志置位、同时清掉启动标志;温度没达标,已运行时长立刻清零,等下一拍重新判断。
加热设备从冷启动到设定温度需要时间(预热),这段预热时间不能算进烹饪时长——否则你设 5 分钟,实际只有 3 分钟在真正加热。所以程序在"没到温"的时候把 Run_time 反复清零,直到温度上去才开始数。这就是工业控制里说的"条件满足才计时",也是这一篇最值得学的一句逻辑。



02 硬件:四键 + 双灯 + 一测温
整机结构比前几篇清楚得多,器件功能一目了然:
| 器件 | 引脚 | 作用 |
| DS18B20(DQ) | P2.5 | 测温,4.7K 上拉到 +5V |
| LCD1602 | 数据 P0(配 RP 10K 排阻)、RS/RW/E 接 P2 口 | 显示温度、时间、设定值 |
| K1 设置时间 | P1.0 | 循环加 0~10 分钟 |
| K2 设置温度 | P1.1 | 20/30/50℃ 三档循环 |
| K3 启动 | P1.2 | 开始工作 |
| K4 清零或停止 | P1.3 | 停机、解除报警 |
| 绿灯 LED_GREEN | P1.7 | 运行指示(运行中亮) |
| 红灯 LED_RED | P2.7 | 报警指示(完成时闪烁) |
| 蜂鸣器 | P2.6 | 经 PNP 三极管驱动,低电平响 |
两个硬件细节:
一是蜂鸣器和红灯都是"低有效"。BUZZ=0 才响,LED_RED=~LED_RED 取反闪烁——51 的 IO 默认上电是高电平,直连 PNP 基极时"高=关",所以程序里初始 BUZZ=1 就是关。这类极性判断不用记,看原理图上管子类型就行:PNP 基极低电平导通,NPN 基极高电平导通。
二是 P0 口必须上拉。 LCD 数据口接 P0,配上 10K 排阻——P0 是开漏结构,不接上拉输出不了高电平,屏幕会一片空白。这个坑在 H33、H39 都提过,只要是用 P0 驱动外设,先看有没有上拉。
03 三档温度、十一档时间:设定怎么循环
微波炉的设定交互讲究"简单"——不用数字键盘,按一下就切下一档,闭着眼睛也能操作。程序用了两个循环计数器:
时间:0~10 分钟循环
按下设置时间键先把设定分钟加一,加过 10 就回到 0;随后按当前是几位数分别刷新显示。
这里有个很实在的界面处理:个位数时先写一个空格再写数字,两位数时直接从第 3 列写。因为 LCD_Print_num 会消前导零,10 只占 2 个字符、5 只占 1 个字符——如果不补空格,从 1 切到 1 0 时会残留一个字符(10 显示成 100,或者 1min 和 10min 混着显示)。
定长显示 + 补空格,是字符型 LCD 刷新的通用解法(H39 用的是补零,这里是补空格,思路一样)。
温度:三档循环
每按一次,索引值加一,超过 2 就归零,三个档位循环往复。
用一个 0/1/2 的索引去映射三档温度,而不是直接对温度值做加减——好处是档位之间跳跃不需要考虑步长(20→30 是 +10,30→50 是 +20)。想让档位变成 20/40/60?只改映射表就行,索引逻辑不动。这就是"值表 + 索引"的写法,和 H35 的五个设定项、H28 的段码表是同一个套路。
实际微波炉会有更多档(中低火/中火/中高火/高火),扩展时把索引上限和映射分支加长即可。
04 温度采集:三次相同才采用
温度采集放在 T1 中断里,大约 60ms 一次。采到之后不是直接用,而是先过一道"三取同"的滤波器:
中断里用一个长度三的缓冲加一个下标轮着存整数温度,攒满三拍才开始比较;比对通过就把这个值写进当前温度、置起刷新标志,然后下标归零、重新攒。
连续三个采样值完全相等,才承认这个温度。这跟 H38 的"连续 4 次不同才更新"正好相反方向——一个防"抖动",一个防"跳变",本质都是要求数据稳定才让它影响显示和控制。
为什么这里必须稳?因为 Real_Tempurature 是要参与开关判断的:如果温度在 29℃/30℃ 之间来回跳,而设定值是 30℃,程序会不停地"到温→不到温→清零 Run_time→重新计时",运行时间永远攒不起来,微波炉永远不结束。用滤波把临界抖动镇住,控制逻辑才不会被噪声带跑。
05 显示与比较分离:ASCII 缓冲 vs 整数变量
这份程序有个很聪明的设计:温度同时存在两份。
· TempBuffer1[]:ASCII 字符缓冲,给 LCD 用,格式是 符号 + 百位 + 十位 + 个位 + . + 十分位 + 百分位 + ℃,比如 +25.00℃;
· Real_Tempurature:整数变量,给比较用,只取整数部分(25)。
Covert_tempurature() 先把 DS18B20 的原始值翻译成 ASCII 填进缓冲,然后再从 ASCII 反算出整数给比较用:
转换时先看百位是不是被消隐标记占着:是就只取十位和个位凑成两位数,否则把百位、十位、个位一起取,逐位换算成数字拼成一个整数存进采样缓冲。
减 0x30 就是 ASCII 转数字(「5」-「0」=5),这套动作在 H31 的数码管显示里反向用过(数字 + 0x30 变 ASCII)。
为什么不一开始就算整数? 因为显示需要两位小数、需要正负号、需要百位消隐,这套格式化逻辑跟"整数比较"完全是两回事。让显示归显示、判断归判断,中间用一份 ASCII 缓冲做媒介——代码是长了一点,但改显示格式不会影响控制逻辑,改比较精度也不会碰坏显示。
顺便说个技巧:百位消隐用 0xfe 当标记。if(TempBuffer1[1]==0x30) TempBuffer1[1]=0xfe;——把不该显示的"0"改成一个 LCD 上不可见的字符,后面判断"是不是三位数"就靠 ==0xfe。用一个魔法值当"空位标记",是嵌入式里省标志变量的常用做法(H37 用下标 16 当负号、H38 用 17 当熄灭,都是一个思路)。不过 0xfe 在标准 1602 字库里并不是"空白",某些屏上会显示成一个怪字符——想保险就用 0x20(空格)。
06 双定时器与"只刷变化的部分"
TMOD = 0x11 把两个定时器都设成方式 1(16 位),各管一摊:
· T0:计时。50ms 中断,20 次 = 1 秒,60 次 = 1 分钟,Run_time++;
· T1:测温。约 60ms 一次,读 DS18B20 → 转换 → 滤波。
T0 每进一次中断,先判断启动标志,运行中才往下累加秒计数,满 20 次让运行分钟数加一;没启动就把秒计数清零,免得下次启动时带着上一轮的残留。
没启动时清 temp_time,不光是省电——如果不清楚,下次启动时残留的秒计数会让第一次"1 分钟"变得不准(H27 抢答器的软件时钟也踩过这个点)。
LCD 刷新则做了增量更新,只刷变化的部分:
主循环拿当前运行分钟和一个影子变量比,只有不相等才更新影子变量并把新分钟数写到第 10 列;温度那边等滤波标志置起,清掉标志后才把整行缓冲刷上屏。
为什么不能每轮都刷? 1602 一次写字符要好几十微秒,整屏重写几十毫秒——主循环里每轮都刷,按键响应会变迟钝,屏幕还会轻微闪。"数据没变就不刷"是做 LCD 界面的第一性能纪律,比什么优化都管用。
同时这也解释了 Lvbo_flag 的另一个作用:它让"刷新"这件事跟着"数据可信"走——滤波没通过的中间值,根本不会上屏。
07 排错清单
1、LCD 全黑/全白无字符:先查 P0 排阻上拉有没有接(P0 开漏),再查 1602 的 VEE 对比度电位器——两个都不接就是白屏;
2、温度一直显示 +125.00℃:TempBuffer1 的初值就是这样(源码里写死了一串占位字符),如果一直不刷新,说明 T1 中断没进或者 DS18B20 没应答。查 DQ(P2.5)接线和 4.7K 上拉;
3、按启动后时间不走:正常现象——没到设定温度前时间是被反复清零的(见 01 节)。想看时间走,把设定温度调到低于当前温度(比如 20℃)立刻就能验证;
4、时间到不停机/一直闪灯:Finsh_flag 置位后程序仍在循环闪灯响铃,必须按"清零或停止"才解除——这是设计行为,不是死机;
5、温度跳变导致时间反复清零:Real_Tempurature 在设定值附近抖动。检查温度滤波是否生效(三次相等),或者把设定温度调离当前温度几度再试;
6、设定时间显示错位(1min 变成 10min 残留):个位数补空格的逻辑没了,见 03 节;
7、源码几处注释与实际不符,看代码别看注释:① uchar TH=110,TL=-20; 注释写"上限-22,下限-24",实际是 110℃ / -20℃(-20 存进 uchar 是补码 0xEC);② Time_Init() 里 T0 装的是 10ms、T1 装的是 50ms,而两个中断里重装的却是 50ms 和 60ms——初值与重装值不一致,第一次中断的时间会偏(H36 也有同款问题,改法:把初值和重装值统一);③ TempBuffer1 初值注释写"+125.00C",实际字符串是 +122.20℃。
08 总结
这份程序最值钱的不是显示,也不是测温——那些前面几篇都练过了——而是"到温才计时"这一行逻辑。它代表了一类工业控制思路:动作的触发条件不止一个(既要温度到、又要时间到),而且条件之间还有先后(先升温、后计时)。
整段逻辑可以压成一条链:以「启动」为起点、以「执行完成动作」为终点,中间反复判断条件 A,不满足就一直等着、等待期间顺便把 B 清零,一旦满足才开始累计 B,B 到目标值就执行完成动作。
微波炉、烤箱、恒温箱、电热水器、充电器的"恒流→恒压"切换,全是这个结构。以后遇到"两个条件都要满足才结束"的题,先想想是不是"到 A 才开始数 B"。
下一步该进通讯了——串口怎么让两台单片机说话,H41、H42 见。
欢迎关注,持续更新!
09 文件日期修改
如果你想把文件修改为最新的日期,又不想新建工程,那么你可以参考下面的文章。
"如何不打开程序的情况下更改proteus和keil程序的日期?"
工具和仿真文件在一起,见下面的下载链接。
10 资料下载
全套资料包含 Keil 工程(C 语言源码 + hex 文件)、Proteus 仿真工程、设计报告、程序流程图、讲解视频,以及编译截图和仿真截图。下载后可直接打开运行,也可用于课程设计、毕业设计参考。
也可以关注公众号「单片机仿真」,回复 H40 获取下载入口。公众号内还有 A / T / H 全系列 400 多个单片机仿真实例,回复「目录」可查看完整清单。

