☰
基于51单片机的谷物智能干燥系统 Proteus仿真与源代码实现
2026/10/28 6:52:02 网站建设 项目流程

简介:这份资源面向电子信息、自动化及测控类专业的学生与单片机初学者,提供一套基于51单片机的谷物智能干燥系统完整仿真方案,可用于课程设计、毕业设计或电子竞赛参考。系统以51单片机为核心,实现谷物温湿度采集、冷风与热风控制、对应指示灯状态显示,并通过按键完成加入谷物、开始干燥、停止干燥、取出谷物等操作,LCD1602液晶屏同步显示运行信息,帮助读者理解闭环控制与执行机构联动的设计思路。压缩包共28个文件,约418KB,包含Proteus仿真工程(pdsprj、DSN)、Keil工程与C源码(uvproj、c、h)、编译输出(hex、obj、lst、m51)以及备份与说明文件,仿真图与源代码配套齐全,可直接运行验证。目前已有163人学习下载,适合作为谷物干燥仪、烘干机类课题的参考模板,便于快速搭建仿真环境、对照代码理解控制逻辑并在此基础上二次开发。

1. 谷物智能干燥系统:为什么要在 Proteus 里先跑一遍

收粮季最怕什么?不是没设备,是干燥机温湿度控制不稳,烘过头谷物开裂,烘不到位又发霉。基于单片机的谷物智能干燥系统,核心就是用传感器实时采集温湿度,单片机按阈值控制加热和通风,让谷物始终待在安全水分区间。但直接焊板子、烧程序、接加热管,一旦逻辑写错,轻则谷物报废,重则烧毁器件。所以我在动手做实物之前,一定会先在 Proteus 里把整套系统跑通——仿真图验证电路连接,源代码验证控制逻辑,两者对上了再投硬件。这套方案适合正在做课程设计、毕业设计,或者想快速验证干燥控制算法的嵌入式从业者。51单片机、STC系列都行,关键是把仿真这一步走扎实。

2. 系统架构与 Proteus 元器件选型:先把仿真图搭对

2.1 为什么选 51 单片机而不是 STM32

谷物干燥系统的控制量其实不复杂:两路模拟输入(温度、湿度),两路数字输出(加热继电器、风机继电器),外加一个 LCD 显示和几个按键。这种规模用 51 单片机绰绰有余,而且 Proteus 对 51 系列的支持最成熟,仿真时不容易出现模型不收敛的问题。STC8G1K17 这类增强型 51 也可以,但 Proteus 元件库里的通用 80C51 模型更稳定,仿真速度也快。

我一般选 AT89C51 或 AT89C52 作为仿真核心,原因有三:一是 Proteus 自带模型,不用额外装库;二是 Keil 里选对应的 Device 就能编译,工具链简单;三是引脚定义清晰,P0 口加上拉电阻就能驱动 LCD,P1/P2 口做输入输出分配很灵活。如果你手头只有 STC 单片机,仿真阶段也可以用 80C51 替代,等实物阶段再换回来,代码几乎不用改。

2.2 Proteus 仿真图的关键元器件清单

搭建仿真图之前,先把元件备齐。下面这张表是我常用的配置,覆盖了谷物干燥系统的最小完整功能:

元器件Proteus 库中名称作用关键参数
单片机AT89C51主控12MHz 晶振
温湿度传感器DHT11采集谷物环境温湿度单总线,精度 ±2℃/±5%RH
液晶显示LM016L显示当前温湿度与设定值16×2 字符
继电器RELAY-SPDT控制加热管和风机5V 线圈
按键BUTTON设定阈值、切换模式4 个
晶振CRYSTAL提供时钟12MHz
电阻RES上拉、限流10kΩ、1kΩ
三极管2N2222驱动继电器NPN
电源POWER供电+5V

DHT11 在 Proteus 里的模型行为跟实物有差异,仿真时数据变化比较慢,这是正常的。如果你要做更精确的温湿度响应测试,可以用 POT-HG 电位器模拟模拟量传感器,再配合 ADC0809 做采集,但那样电路会复杂不少。对于谷物干燥这种慢过程控制,DHT11 的仿真精度够用了。

2.3 引脚分配与电路连接要点

