
简介面向计算机应用技术嵌入式方向专业的毕业设计文档选题为基于51单片机的空气智能加湿器适合需要完成嵌入式课程设计、毕业设计选题与论文写作的学生参考。方案以AT89C51单片机为核心配合DS18B20温度传感器与1602LCD液晶显示屏实现加湿器自动开启关闭、防干烧、声光报警及室内温湿度显示等功能可作为软硬件联调与智能控制系统设计的实践蓝本。包内为1个doc文档压缩包约339KB篇幅集中、便于通读。文档按毕业设计规范组织含摘要、任务来源与意义、总体设计方案、硬件设计电路图、信号分析、功能描述、复位电路、液位定位与光电开关以及软件设计等章节能帮助读者快速搭建论文框架、理清模块划分与实现思路。目前已有538人学习下载。1. 干湿球测湿 AT89C51一个能跑通的低成本方案市售几十块的加湿器有个通病插上电就一直雾化水箱空了照样转房间湿度都到 70% 了还在喷。干烧和过度加湿这两件事恰好是低端机型最容易省掉的部分。这份毕业设计要做的就是给加湿器装一个脑子——用 AT89C51 读两路 DS18B20 的干湿球温度反推相对湿度配上水位检测做防干烧声光报警做提示1602 液晶实时显示温湿度。干湿球法几乎是单片机课程里成本最低的测湿方案两只数字温度传感器加一片 51BOM 不到二十块不需要额外的湿度芯片和标定设备。它适合正在做嵌入式方向毕业设计的人也适合想把 51 的 I/O 口操作、定时器、单总线微秒级时序一次性打通的人。下面从引脚分配开始把单总线时序、干湿球公式的定点实现、水位互锁逻辑一路写到烧录联调每一步都能直接抄进工程。2. 51 单片机硬件设计引脚先分配好再动手画图2.1 为什么还在用 AT89C51AT89C51 是 4KB Flash、128B RAM、32 根 I/O 的经典 8 位机MCS-51 指令集Keil C51 和汇编都能编。选它的理由很实际Proteus 元件库里模型齐全教学资料铺天盖地出了问题好查。但有两个要提前想清楚。一是容量。AT89C51 只有 4KB 程序空间把 1602 驱动、两路 DS18B20 时序、显示转换和主逻辑全塞进去很容易顶到 3KB 以上改一点就超。稳妥做法是直接上 AT89C528KB或 STC89C52RC引脚完全兼容程序不用改。现在市面上标 AT89C51 的片子翻新货不少STC89C52RC 反而更好买、还支持串口下载。二是执行速度。12MHz 晶振下机器周期 1μs一条MOV级指令占 1~2μs。DS18B20 的读时隙要求主机拉低 1μs 后 15μs 内完成采样靠的是 NOP 和短延时循环卡时间编译器优化一开就可能把时序打乱。所以时序子程序必须单独放一个文件关掉该文件的优化。2.2 一张引脚分配表管住整块板子硬件设计最怕画到一半改引脚改一处牵动一片。先把分配表定死写代码和画原理图都照它来。功能引脚方向说明干球 DS18B20P1.2双向4.7kΩ 上拉到 5V湿球 DS18B20P1.3双向独占一根 I/O避免 ROM 匹配1602 数据 D0~D7P0.0~P0.7输出P0 为开漏必须 10kΩ 排阻上拉1602 RS / RW / EP2.0 / P2.1 / P2.2输出E 下降沿锁存1602 背光P2.7输出经 S8050 驱动可调亮度加湿执行机构P1.0输出三极管驱动继电器0 断 1 通蜂鸣器P1.1输出无源型定时器输出 2kHz 方波运行指示灯P1.4输出加湿中点亮水位 低 / 中 / 高P3.2 / P3.3 / P3.4输入低电平有效按键或浮球开关对应到代码就是一组sbit定义别用宏定义数字引脚51 的位操作靠sbit才写得动。/* ---------- 引脚分配与原理图严格一致 ---------- */ sbit DQ_DRY P1^2; /* 干球 DS18B20 数据线需 4.7k 上拉 */ sbit DQ_WET P1^3; /* 湿球 DS18B20 数据线 */ sbit HUM_CTRL P1^0; /* 加湿执行机构1通0断 */ sbit BEEP P1^1; /* 无源蜂鸣器由定时器 0 翻转输出方波 */ sbit LED_RUN P1^4; /* 加湿进行中指示灯 */ sbit LCD_RS P2^0; /* 0指令1数据 */ sbit LCD_RW P2^1; /* 0写1读忙检测时用 */ sbit LCD_EN P2^2; /* 下降沿执行 */ sbit LCD_BLK P2^7; /* 背光开关1亮 */ sbit LV_LOW P3^2; /* 低水位0 表示缺水 */ sbit LV_MID P3^3; /* 中水位 */ sbit LV_HIGH P3^4; /* 高水位 */sbit是 Keil C51 的位寻址关键字只能用在可位寻址区P0~P3、20H~2FH 位区。P3.2~P3.4 是外部中断和定时器引脚这里当普通输入用没问题但要注意 P3 口内部有弱上拉接按键时按下接地即可不必外加上拉电阻。2.3 复位、晶振、P0 上拉三个最容易翻车的细节复位电路用 10μF 电解电容加 10kΩ 电阻构成 RC配上手动复位按键和一个 1N4148 反向泄放二极管。二极管的作用是掉电瞬间让电容快速放电否则电源跌落再上电时电容还存着电复位脉冲宽度不够单片机会跑飞。RC 常数别贪大10μF×10kΩ 已经给出几十毫秒的复位宽度电容上到 47μF 反而会导致上电后复位脚缓慢爬升出现上电不启动、按一下复位才行的经典故障。晶振选 12MHz配两只 30pF 瓷片电容尽量贴着晶振引脚焊接走线短而等长。如果后续打算接串口调试换 11.0592MHz 能让波特率误差归零代价是机器周期变成 1.085μs所有基于 1μs 假设的延时循环要重算。P0 口是开漏输出驱动 1602 的数据线时如果不接上拉写进去的1根本拉不高屏幕会显示乱码或者干脆空白。加一只 10kΩ 排阻拉到 5V问题立刻消失。这是新手最常踩、也最容易被误判成液晶坏了的一个坑。2.4 加湿器负载驱动从 LED 原型到继电器开发阶段用一只 LED 加限流电阻代替加湿器执行机构这是很合理的做法——省了接强电的风险仿真时也能直观看到 P1.0 的电平变化。但真机上板必须换成继电器或固态继电器。51 单个 I/O 口的灌电流典型值 20mA 上下继电器线圈 5V 要 70mA 左右直连必然拉垮口线甚至烧片。标准接法是 P1.0 经 1kΩ 基极限流电阻驱动 S8050继电器线圈并在一个 1N4148 续流二极管上方向与线圈反接吸收断电瞬间的反向电动势。想彻底隔离强电侧干扰就用 PC817 光耦替代三极管做前级。超声波雾化片本身工作频率在兆赫兹级51 直接驱动不现实。工程上更可行的做法是把加湿器整机当成一个负载用继电器控制它的市电通断防干烧逻辑作用在继电器之前。3. DS18B20 单总线时序与干湿球相对湿度计算3.1 单总线为什么难写微秒级窗口DS18B20 用一根线同时传数据和供电寄生电源模式代价是所有时序都由主机严格把控。它没有时钟线靠的是拉低多久、什么时候采样、什么时候释放来编码 0 和 1。51 在 12MHz 下指令周期 1μs容错窗口本来就只有十几微秒再叠上 C 编译器的优化很容易出现仿真能读、上板读 85℃的现象——85℃ 是 DS18B20 上电默认值意味着转换根本没完成或者时序没被识别。时序类型主机拉低采样/释放时机总时隙备注复位脉冲≥480μs释放后 15~60μs 内等存在脉冲—存在脉冲由从机拉低 60~240μs写 11~15μs释放后保持高≥60μs释放后 15~60μs 内采样写 060~120μs全程拉低≥60μs拉低时间要覆盖采样窗口读时隙≥1μs释放后 15μs 内采样≥60μs从机在窗口内驱动总线表里的总时隙 ≥60μs是硬约束相邻两个时隙之间至少留 1μs 恢复时间否则从机会丢掉后面几位。3.1.1 复位与存在脉冲复位脉冲的做法是主机拉低总线 480μs 以上再释放然后等大约 60μs 读总线电平。如果读到低电平说明从机响应了存在脉冲器件在线读不到就把存在标志位置位跳过后续操作。RESET: ; 复位 存在脉冲检测 SETB DQ_DRY NOP CLR DQ_DRY ; 主机拉低 MOV R3,#150 ; 150*4us 600us 480us CALL DELAY_BY_R3 SETB DQ_DRY ; 释放总线 MOV R3,#15 ; 约 60us 后开始检测 CALL DELAY_BY_R3 MOV R3,#60 ; 检测窗口约 240us CHK: JNB DQ_DRY,GET_ACK ; 读到低电平 - 器件存在 DJNZ R3,CHK SETB 70H ; 70H.0 1器件不存在 JMP RESET_END GET_ACK: CLR 70H ; 存在标志清零 MOV R3,#120 ; 等恢复时间 2*120us DJNZ R3,$ RESET_END: RETDELAY_BY_R3是按 R3 计数的空循环基准是 4μs 一跳。这里把存在标志放在位寻址区 70H 的第 0 位主程序直接JB 70H.0就能判断不用额外传参。注意DJNZ R3,$这种原地循环写法在 Keil 汇编器里合法在有些第三方汇编器上要展开成标号。3.1.2 写时隙和读时隙写一位的逻辑是先拉低启动时隙然后根据要写的位决定拉低多久——写 1 就很快释放写 0 就拉够 60μs 以上。WRITE: ; 写一个字节A 为待写数据 MOV R2,#8 ; 一次写 8 位 CLR C W_LP: SETB DQ_DRY NOP CLR DQ_DRY ; 启动写时隙 MOV R3,#4 ; 约 16us写 1 的拉低时长 CALL DELAY_BY_R3 RRC A ; 最低位先进 C MOV DQ_DRY,C ; 写 0 时这里把总线继续拉低 MOV R3,#12 ; 约 48us凑满 60us 时隙 CALL DELAY_BY_R3 SETB DQ_DRY ; 释放恢复 1us 以上 NOP DJNZ R2,W_LP RET读时隙正好相反主机拉低 1μs 以上启动然后立刻释放在 15μs 内采样。关键在于采样必须发生在释放之后、从机还没来得及松手之前。READ: ; 读一个字节结果放在 A MOV R2,#8 R_LP: SETB DQ_DRY NOP CLR DQ_DRY ; 拉低 1us 启动读时隙 NOP NOP SETB DQ_DRY ; 释放让从机驱动总线 NOP NOP MOV C,DQ_DRY ; 15us 内采样 RRC A MOV R3,#6 ; 补齐到 60us 时隙 CALL DELAY_BY_R3 DJNZ R2,R_LP RETWRITE和READ都依赖准确的软件延时因此这两个函数以及调用的DELAY_BY_R3建议放在单独的ds18b20.c里用#pragma OPTIMIZE(5,SPEED)之类的指令固定优化等级避免换编译器版本后时序漂移。一次完整读温的流程是复位 → 跳过 ROM0xCC→ 启动转换0x44→ 等转换完成 → 复位 → 跳过 ROM → 读暂存器0xBE→ 连读两个字节。跳过 ROM 只在一个总线挂一只器件时可用挂两只就必须走0x55加 64 位序列号的匹配流程毕业设计里更省事的做法是两个传感器各占一根 I/O时序代码直接复用。3.2 12 位原始值到 0.1℃定点换算DS18B20 默认 12 位分辨率读回的 16 位数据里低 4 位是小数每 LSB 等于 0.0625℃。温度寄存器最大正数是 125℃最高 5 位是符号位负温用补码表示。/* raw 为 DS18B20 读出的 16 位原始值返回 0.1℃ 单位的温度 */ int ds18b20_to_tenth(unsigned int raw) { long v (long)(int)raw; /* 转有符号补码直接可用 */ return (int)(v * 10 / 16); /* 0.0625℃/LSB - 0.1℃ */ }先转成有符号再运算是为了让 -10℃ 这类负温也能自动处理。v * 10 / 16用整数除法截断误差不超过 0.05℃对相对湿度计算完全够用。整个工程里不要出现float——51 上引入浮点库会让代码体积暴涨两三 KB4KB 的片子立刻装不下。还有一个容易被忽略的点12 位分辨率下单次转换需要 750ms。原文代码里的DELAY_600MS是不够的读到的会是上一次的转换结果表现为温度变化总是慢半拍。要么把延时加到 750ms 以上要么在初始化时把分辨率配置成 11 位375ms用 0.125℃ 的精度换响应速度。3.3 干湿球法查表 线性插值算相对湿度干湿球法的物理基础是湿球表面水分蒸发会带走热量空气越干燥蒸发越快湿球温度相对干球温度就降得越多。两者温差和相对湿度成一一对应关系核心公式是e Ew(Tw) − A·P·(T − Tw)RH e / Ew(T) × 100%其中Ew是饱和水汽压用 Magnus 公式算Ew(T) 6.112 × exp(17.62T / (243.12 T))单位 hPa。A是干湿表系数大约 0.00066×(10.00115Tw)乘以标准大气压 1013hPa 后约等于 0.67 hPa/℃。51 上算不了exp工程做法是把Ew做成 0~50℃、步长 5℃ 的查表中间线性插值。/* 0,5,10,...,50℃ 对应的饱和水汽压单位 0.1hPa */ static const unsigned int ew_tab[11] { 61, 87, 123, 170, 234, 317, 424, 562, 738, 958, 1234 }; /* t 为 0.1℃ 单位整数返回 0.1hPa 单位的饱和水汽压 */ unsigned int ew_lookup(int t) { unsigned char i; if (t 0) return ew_tab[0]; if (t 500) return ew_tab[10]; i (unsigned char)(t / 50); /* 定位到 5℃ 区间 */ return ew_tab[i] (unsigned int)(((unsigned long)(ew_tab[i1] - ew_tab[i]) * (t - i * 50)) / 50); /* 区间内线性插值 */ } /* td 干球、tw 湿球单位 0.1℃返回 0.1%RH0~1000 */ unsigned int calc_rh(int td, int tw) { unsigned int es, e; unsigned long drop; if (td tw) return 1000; /* 湿球不低于干球按饱和处理 */ es ew_lookup(td); e ew_lookup(tw); drop (unsigned long)(td - tw) * 67 / 10; /* 0.669hPa/℃ - 0.1hPa */ if (e drop) return 0; /* 低于露点防止无符号下溢 */ return (unsigned int)((unsigned long)(e - drop) * 1000 / es); }ew_tab的数值是按 Magnus 公式算出来再放大 10 倍取整的插值误差在 0.3%RH 以内比 DS18B20 本身 ±0.5℃ 的精度贡献小得多不构成瓶颈。drop用无符号长整型是因为(td - tw)最大可能到 20020℃ 温差乘 67 后会超出 16 位整数范围这是 C51 里很典型的一处溢出陷阱。湿度测量的实际误差主要来自湿球纱布的湿润程度和通风条件。真机上必须保证湿球探头包着脱脂纱布、纱布另一端浸在水槽里而且要有微风掠过——静止空气里湿球温度会趋近室温算出来的湿度永远偏高。4. 主程序状态机水位互锁、防干烧与声光报警4.1 主循环不要写成一条直线原文的流程图是读干球 → 读湿球 → 算湿度 → 读水位 → 比较 → 控制一条线跑到底加上 750ms 的转换延时一轮循环要将近两秒。这带来两个问题按键调整报警阈值时响应迟钝水位突变时不能立刻切断加湿输出。更合理的结构是主循环只管状态判断和输出温度转换放在后台。具体做法是用定时器 0 产生 1ms 中断做时基主循环每 10ms 跑一次水位扫描和输出更新每 1s 触发一次温度读取两边互不阻塞。4.2 阈值参数表与滞回区间控制参数集中定义比散在代码里到处写魔法数字好维护得多。参数符号取值说明加湿开启阈值RH_ON40%RH低于此值启动加湿加湿停止阈值RH_OFF55%RH高于此值停止形成 15% 滞回水位下限LV_MIN低水位有效触发断加湿 报警水位上限LV_MAX高水位有效停止补水提示温度报警上限T_HI可设默认 35℃超限声光报警温度报警下限T_LO可设默认 5℃低于此值报警滞回是这套设计里最值得加的一处改进。如果按 40% 单点判断湿度在阈值附近抖动时继电器会一秒吸合好几次机械触点寿命几天就报废听感上也是哒哒哒停不下来。把开启和停止拆成两个阈值实际湿度必须跨过 15% 的带宽才会改变输出状态。4.3 水位输入去抖与互锁逻辑开发阶段用三个按键代替水位传感器按键的机械抖动在 5~20ms 之间直接读会采到一串跳变。软件上做 20ms 延时确认或者用定时器累计三次相同采样。互锁逻辑的核心是只要低水位有效HUM_CTRL必须无条件置 0且这个判断要放在湿度判断之前不能被湿度分支短路。#define RH_ON 400 /* 40.0%RH0.1% 单位 */ #define RH_OFF 550 /* 55.0%RH */ #define DEBOUNCE 20 /* 去抖次数2ms 一次 */ static unsigned char low_cnt 0; /* 低水位连续计数 */ static bit water_ok 0; /* 水位允许标志 */ void water_scan(void) /* 2ms 调用一次 */ { if (LV_LOW 0) { /* 低电平有效说明缺水 */ if (low_cnt DEBOUNCE) low_cnt; } else { if (low_cnt) low_cnt--; } water_ok (low_cnt DEBOUNCE) ? 1 : 0; /* 稳定缺水才置 0 */ } void control_task(unsigned int rh, unsigned char water_ok) { static bit humidifying 0; if (!water_ok) { /* 第一优先级防干烧 */ humidifying 0; HUM_CTRL 0; LED_RUN 0; alarm_on(ALARM_WATER); /* 缺水声光报警 */ return; } if (!humidifying rh RH_ON) humidifying 1; /* 低于下限才开 */ if ( humidifying rh RH_OFF) humidifying 0; /* 高于上限才关 */ alarm_off(ALARM_WATER); HUM_CTRL humidifying; LED_RUN humidifying; }low_cnt用递增递减而不是直接赋值是为了抗单次脉冲干扰——电源波动引起的一两个采样点跳变不会立刻改变water_ok。water_ok声明的bit类型是 Keil C51 特有的位变量占位寻址区一个位比用unsigned char省内存。4.4 报警优先级与蜂鸣器驱动系统里有缺水报警和温度越限报警两类都走同一个蜂鸣器和同一只红色 LED那就必须定优先级。工程上的排序是缺水 温度越限 正常。缺水是安全相关只要成立就一直响直到水位恢复温度越限可以做成断续响响 200ms 停 800ms避免长时间高分贝噪声。无源蜂鸣器要方波驱动才有声音直接用BEEP 1只会得到一声轻微的嗒。用定时器 0 的 1ms 中断做一个 2kHz 方波发生器最省事/* 定时器 0 中断1ms 一次内部再分频出方波 */ void timer0_isr(void) interrupt 1 { TH0 0xFC; TL0 0x18; /* 12MHz 下 1ms 定时初值 */ if (alarm_flags ALARM_MASK) { /* 有报警才输出 */ beep_div ^ 1; BEEP beep_div; /* 每 1ms 翻转一次 500Hz 基频 */ } else { BEEP 0; } /* 断续报警计时、按键扫描、水位采样分频都在这里排队 */ }1ms 翻转一次得到 500Hz蜂鸣器响度尚可想更刺耳就把翻转周期缩到 0.5ms。TH0/TL0的初值随晶振变化12MHz 下 1ms 是0xFC18换成 11.0592MHz 就要按 1.085μs 一个机器周期重算这一处很容易被漏掉换晶振后蜂鸣器音调会明显偏低。5. 51 单片机烧录与 Proteus 仿真联调HEX 灌进芯片前的三道验证5.1 先在 Keil 里把算法单独跑一遍时序和算法混在一起调出了问题分不清是公式错了还是总线没通。有效做法是把calc_rh和ew_lookup拷到一个空的 PC 工程里喂几组已知的干湿球数据验证。比如干球 25.0℃、湿球 19.2℃查表算出来应该在 55%RH 附近如果得到 90% 或者 20%说明插值或者drop的缩放系数写错了。Keil C51 的编译链路是三个独立工具搞清楚参数含义比点 IDE 按钮有价值# 在 Keil 安装目录的 C51\BIN 下执行 C51.EXE main.c DEBUG OBJECTEXTEND # 编译生成 .obj带调试信息 BL51.EXE main.obj,ds18b20.obj TO humid # 链接绝对定位输出 OH51.EXE humid # 转换Intel HEX 格式OBJECTEXTEND打开扩展目标文件格式能在 μVision 里做源码级单步BL51的TO后面接输出名OH51生成的.hex才是烧录器认的文件。如果链接报DATA SPACE OVERFLOW说明 128 字节 RAM 不够用了把不必要的大数组挪到code段或者把unsigned int降级成unsigned char。5.2 在 Proteus 里核对时序Proteus 里搭好电路后加载 HEX 文件之前先把元件和接法核对一遍。常用的几个模型是 AT89C51、LM016L1602 的仿真模型、DS18B20。DS18B20 模型在仿真时不一定严格复现 750ms 转换时间和微秒级时序所以它验证的是逻辑通不通不是时序准不准。仿真时看到湿度数字在变、水位按下后继电器释放、蜂鸣器响主逻辑就对了。仿真的真正价值在两处一是抓 P1.2 波形看复位脉冲有没有到 480μs、存在脉冲有没有出现二是用单步执行配合变量观察窗看calc_rh的中间值。如果仿真里 DS18B20 一直返回 85先把READ里采样点的 NOP 数量加两个试试。5.3 上板烧录与首次上电检查烧录用 STC-ISP 走串口是最方便的前提是芯片换成了 STC89C52RC如果坚持用 AT89C51就得靠编程器或者专用仿真器写入。烧录前把 HEX 路径、芯片型号、串口号三项核对一遍尤其是串口号——插拔几次 USB 转串口线后 COM 号会变选错会一直提示正在检测单片机。首次上电按这个顺序看液晶背光亮不亮P2.7 和驱动管→ 第一行有没有出现字符P0 上拉和初始化延时→ 温度值是不是接近室温 → 按下低水位按键时继电器是否立刻断开。这个顺序从电源到通信再到逻辑出问题能快速缩小范围。5.4 常见故障对照表现象最可能原因排查动作液晶全黑块或全空白对比度电位器没调 / P0 排阻未接调 10kΩ 电位器量 P0.0 静态电平显示乱码初始化延时不足未等 15ms在初始化开头补 15ms 以上延时温度恒为 85℃转换未完成或时序被优化打乱延时加到 750ms时序文件关优化温度恒为 0℃跳过 ROM 命令用错多挂场合改 0x55 匹配 ROM或拆成两根 I/O继电器反复吸合单点阈值判断无滞回开启/停止阈值拉开 10%RH 以上上电不启动按复位才行复位电容过大RC 爬升太慢电容降到 10μF 以内加泄放二极管蜂鸣器只有嗒一声无源蜂鸣器被当有源用改用定时器输出方波或换有源蜂鸣器上电那一下拿示波器抓 P1.2 的复位脉冲480μs 拉低加 60μs 存在脉冲这段波形对上了剩下的基本都是应用层逻辑问题调试会快很多。本文还有配套的精品资源点击获取