首页 / 资讯中心 / 文章详情

温控系统Proteus仿真与编程:从LM35到51单片机完整设计

温控系统Proteus仿真与编程:从LM35到51单片机完整设计 ★ FEATURED ARTICLE
简介面向嵌入式与物联网初学者的鱼缸温控系统仿真资源基于Proteus和Keil实现硬件电路验证与C语言编程覆盖温度采集、LCD显示、PID闭环控制、超限报警等典型环节。资源包共33个文件压缩后仅208KB包含DSN仿真工程、C/H源文件、HEX固件、电路备份图纸及取模说明等可在Proteus中直接打开并运行调试。当前已有491人学习使用。通过该文件包读者可获取完整的仿真工程和程序源码参照PID算法C程序、DS18B20与LCD1602/12864驱动代码理解从传感器读取、界面显示到闭环调节与报警触发的实现思路同时工程附带备份与修改版本便于对比加光耦前后的差异适合课程设计、毕业设计或嵌入式入门进阶参考。1. 温控系统先用 Proteus 仿真再编程能提前暴露多少问题很多第一次做温控系统的朋友习惯直接焊板子结果要么加热过头要么继电器反复吸合几分钟就烧掉排查起来相当费劲。用 Proteus 做温控系统的仿真和编程价值在于把“传感器—单片机—执行器”这条闭环链路完整放进原理图里改参数只是几十秒的事烧坏继电器也不需要换器件。这篇笔记要讲的是一条能直接上手的路线LM35 采集温度运放把信号放大到 ADC 的量程51 单片机读取、换算、做控制判断再驱动加热器和风扇。你可能是刚学完单片机课程也可能是正在做课程设计或小规模产品原型这条路线能把控制逻辑和硬件接口一次讲透。2. 仿真电路搭建从传感器到加热器的信号链路设计先看清楚温控系统的链路再画图否则后面接错线都找不到原因。温度被 LM35 转成毫伏级电压这个电压太弱直接进 ADC 会浪费量化精度所以要先用运放放大到满量程附近再给 ADC0804 转换单片机拿到数字量之后换算成实际温度最后通过继电器或 PWM 控制加热负载。整个链路里每一级都有明确职责下面逐个选型和接线。2.1 器件选型为什么选择 LM35 ADC0804 51 这种组合LM35 是温控仿真里最常见的模拟温度传感器输出电压与温度呈线性关系10mV/°C0°C 时输出 0V100°C 时输出 1V。它不需要校准和外部补偿Proteus 库里直接搜索 LM35 就能拖放仿真模型稳定不会出现数字传感器那种时序读不到的问题。直接拿 1V 以内的信号去匹配 5V 满量程的 ADC量化分辨率会很差所以前级加一个同相放大电路很有必要。ADC0804 是 8 位并行 ADC接口简单CS、RD、WR、INTR 四根控制线就能和单片机对接。它的满量程由 Vref/2 决定默认取 2.5V 时满量程是 5V量化精度约 20mV对应到我们放大后的信号大约 0.4°C对温控演示完全够用。单片机选择 AT89C51 或者 AT89S52因为它们和教材、开发板、Keil 工程兼容性最好引脚定义也直观。如果以后想做更复杂的上位机联动串口功能在 51 上也能实现不必换平台。执行器驱动有两种常见做法。一种是开关量控制单片机 I/O 口通过三极管或 ULN2003 驱动继电器继电器控制加热器通断另一种是 PWM 调速用 MOS 管或继电器配合可控硅控制加热功率。Proteus 仿真里继电器模型比较直观适合观察滞后控制效果而 PWM 更适合后面做 PID 调功。建议两种都在电路里预留位置先用继电器跑通闭环再切换到 PWM 看曲线。2.2 Proteus 原理图接线最小系统、运放增益和参考电压计算画原理图前先算一遍增益。LM35 在 0 到 100°C 输出 0 到 1000mV想让 100°C 对应接近 5V放大倍数取 5 比较合适。运放选用 LM358同相放大倍数等于 1 加反馈电阻比输入端电阻取 R4 为 20kR5 为 5k倍数就是 5。这样 25°C 时 LM35 输出 250mV运放输出 1.25VADC 读到的数字量约 64分辨率清晰可辨。接线时注意几点LM35 供电接 5V输出接运放同相输入端运放输出接 ADC0804 的 VinVin- 接地ADC0804 的 Vref/2 接 2.5V可以用两个电阻分压加电容滤波或者直接用电源经过稳压得到 2.5VADC 的数据口 D0-D7 接单片机的 P0 口并且 P0 口要接上拉电阻因为 P0 是开漏输出结构。控制线部分AD C 的 CS 接 P2.0WR 接 P2.1RD 接 P2.2INTR 接 P2.3。下面给一个完整的引脚连接对照表照着连不会漏线。信号方向源器件引脚目的器件引脚说明温度信号LM35 VoutLM358 IN10mV/°C 原始信号放大输出LM358 OUTADC0804 Vin100°C 对应约 5V参考电压2.5V 基准ADC0804 Vref/2决定满量程 5V数据总线ADC0804 D0-D7单片机 P0.0-P0.78 位并行数据片选控制单片机 P2.0ADC0804 CS低电平有效写启动单片机 P2.1ADC0804 WR低脉冲启动转换读使能单片机 P2.2ADC0804 RD低电平读出数据状态查询ADC0804 INTR单片机 P2.3转换完成拉低加热驱动单片机 P3.6继电器驱动三极管基极控制加热器通断状态显示单片机 P1.0-P1.7LCD1602 数据口显示实时温度ADC0804 的转换时钟不能忽略。Proteus 仿真里如果 CLK 引脚悬空AD 转换不会启动INTR 永远保持高电平程序会卡死在等待状态。常见做法是用 Proteus 的 DCLOCK 信号源设置频率 500kHz 左右接到 CLK 脚。如果用单片机 ALE 引脚分频记得 ALE 频率对 ADC0804 可能偏高稳妥起见接一个 D 触发器分频到 600kHz 以下。2.3 执行器与降温通道加热和风扇如何同时存在纯加热的温控系统只能做升温实验温度超过设定值之后没有主动降温手段仿真曲线会长期停留在高位控制算法看不出好坏。常见做法是加一个风扇继电器当温度高于上限时开启风扇负载模拟强制风冷。Proteus 里风扇用一个直流电机模型替代加热器用电阻或者灯泡模型替代都行两个负载分别由两个继电器控制。降温通道的接线和加热通道类似单片机 P3.7 通过三极管驱动另一个继电器。需要注意的是Proteus 里的温度传感器并不会被风扇物理降温它只反映电路电压所以风扇回路更多是为了验证控制逻辑和展示系统完整性。真实验证降温效果时需要把温度模型和环境模型配合起来这个后面在避坑章节单独讲。3. 编程实现从 ADC 采集到温度换算到 PWM 输出的完整固件电路画好后编程才是温控系统的主战场。程序分为三个层次底层是 ADC0804 的采集时序中间是温度换算和显示上层是控制算法与执行输出。很多工程问题不是算法不行而是底层采集就有偏差所以先从最底层的代码说起。3.1 ADC0804 采集时序状态位等待比固定延时更可靠ADC0804 启动一次转换需要给 WR 一个低脉冲转换完成后 INTR 引脚会由高电平拉低。常见错误是固定延时等待转换完成但 ADC 的转换时间会随时钟频率变化延时太短读出来的是旧数据延时太长又拖慢采样周期。我比较习惯用 INTR 等待的方式转换完成信号到了才读数据。下面这段是 Keil C51 工程里的采集函数引脚定义和时序都做了注释。#include REG51.H #define ADC_DATA P0 // ADC0804 数据口接 P0 sbit ADC_CS P2^0; // 片选 sbit ADC_WR P2^1; // 启动转换 sbit ADC_RD P2^2; // 读使能 sbit ADC_INTR P2^3; // 转换完成标志 unsigned char read_adc(void) { unsigned char value; ADC_CS 0; // 拉低片选选中 ADC0804 ADC_WR 0; // 写信号低脉冲 ADC_WR 1; // 上升沿启动一次转换 while(ADC_INTR 1); // 等待 INTR 拉低表示转换完成 ADC_RD 0; // 读使能有效 value ADC_DATA; // 从 P0 读入 8 位转换结果 ADC_RD 1; // 关闭读使能 ADC_CS 1; // 释放片选 return value; }代码逻辑很直接每次调用先选中 ADC然后发写脉冲启动转换接着死等 INTR 信号。这里有个容易被忽略的细节ADC0804 的 INTR 在启动转换后会被拉高转换完成再拉低所以代码里等待条件写的是while(ADC_INTR 1)。如果初始化时 INTR 已经是低电平这个等待会跳过直接读到上一次的结果因此主循环里每次采集之间最好保留几十毫秒间隔。参数说明ADC0804 的时钟输入建议 400kHz 到 640kHz频率越高转换越快但精度会略降。Proteus 仿真时可以用 DCLOCK 信号源直接给 500kHz实体板卡上则要注意晶振频率与分频关系。3.2 温度换算量程、电压和整数的关系拿到 8 位采样的数字量之后要把它换算成真实温度。这里必须先明确硬件的对应关系LM35 在 100°C 输出 1000mV经过 5 倍放大后是 5000mV正好对应 ADC0804 的满量程 5V所以数字量 255 对应 100°C。反算回去每一位代表约 19.6mV也就是约 0.39°C。换算代码里有三个容易出问题的地方整数溢出、浮点开销、显示精度。用 51 单片机时我习惯全部用整数运算先放大再除法避免浮点库占用程序空间。unsigned char adc_value; unsigned int voltage_mv; unsigned char temperature; adc_value read_adc(); // 满量程按 5000mV 计算数字量 / 255 * 5000 voltage_mv (unsigned int)((unsigned long)adc_value * 5000 / 255); // 单位电压 50mV 对应 1°C传感输出 10mV/°C * 放大倍数 5 temperature (unsigned char)(voltage_mv / 50);这段代码先算电压再算温度每一步都有明确物理意义。adc_value * 5000最大是 127500016 位整型放不下所以先强制转成 unsigned long 再做乘法这是很多人翻车的地方。除以 255 得到毫伏数再除以 50 得到整数的摄氏度。注意这里没有做四舍五入如果显示要求高一位小数可以把 temperature 存成 10 倍温度值比如 25.3°C 存成 253显示时再分别取十位和个位。显示部分我用 LCD1602 来输出实时温度P1 口接数据线RS、RW、EN 分别接 P3.0、P3.1、P3.2。显示完整代码其实较长核心就一句话把温度的十位、个位、小数位拆出来分别加 0x30 转成 ASCII 码。新手最容易犯的错误是直接用sprintf51 的存储空间有限编译出来的库函数体积很大建议手写拆分。3.3 开关控制和 PWM 输出的固件骨架温度换算出来之后控制策略就要落到输出上。开关控制最简单设定一个下阈值和一个上阈值温度低于下阈值就开加热高于上阈值就关加热中间的区间保持当前状态不动。这个滞回区间就是防抖区间下面代码里用两位小数的温度值直接比较。// 目标温度 30.0°C防抖区间 1.5°C #define TEMP_SET 300 #define TEMP_HYSTERESIS 15 unsigned int current_temp_x10; // 实际温度放大 10 倍 if(current_temp_x10 (TEMP_SET - TEMP_HYSTERESIS)) { HEATER 1; // 温度偏低开启加热 } else if(current_temp_x10 (TEMP_SET TEMP_HYSTERESIS)) { HEATER 0; // 温度偏高关闭加热 } // 中间状态不改变输出避免继电器频繁动作PWM 输出则需要一个定时器周期触发。用 Timer0 工作在模式 2即 8 位自动重装模式晶振 11.0592MHz 时每个计数周期约 1.085 微秒设置初值 0xA4 可以得到约 0.1ms 的中断周期。累计 100 次中断就是 10ms即 100Hz 的 PWM 频率对人眼和继电器都足够平滑。sbit HEATER P3^6; // 加热器控制引脚 #define PWM_PERIOD 100 // 10ms 周期对应 100 个 0.1ms 计数 static unsigned char pwm_cnt 0; static unsigned char pwm_duty 0; void Timer0_ISR(void) interrupt 1 { TL0 0xA4; // 重装载初值保证 0.1ms 周期 TH0 0xA4; if(pwm_cnt PWM_PERIOD) { pwm_cnt 0; } // 占空比比较计数小于目标占空比时输出有效 HEATER (pwm_cnt pwm_duty) ? 1 : 0; }pwm_duty的范围是 0 到 100它不是固定常量而是由控制算法计算后不断更新。比如 PID 输出的 0 到 255 可以直接缩放到 0 到 100也可以直接让周期变成 255 个计数点这样 PID 输出和 PWM 占空比一一对应省一次除法。PWM 频率的选择有讲究控制加热器时太高会增加开关损耗太低会让温度波动明显10ms 周期在绝大多数教学仿真里都比较适中。4. 控制策略与参数整定从通断控制到 PID 的递进思路很多教程一上来就讲 PID但实际工程里通断控制已经能解决 80% 的温控问题。单片机课程的温控仿真先把通断控制调稳再考虑 PID这样理解更深调试也有对照。4.1 通断控制的滞回区间怎么设才合理通断控制的本质是让系统在目标温度附近振荡。如果没有滞回区间温度刚到设定值就关加热结果热量还在继续传导温度过冲等温度降到设定值以下又开加热又会反复波动。继电器在这种振荡下频繁吸合寿命急剧下降。滞回区间的设置直接影响温控精度和继电器寿命。以目标 30°C 为例滞回区间设 1°C即 29.5°C 开启加热、30.5°C 关闭加热温度波动范围约 1°C。这个区间越窄温控精度越高但继电器动作越频繁Proteus 仿真里因为响应快很容易看到继电器不断切换。我一般把仿真里的滞回区间放到 1.5°C 左右既能看出明显的趋势又不会把继电器模型折腾出噪声。实体系统里还要结合加热功率和传感器响应时间调整总原则是保证两次加热间隔足够长一般不小于继电器动作周期的 5 到 10 倍。通风断控制的参数调整也有规律。加热功率不变的情况下采样周期越短越容易在临界点反复触发采样周期如果太长温度可能已经过冲之后才被采集到。建议采样周期 500ms 到 1s 之间每次采样后更新一次继电器状态不需要在中断里做控制判断。4.2 增量式 PID 在 51 上的实现和三个整定参数通断控制只能让温度在目标附近波动做不到平稳。想看到一条平滑逼近设定值的曲线就需要 PID 的输出连续可调用 PWM 控制加热功率。PID 在单片机上有位置式和增量式两种写法我推荐增量式原因有三个它输出的是控制增量误动作影响小计算基于最近几个误差值不需要累加历史误差不易积分饱和在 51 这种低算力平台上浮点运算也不吃力。增量式 PID 的核心公式是输出增量等于三项误差变化的加权和。积分项直接选当前误差作为增量的一部分微分项用误差的一阶差分和二阶差分表达最终输出是上一次输出加上这个增量。// 增量式 PID 参数 float Kp 8.0f; // 比例增益 float Ki 0.5f; // 积分增益 float Kd 2.0f; // 微分增益 float err_now 0.0f; // 当前误差 float err_prev 0.0f; // 上一拍误差 float err_prev2 0.0f; // 上上拍误差 float out 0.0f; // 当前输出 unsigned char pwm_target 0; // 每个控制周期调用一次current 为当前温度 void pid_update(unsigned int current, unsigned int setpoint) { float delta 0.0f; err_now (float)(setpoint - current) / 10.0f; // P 项当前误差与上拍误差之差 // I 项当前误差本身 // D 项误差二阶差分用于抑制过度趋势 delta Kp * (err_now - err_prev) Ki * err_now Kd * (err_now - 2.0f * err_prev err_prev2); out delta; if(out 100.0f) out 100.0f; // 占空比上限钳位 if(out 0.0f) out 0.0f; // 下限钳位 pwm_target (unsigned char)out; err_prev2 err_prev; err_prev err_now; }增量式公式有个优势如果系统已经稳定误差不变那么err_now - err_prev为零二阶差分也是零delta 只剩 Ki 乘以当前误差但当前误差为零时输出就不再变化不会出现位置式 PID 积分项不断累积导致超调的问题。输出钳位也相当于抗积分饱和如果 PID 算出来超过 100%直接截断不把多余的部分带入下一次计算。整定参数时不要直接三个一起调。常规顺序是先让 Ki 和 Kd 为零只调 Kp。在目标温度 40°C 下从 Kp2 开始加大观察温度曲线是否在目标附近等幅振荡记下临界振荡的 Kp 值和振荡周期。然后按经验比例缩小 Kp加入 Ki 消除稳态误差最后加 Kd 减小超调。Proteus 的优势是修改参数后立即重新仿真几分钟就能看到一组完整的整定曲线。值得说清楚的是Proteus 里的温度模型比较理想传热时间常数比实体设备快很多所以在仿真里整定好的 PID 参数直接搬到板子上往往偏激进。实体系统热惯性大同样的 Kp 可能不够快需要重新观察曲线微调。这一点很多初学者容易误解以为仿真参数可以直接移植。5. 避坑Proteus 温控仿真里最常遇到的 5 个问题这一章是我自己做温控仿真时反复踩过的坑每条都按现象、原因、解决方式写清楚希望能帮你节省排查时间。5.1 温度显示始终不变或者数值为 0现象程序跑起来 LCD 一直显示 0加热器开开关关但温度读数纹丝不动。原因LM35 的输出没有正确接到运放输入端或者 ADC0804 的转换时钟没有接。我遇到最多的情况是 Proteus 里元件引脚悬空看起来连了线实际上网络标号没对齐导致信号根本没传过去。解决先在仿真里放两个电压表一个测 LM35 输出一个测运放输出。用鼠标拖动 LM35 旁边的温度调节箭头改变仿真温度看电压表是否随之线性变化。如果 LM35 输出正常但运放输出不变查运放供电和反馈电阻网络如果运放输出正常但 ADC 读数为零查 CLK 时钟源和 CS、RD、WR 控制线是否真的连到了对应 I/O 口。5.2 INTR 一直为高程序卡死现象程序停留在 read_adc 函数的 while 循环里单步调试发现ADC_INTR永远读回 1。原因ADC0804 必须有外部时钟才能完成转换。Proteus 中直接拖出来的 ADC0804 默认没有 CLK 信号如果忘了接 DCLOCK 或者接了但频率设置不对转换永远不会完成。解决从 Proteus 左侧器件库搜 DCLOCK放到 ADC0804 的 CLK 引脚旁边属性里把频率设为 500kHz。如果用的是单片机 ALE 输出要通过分频器降到 640kHz 以下再接 CLK。仿真通过后画实体板时CLK 引脚通常会用一个 555 多谐振荡器产生或者直接用单片机的定时器翻转输出。5.3 继电器疯狂通断仿真噪音很大现象温度到达设定值后继电器不是稳定保持而是快速反复吸合释放Proteus 仿真波形看上去密密麻麻。原因通断控制的滞回区间设得太小加上仿真系统热响应极快温度在设定值附近来回穿越继电器每次穿越都切换一次。还有一个因素是采样周期太短每次采集到新的温度值就立刻改变输出没有给系统自然冷却留时间。解决把滞回区间从 0.5°C 增大到 1.5°C 左右同时把控制周期从几十毫秒拉长到至少 500ms。如果还是频繁通断检查温度换算是不是有抖动适当对采样值做中值滤波取连续 3 到 5 次采样的中间值再参与控制。5.4 温度只升不降降温通道不起作用现象温度超过设定值很多风扇继电器也动作了但温度曲线没有任何下降趋势。原因Proteus 里的 LM35 模型本质是一个电压源它不感知环境热交换风扇对它没有物理降温效果。很多新手以为仿真里能直接看到温度下降实际不是这样必须通过外部方式模拟降温过程。解决有两个常见做法。一是把温度模型外部化用一个可控电压源给运放输入一个随时间下降的电压模拟降温阶段二是直接用信号源替换 LM35在仿真中预设温度曲线让控制逻辑去响应。如果只是为了验证风扇控制逻辑也可以给风扇回路加一个独立的反馈源让风扇开启时拉低模拟温度电压。5.5 Keil 代码编译正常Proteus 却运行异常现象程序编译零错误也生成了 hex 文件但 Proteus 里单片机完全没有反应LED 不亮LCD 无显示。原因最常见的是没有把 hex 文件加载进单片机模型或者加载了但晶振频率设置和程序里的参数不一致。另一种是 Keil 生成的 hex 文件格式不兼容选择了错误的输出格式。解决双击 Proteus 里的单片机模型在程序文件路径选择 Keil 生成的 hex 文件。晶振频率要与实际仿真电路一致如果代码按 11.0592MHz 计算定时器初值而模型里设置的晶振是 12MHz定时就全偏了。Keil 输出设置里记得勾选 Create HEX File并在 Output 选项卡里确认格式是 Intel HEX。6. 仿真转实物前的最后一道验证边界条件测试与信号注入把仿真代码放到实体板之前我习惯做一组边界条件测试这一步能发现大量只在特定输入下才暴露的问题。方法很简单用 Proteus 的信号发生器或电压源直接替代 LM35在运放输入端注入固定的直流电压分别对应 25°C、50°C、75°C 和 100°C观察 LCD 显示值和控制输出。这样绕过了传感器误差把重点放在单片机程序和 ADC 采样链路上。注入电压的计算是25°C 对应 LM35 输出 250mV放大后 1.25V50°C 放大后 2.5V75°C 放大后 3.75V100°C 放大后 5V。用电压源在这四个点分别仿真如果显示出来的温度误差超过 1°C问题基本出在 ADC0804 的参考电压精度或运算放大器的失调上。这个测试做好之后再把 LM35 接回电路仿真通过就意味着核心逻辑没大问题。另一个值得做的验证是看 PWM 占空比输出。把控制目标设为 50°C初始温度设为 20°C用 Proteus 的数字示波器观察 P3.6 引脚的波形确认占空比随温度逼近目标而逐渐减小。如果占空比始终满值直到超调说明 PID 积分项过强如果稳定后还有高频抖动说明微分增益偏大。波形比只看数码管数字直观得多强烈建议每一个做温控仿真的朋友都打开示波器观察一次。我自己的习惯是每改一次参数先记下温度曲线的过冲量和稳定时间做成一张表格再调整下一个参数。仿真不是调完就结束它真正的价值是在投板前把控制逻辑和异常路径全部跑一遍。当年做某课程设计时我就是在仿真里发现继电器驱动引脚初始化默认是高电平上电瞬间加热器会误动作于是加了一行上电强制关闭的初始化代码才避免实机一上电就全功率加热的问题。这种细节只有认真做边界测试才能发现。希望这篇笔记能让你在温控仿真与编程这条路上少走几个弯每一步都有可验证的依据。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站