引脚分配直接决定后面代码怎么写,所以我在画仿真图之前会先把分配表定下来。下面是我常用的分配方案:

// 引脚分配定义(AT89C51) // P0.0 ~ P0.7 -> LCD1602 数据口 D0~D7 // P2.0 -> LCD RS // P2.1 -> LCD RW // P2.2 -> LCD EN // P1.0 -> DHT11 数据线 // P1.1 -> 加热继电器控制 // P1.2 -> 风机继电器控制 // P3.2 -> 按键1:进入设置 // P3.3 -> 按键2:加 // P3.4 -> 按键3:减 // P3.5 -> 按键4:确认/退出

P0 口接 LCD 时必须加上拉电阻,因为 51 的 P0 是开漏输出,不加电阻液晶根本不显示。这个坑我在早期仿真里踩过好几次,Proteus 里不报错,但 LCD 就是一片空白。继电器线圈两端要反并联一个续流二极管,Proteus 里虽然不一定烧模型,但实物上不加必烧三极管。DHT11 的数据线需要 4.7kΩ 上拉电阻,仿真里可以省略,但代码里必须按单总线时序写,否则读出来全是零。

3. 源代码核心逻辑:从 DHT11 读取到继电器控制

3.1 DHT11 单总线驱动的时序实现

DHT11 用的是单总线协议,一根线完成握手、发送和接收。51 单片机没有硬件单总线外设,全靠软件延时模拟时序。下面这段代码是我反复调过的版本,在 Proteus 里能稳定读到数据:

#include <reg51.h> #include <intrins.h> sbit DHT11 = P1^0; // 微秒级延时,12MHz 晶振下约 1us void delay_us(unsigned int us) { while (us--) { _nop_(); _nop_(); _nop_(); _nop_(); } } // 毫秒级延时 void delay_ms(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 120; j++); } // 主机发送开始信号 void DHT11_Start(void) { DHT11 = 0; delay_ms(20); // 拉低至少 18ms DHT11 = 1; delay_us(30); // 拉高 20~40us 等待响应 } // 等待 DHT11 响应 unsigned char DHT11_Check(void) { unsigned char timeout = 0; while (DHT11 && timeout < 100) { // 等待低电平响应 timeout++; delay_us(1); } if (timeout >= 100) return 0; timeout = 0; while (!DHT11 && timeout < 100) { // 等待高电平 timeout++; delay_us(1); } if (timeout >= 100) return 0; return 1; } // 读取一个字节 unsigned char DHT11_ReadByte(void) { unsigned char i, dat = 0; for (i = 0; i < 8; i++) { while (!DHT11); // 等待 50us 低电平结束 delay_us(40); // 延时 40us 判断高电平持续时间 dat <<= 1; if (DHT11) { dat |= 1; while (DHT11); // 等待剩余高电平结束 } } return dat; } // 读取温湿度,temp 和 humi 为输出 unsigned char DHT11_Read(float *temp, float *humi) { unsigned char buf[5]; unsigned char i; DHT11_Start(); if (!DHT11_Check()) return 0; for (i = 0; i < 5; i++) { buf[i] = DHT11_ReadByte(); } DHT11 = 1; // 校验:前四字节之和等于第五字节 if (buf[0] + buf[1] + buf[2] + buf[3] == buf[4]) { *humi = buf[0] + buf[1] * 0.1; *temp = buf[2] + buf[3] * 0.1; return 1; } return 0; }

这段代码的关键在延时精度。12MHz 晶振下,delay_us里四个_nop_()大约 4us,加上循环开销接近 1us 每单位。如果你换 11.0592MHz 晶振,延时参数要微调,否则 DHT11 读出来全是 0 或者校验失败。Proteus 仿真时 DHT11 模型对时序容忍度比实物高,但代码写得太离谱照样读不到数据。我一般会在DHT11_ReadByte里加一个超时计数,防止while(!DHT11)死循环卡住整个程序。

3.2 温湿度阈值判断与继电器输出逻辑

读到了温湿度,接下来就是控制逻辑。谷物干燥的目标是把湿度降到安全水分以下,同时温度不能超过谷物承受上限。我用的策略是:湿度优先,温度做上限保护。

// 控制逻辑:湿度优先,温度保护 void Control_Logic(float temp, float humi) { // 温度超过上限,强制关闭加热,只开风机 if (temp >= TEMP_MAX) { HEATER = 1; // 继电器高电平断开 FAN = 0; // 风机开启 return; } // 湿度高于目标值,开启加热和风机 if (humi > HUMI_TARGET) { HEATER = 0; // 加热开启 FAN = 0; // 风机开启 } // 湿度达到目标,关闭加热,风机延时关闭 else { HEATER = 1; FAN = 1; } }

TEMP_MAX一般设 45℃ 左右,具体看谷物品种。小麦干燥温度不超过 45℃,玉米可以到 50℃,稻谷更敏感,建议 40℃ 以下。HUMI_TARGET根据安全水分设定,小麦 12.5%,玉米 14%,稻谷 13.5%。这些参数在代码里用宏定义,方便调整:

#define TEMP_MAX 45.0 // 温度上限,单位 ℃ #define HUMI_TARGET 13.0 // 目标湿度,单位 % #define TEMP_MIN 10.0 // 温度下限,低于此值不启动

继电器输出要注意电平极性。Proteus 里的 RELAY-SPDT 模型默认低电平吸合,但实物继电器模块可能是高电平触发。仿真阶段先按低电平有效写,等实物调试时如果发现继电器反了,改一下HEATER和FAN的赋值就行。另外,加热和风机不要同时频繁启停,我一般加一个 30 秒的最小动作间隔,防止继电器抖动。

3.3 LCD1602 显示与按键设置阈值

LCD1602 用 4 位或 8 位模式都行,我习惯用 8 位,接线简单。显示内容分两行:第一行显示当前温湿度,第二行显示设定阈值和系统状态。

// LCD1602 显示刷新 void LCD_Display(float temp, float humi) { char buf[16]; // 第一行:当前温湿度 LCD_SetCursor(0, 0); sprintf(buf, "T:%4.1fC H:%4.1f%%", temp, humi); LCD_WriteString(buf); // 第二行:设定值和状态 LCD_SetCursor(1, 0); sprintf(buf, "SET:%2.0fC %2.0f%% ", TEMP_MAX, HUMI_TARGET); LCD_WriteString(buf); // 状态指示 LCD_SetCursor(1, 12); if (HEATER == 0) LCD_WriteString("HT"); else if (FAN == 0) LCD_WriteString("FN"); else LCD_WriteString("--"); }

按键处理用状态机,四个按键分别对应进入设置、加、减、确认退出。设置模式下,光标闪烁提示当前编辑位,加/减按键调整数值,确认后写入全局变量并保存到 EEPROM(仿真阶段可以省略 EEPROM,直接存 RAM)。按键消抖用 10ms 延时,Proteus 里按键模型没有抖动,但实物必须有,所以代码里统一加上。

4. Proteus 仿真联调:从编译到波形验证的完整流程

4.1 Keil 编译生成 HEX 文件并加载到 Proteus

代码写完后,在 Keil 里新建工程,Device 选 AT89C51,晶振频率填 12MHz,输出勾选“Create HEX File”。编译通过后,打开 Proteus 仿真图,双击单片机,在 Program File 栏选择刚才生成的 HEX 文件,Clock Frequency 填 12MHz。这一步看着简单,但有两个高频翻车点:一是 Keil 里没勾 HEX 输出,Proteus 加载时找不到文件;二是晶振频率不一致,导致延时函数全偏,DHT11 读不出数据。

加载成功后点运行,LCD 应该立刻显示温湿度。如果 LCD 一片空白,先查 P0 口上拉电阻,再查 LCD 的 RS/RW/EN 接线。如果 DHT11 读数一直是 0,用 Proteus 的虚拟示波器看 P1.0 波形,正常应该看到主机拉低 20ms、拉高 30us、然后 DHT11 响应拉低 80us 再拉高 80us 的序列。波形不对就回去查DHT11_Start和DHT11_Check的延时。

4.2 用虚拟示波器和信号发生器验证时序

Proteus 里的虚拟示波器(OSCILLOSCOPE)是调时序的利器。把通道 A 接 P1.0,通道 B 接加热继电器控制脚 P1.1,运行后观察波形。正常工作时,P1.0 上应该周期性地出现 DHT11 通信序列,周期约 2 秒(我一般 2 秒读一次)。P1.1 在湿度高于目标时变低(加热开启),湿度达到目标后变高。

如果 DHT11 通信失败,示波器上会看到主机发送开始信号后,DHT11 没有拉低响应,或者响应后数据位全是高电平。前者通常是delay_ms(20)不够 18ms,后者是delay_us(40)判断窗口偏了。12MHz 晶振下,delay_us(40)实际约 40~50us,正好卡在 DHT11 数据位 0 和 1 的分界线上,所以读出来容易出错。我的经验是把判断点设在 35us 左右,留一点余量。

信号发生器(SIGNAL GENERATOR)可以用来模拟传感器异常,比如给 P1.0 注入一个固定频率的方波,看程序会不会死循环。如果程序卡在while(!DHT11)里,说明超时保护没写到位。加上超时计数后,即使传感器坏了,系统也能继续显示和报警,不会整个瘫痪。

4.3 仿真步长与实时性设置

Proteus 默认仿真步长是 1ms,对于 DHT11 这种微秒级时序来说太粗了。在“System”菜单里把“Animation”和“Simulation”步长调到 1us 或者更小,波形才看得清。但步长调太小,仿真速度会明显变慢,所以调试时序时用 1us,跑整体逻辑时用 1ms 就行。

另外,Proteus 的“Real Time”模式跟实际时间有偏差,DHT11 模型响应比实物慢,所以仿真里 2 秒读一次,实际可能感觉像 3~4 秒。这不是代码问题,是仿真模型本身的限制。如果你要做时间敏感的算法验证,比如 PID 控制,建议在仿真里先把 PID 参数调个大概,实物上再精调。

5. 避坑与排查:仿真能跑通但实物翻车的 5 个典型场景

5.1 LCD 显示乱码或空白

现象:Proteus 里 LCD 正常显示,烧到实物后要么全黑,要么只亮背光不显示字符。

原因:P0 口没加上拉电阻,或者上拉电阻阻值太大(比如用了 100kΩ)。51 单片机 P0 口是开漏输出,驱动能力很弱,不加 10kΩ 上拉,LCD 数据口根本拉不高。另外,LCD 的 V0 对比度引脚没接可调电阻,实物上对比度不对也会导致看不见字符。

解决:P0 口每个引脚接 10kΩ 上拉排阻到 +5V,V0 接 10kΩ 电位器中点,调节到字符清晰为止。Proteus 里可以省略上拉,但实物必须加。

5.2 DHT11 读数失败或校验错误

现象:仿真里 DHT11 读数正常,实物上读出来全是 0 或者校验和不匹配。

原因:延时函数在实物上偏差太大。Keil 编译优化等级不同,delay_us的实际执行时间会变。另外,实物 DHT11 对上拉电阻和线长有要求,线太长或者没上拉,信号质量差。

解决:用示波器测 P1.0 波形,调整delay_us里的_nop_()数量,直到主机拉低时间在 18~20ms、拉高在 20~40us。DHT11 数据线接 4.7kΩ 上拉到 +5V,线长控制在 20cm 以内。

5.3 继电器频繁启停导致触点烧蚀

现象:仿真里继电器动作正常,实物上运行几天后继电器触点粘连或者不吸合。

原因:控制逻辑没有加最小动作间隔,温湿度在阈值附近波动时,继电器每秒开关好几次。实物继电器机械寿命有限,频繁动作很快报废。

解决:在控制逻辑里加一个 30 秒的最小动作间隔定时器,每次继电器状态改变后,30 秒内不再响应新的控制请求。仿真阶段可以把这个间隔设短一点(比如 5 秒)方便观察,实物上改回 30 秒。

5.4 仿真通过但实物程序跑飞

现象:Proteus 里程序运行稳定,实物上运行几分钟后死机或者复位。

原因:没有开看门狗,或者电源滤波不好。实物环境有电机、继电器等干扰源,电源波动会导致单片机复位。另外,Keil 里没勾“Use On-chip ROM”或者中断向量表配置错误,也会导致跑飞。

解决:在代码里启用看门狗(STC 单片机有内部看门狗,51 需要外置 MAX813 等芯片),主循环里定期喂狗。电源部分加 100uF 电解电容和 0.1uF 陶瓷电容滤波,继电器线圈加续流二极管。

5.5 温度控制超调严重

现象:仿真里温度控制平稳,实物上加热管一开,温度冲过上限好几度才停。

原因:加热管有热惯性,继电器断开后余热还在释放。仿真模型没有热惯性,所以看起来控制很准。

解决:在控制逻辑里加一个提前量,温度接近上限前 3~5℃ 就提前关闭加热。另外,加热管功率不要选太大,匹配干燥箱体积就行。如果要求高,可以上 PID 控制,但 51 单片机做 PID 运算比较吃力,建议用 STC8G1K17 这类增强型 51,或者换 STM32。

6. 进阶技巧:用状态机重构控制逻辑并做仿真对比验证

6.1 从 if-else 到状态机的重构思路

前面写的控制逻辑是 if-else 堆出来的,功能没问题,但扩展性差。比如要加一个“预热模式”或者“冷却模式”,if-else 会越写越乱。我后来把控制逻辑重构成状态机,代码清晰很多,也方便在 Proteus 里做对比验证。

状态机定义四个状态:IDLE(空闲)、HEATING(加热)、VENTING(通风)、COOLING(冷却)。状态转移条件由温湿度和按键决定。下面是一个简化版的状态机框架:

typedef enum { STATE_IDLE, STATE_HEATING, STATE_VENTING, STATE_COOLING } SystemState; SystemState state = STATE_IDLE; void StateMachine(float temp, float humi) { switch (state) { case STATE_IDLE: if (humi > HUMI_TARGET && temp < TEMP_MAX) { state = STATE_HEATING; } break; case STATE_HEATING: if (temp >= TEMP_MAX) { state = STATE_COOLING; } else if (humi <= HUMI_TARGET) { state = STATE_VENTING; } break; case STATE_VENTING: if (humi <= HUMI_TARGET) { state = STATE_IDLE; } break; case STATE_COOLING: if (temp < TEMP_MAX - 5.0) { state = STATE_HEATING; } break; } // 根据状态输出 HEATER = (state == STATE_HEATING) ? 0 : 1; FAN = (state == STATE_HEATING || state == STATE_VENTING || state == STATE_COOLING) ? 0 : 1; }

状态机的优势在于每个状态的进入和退出条件明确,调试时可以在 Proteus 里加一个状态指示引脚,用示波器看状态切换波形,比看 LCD 直观得多。

6.2 在 Proteus 里做控制策略对比验证

有了状态机,就可以在 Proteus 里对比不同控制策略的效果。我一般做两组仿真:一组用简单的阈值控制,一组用状态机加提前量控制。用 Proteus 的图表功能(Graph)记录温度变化曲线,对比超调量和稳定时间。

具体操作:在 Proteus 里添加一个模拟分析图表,探针接 DHT11 的温度输出(或者用 POT 模拟温度变化),运行仿真后导出曲线。阈值控制的曲线会在目标值附近振荡,状态机加提前量的曲线更平滑。这个对比结果可以直接写进课程设计报告,比纯文字有说服力。

6.3 仿真代码到实物的移植检查清单

仿真跑通后,移植到实物前,我习惯过一遍下面这个清单:

检查项仿真环境实物环境处理方式
晶振频率12MHz可能不同改延时参数
P0 上拉可省略必须加加 10kΩ 排阻
继电器极性低电平吸合可能高电平改输出赋值
按键消抖可省略必须加加 10ms 延时
看门狗不需要建议启用加喂狗代码
电源滤波理想电源有波动加电容

这份清单是我踩了无数次坑之后总结的,每次移植前过一遍,能省下大量调试时间。特别是继电器极性和 P0 上拉,这两个问题在仿真里完全看不出来,实物上必翻车。

最后说一个习惯:我每次在 Proteus 里调通一个功能,就会把当时的仿真图和 HEX 文件一起存档,命名带上日期和版本号。后面改代码改出问题了,随时可以回退到上一个能跑的版本。这个后悔药,比任何调试技巧都管用。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询