简介:基于单片机Proteus仿真的空气质量PM2.5检测系统资源包,面向单片机初、中级学习者和电子设计人员,主要用于课程设计、毕业设计及环境监测类项目验证。系统集成PM2.5粉尘检测与温湿度采集,具备空气质量等级判断、历史数据查询、超限报警、断电保存等功能,测量范围覆盖0~999ug/m^3,可满足家庭、办公室及小型工厂等场景的监测需求。压缩包共55个文件,内含C语言源码、Proteus仿真工程(DSN)、原理图SchDoc、HEX固件、仿真截图及元件清单等,整体仅807KB,文件类型覆盖程序设计、电路仿真、文档说明等多个维度,便于直接打开工程进行学习、修改与二次开发。目前已有132人学习下载,适合希望通过完整仿真案例快速理解单片机软硬件协同设计的读者。
1. 单片机Proteus仿真的PM2.5检测系统到底在仿什么
先说结论:仿真环境里没有真实的粉尘颗粒,只有传感器输出到单片机引脚的电压,因此这个基于单片机Proteus(protues)仿真的PM2.5检测系统,真正仿的是采集链路,而不是空气本身。把一路可调电压当作PM2.5浓度,由单片机ADC量化后换算成µg/m³,再叠加DHT11温湿度结果,路由到LCD和报警电路,这套流程就叫protues仿真里的空气质量检测。它的价值在于:在没有硬件、没有风洞的条件下,把模拟量输入、单总线时序、串口打印和阈值报警全部跑通,并且这些逻辑转移到实物后仍然成立。常见做法是AT89C51作为主控,Proteus中放电位器模拟粉尘信号,DHT11模型读温湿度,LM016L显示。这套内容适合课程设计、毕设,也适合准备蓝桥杯单片机笔试的人用来理解传感器数据流。
2. PM2.5检测系统方案与传感器仿真模型选型
2.1 粉尘传感器:用电压节点模拟GP2Y1010的输出
实物项目里最常见的粉尘传感器是夏普GP2Y1010AU,内部红外LED照射空气,光敏二极管把散射光强度换成电压输出。输出电压与粉尘浓度近似线性,越脏电压越高,典型关系是0.5V对应50µg/m³,0.9V对应100µg/m³。Proteus元件库里没有GP2Y1010,也不可能有真正的颗粒散射模型,所以比较省事的替代方案是放一个电位器POT-HG,中心抽头输出0到5V可调电压,接在ADC输入引脚。电位器滑条往上调,等效粉尘浓度升高,整条链路的表现和真实传感器一致。
在程序里做映射时,需要先决定满量程关系。这里用ADC0809做8位量化,参考电压5V,数字0对应0V,数字255对应5V。把PM2.5浓度范围设为0到500µg/m³,电压0V对应0,5V对应500,换算公式写成:
#define PM25_FS 500.0f /* 满量程浓度 500ug/m3 */ #define ADC_VREF 5.0f /* 参考电压 5V */ float pm25_volt = adc_raw * ADC_VREF / 255.0f; float pm25 = pm25_volt / ADC_VREF * PM25_FS;逻辑说明:先把ADC原始值还原成电压,这一步避免以后调整量程时弄混量纲;再按线性比例转成浓度。这样在Proteus里把电位器电压调到3.3V,屏幕显示约为330µg/m³,可以和直流电压表读数互相印证,排查是哪一段转换出错。
参数说明:ADC_VREF必须和Proteus中ADC0809的VREF+引脚一致,否则会看到电压表数值和LCD显示值系统性偏差;PM25_FS在实物移植时要按照传感器数据手册里的灵敏度重新标定,仿真里先给500只作为默认满量程。
2.2 温湿度传感器:为什么选DHT11而不是DS18B20
温度部分用DS18B20也能测,但PM2.5检测系统一般要求同时读湿度和温度,DS18B20没有湿度,所以选DHT11更合适。Proteus自带DHT11模型,数据引脚通过单总线连接单片机P1.0,内部包含NTC和电容式湿度传感器。需要留意的是,Proteus对DHT11时序判断比较严格,起始信号低电平时间要维持在18ms左右,读每一位时的延时也要卡准。
如果当前Proteus版本里的DHT11模型不稳定,一个变通做法是把温度、湿度做成两路电压源,再走两个ADC通道,程序里直接按电压映射成工程单位。这种方案能绕过时序问题,但练不到单总线协议,所以在LCD调试阶段临时用可以,最终代码必须回到标准DHT11时序。
2.3 元件选型表与Proteus使用注意
为避免在protues仿真画图时逐个翻库,常用器件清单如下:
| 功能 | 元件/库名 | 参数与作用 | 仿真注意 |
|---|---|---|---|
| 主控 | AT89C51 | 8位单片机 | 双击后把Clock Frequency设为12MHz |
| 粉尘模拟 | POT-HG | 电位器,分压输出0~5V | 滑条调节要缓慢,避免仿真发散 |
| ADC | ADC0809 | 8位逐次逼近ADC | VREF+接5V,VREF-接地 |
| 温湿度 | DHT11 | 单总线数字温湿度 | 数据脚加10k上拉 |
| 显示 | LM016L | 两行16字符LCD | 可加对比度电位器 |
| 串口 | VIRTUAL TERMINAL | 虚拟终端 | RX接单片机TXD,TX接RXD |
Proteus版本建议使用8.9以上,老版本在ADC0809和DHT11同时仿真时容易出现总线不释放,尤其是DHT11模型需要反复复位才能读到数据。如果打开工程后元件引脚乱飞,右键元件选择“分解”再重新连线。电位器POT-HG在仿真运行时拖动滑条,偶尔会触发“仿真发散”报错,原因是步进电压跳变太快,解决方法是把仿真步长减小,或者改用键盘方向键慢慢调整。
3. 在Proteus里搭出PM2.5采集与显示电路的关键参数
3.1 单片机最小系统:晶振、复位和P0上拉别抄错
AT89C51的时钟电路用12MHz晶振,晶振两端各接22pF电容到地。复位电路使用10µF电解电容和10kΩ电阻串联,上电瞬间RST为高电平,随后被电容下拉到低电平,实现上电复位;手动复位按键并联在电容两端。P0口内部是开漏结构,输出高电平能力很弱,所以LCD数据总线接在P0上时必须加8个10kΩ上拉电阻到5V,否则仿真图里看着正常,实物的高电平可能只有2V,LCD直接花屏。
晶振频率还影响串口波特率。如果后面要用虚拟终端打印ADC电压,建议把晶振设成11.0592MHz而不是12MHz,因为11.0592MHz能够被4800和9600整除,串口帧才不会累积误差。DHT11的延时函数按12MHz计算结果也兼容,差异在微秒级,不影响读取位宽。
3.2 ADC0809的信号输入和启动时序
ADC0809的8个通道可以扩展多路模拟量,这里只用IN0接电位器中心抽头,IN1留作备用,以后想加甲醛传感器时不用重画图。通道地址线A、B、C分别接单片机P1.0、P1.1、P1.2。ALE与START合成一个控制脚,由单片机输出正脉冲启动转换;EOC接P1.3,转换结束后输出低电平,单片机查询电平变化;OE接P1.4,置高电平时打开数据输出。CLK引脚必须送一路方波,典型频率范围在10kHz到1280kHz之间,Proteus里可以用信号发生器输出500kHz方波。
接线关系整理如下:
| ADC0809引脚 | 接到 | 说明 |
|---|---|---|
| VREF+ | +5V | 参考电压,决定满量程 |
| VREF- | GND | 参考地 |
| IN0 | POT-HG中心抽头 | 模拟PM2.5粉尘电压 |
| A/B/C | P1.0~P1.2 | 通道地址 |
| ALE/START | P1.5 | 启动转换脉冲 |
| EOC | P1.3 | 转换结束标志 |
| OE | P1.4 | 输出使能 |
| CLK | 500kHz方波 | 转换时钟 |
| D0~D7 | P0口经上拉 | 8位数据总线 |
这里特别提醒:不要直接拿单片机ALE引脚作为ADC0809的CLK。ALE频率是晶振的六分之一,12MHz下为2MHz,超过ADC0809最大允许值1280kHz,转换结果会乱跳。Proteus里放一个信号发生器最省事,参数设为500kHz、5V方波,DC Offset设2.5V或0V都行,只要高低电平满足TTL阈值。
3.3 LCD1602、按键和报警电路
LCD1602数据线D0到D7接P0口,RS接P2.0,RW接P2.1,E接P2.2。RW接地可以只写不读,省下一个引脚,但没法读取忙标志,所以每次写指令前必须做固定延时,仿真里如此,实物也一样可用。VEE通过10k电位器调整对比度,Proteus中调到约1V时显示最清晰。
报警部分用PNP三极管驱动有源蜂鸣器,单片机P2.3经1kΩ电阻接到三极管基极。Proteus的有源蜂鸣器模型不一定每台电脑都能发声,我通常在蜂鸣器两端并联一个LED,灯光闪烁表示报警已经触发。三个按键分别接P2.4到P2.6,按下拉低,用来调整PM2.5浓度报警阈值,按键消抖在仿真里可以省略,但代码里仍然保留10ms延时,方便之后移植到真实板子。
ADC0809的启动脉冲用代码控制比较干净,初始化后执行一段短脉冲:
void adc_start(void) { AD_ALE = 0; AD_START = 0; AD_ALE = 1; AD_START = 1; /* 高电平有效,持续10us */ AD_ALE = 0; AD_START = 0; }逻辑说明:ADC0809要求ALE下降沿锁存通道地址,START上升沿复位内部逐次逼近寄存器,下降沿启动转换。上面的顺序把地址稳定、锁存和启动合并在一起,可以避免单独控制ALE时出现通道窜位。
参数说明:启动脉冲高电平时间建议大于200ns,这里用两条空语句延时的宽度在微秒级,足够可靠。如果仿真中ADC不启动,先用虚拟示波器看ALE和START引脚波形,确认确实出现正脉冲,而不是程序运行到了某个死循环。
4. 单片机C程序、DHT11时序和PM2.5浓度换算
4.1 主循环与ADC0809多通道读取
主程序流程放在while循环里:启动ADC转换,等待EOC变低,读取8位数据,换算PM2.5浓度;再读DHT11得到温湿度;最后刷新LCD并扫描键盘。ADC0809的多通道切换靠地址线,读取IN0时地址必须是000。如果忘记切换,ADC可能保持上一次通道的数据,仿真中表现为调整电位器但显示值不变。
unsigned char adc_read(unsigned char ch) { unsigned char result; P1 &= 0xF8; /* 清低三位地址 */ P1 |= (ch & 0x07); /* 写入通道号 */ AD_ALE = 0; AD_START = 0; delay10us(); AD_ALE = 1; AD_START = 1; delay10us(); AD_ALE = 0; AD_START = 0; while (AD_EOC == 0); /* 等待转换完成 */ AD_OE = 1; result = P0; /* 从P0口读回8位结果 */ AD_OE = 0; return result; }逻辑说明:读取前清P1低三位再写通道号,可以有效防止其他引脚上的旧状态干扰通道选择。转换结束后OE置高才能打开输出锁存器,否则数据线仍然处于高阻状态,P0口会读到0xFF。
参数说明:ch的取值范围是0到7,对应IN0到IN7。本设计只用ch=0,以后扩展DHT11后备模拟方案时可以把ch改为1。ADC结果和电压的关系是电压 = adc_result * 5 / 255,参考电压变化时该比例必须同步修改。
4.2 DHT11读取函数的起始信号和位判断
DHT11单总线协议比较啰嗦。主机先把数据线拉低至少18ms,再释放并延时20µs;随后DHT11会拉低响应信号,之后连续输出40位数据:湿度整数、湿度小数、温度整数、温度小数和校验和。每一位的区分不在低电平而在高电平宽度:高电平约50µs是0,约70µs是1。
unsigned char dht11_read_byte(void) { unsigned char i, value = 0; for (i = 0; i < 8; i++) { while (DHT11_PIN == 0); /* 等待低电平结束 */ delay_us(40); /* 跳过0位的高电平前半段 */ if (DHT11_PIN == 1) /* 40us后仍为高,表示1 */ { value |= (0x80 >> i); while (DHT11_PIN == 1); /* 等待该位结束 */ } } return value; }逻辑说明:每一位起始都是50µs低电平,之后高电平宽度是区别位值的关键。程序先等低电平结束,然后延时40µs,此时如果是0位,高电平已经结束,读到低电平;如果是1位,高电平还在,读到高电平。这种写法比反复测量脉宽更稳定,尤其适合Proteus模型。
参数说明:delay_us(40)要按晶振频率实际调整。Keil C51在高优化等级下可能把空循环延时函数优化掉,导致DHT11永远读不到数据。解决办法是用关键字volatile修饰循环变量,或者在函数前使用#pragma NOINLINE避免内联,保证延时真实执行。
读取40位数据后,主程序需要校验:湿度高位加湿度低位加温度高位加温度低位,低8位必须等于校验字节。如果校验失败,显示上一次成功读数比显示乱码更有实际意义。我在实物调试时会连续读3次取中间值,确保偶发错误不会让LCD上的数据跳变。
4.3 用虚拟终端观察ADC原始值和浓度换算
仿真阶段建议不要只盯LCD,而是同时打开Proteus的Virtual Terminal,把ADC原始值、电压值和PM2.5浓度都用串口打出来。这样每拨一次电位器,能立刻看到哪一步换算发生变化,排查问题比来回刷新LCD快得多。
printf("raw=%03d, volt=%.2f, pm25=%.1f\r\n", adc_raw, pm25_volt, pm25);逻辑说明:Keil C51的printf默认走串口0,因此这条代码在仿真里就是在往单片机TXD引脚发送数据。虚拟终端必须和单片机的TXD、RXD交叉连接,波特率设成9600、8位数据、1位停止位、无校验。如果虚拟终端只显示空白,先检查串口初始化函数是否调用过,再检查晶振频率是否设置成11.0592MHz。
参数说明:%.2f和%.1f在Keil C51中需要开启浮点格式支持,如果不希望引入浮点库,可以把电压和浓度都放大10倍用整型发送,接收端再除以10,这样能省不少代码空间。
5. 仿真发散后的排查:阈值迟滞与虚拟标定
5.1 给报警阈值加迟滞,避免临界抖动
常见的报警逻辑是PM2.5浓度超过阈值就报警,低于阈值就关闭。仿真中电位器滑条在阈值附近微调时,ADC数值会来回跳,蜂鸣器和报警灯跟着忽闪忽闪。给阈值设置两个边界是最直接的办法。设报警阈值为100µg/m³,当浓度超过100时报警,低于90时解除,中间10µg/m³是迟滞区间:
if (pm25 > alarm_threshold) { alarm_enabled = 1; } else if (pm25 < (alarm_threshold - 10)) { alarm_enabled = 0; }逻辑说明:两个条件之间留出间隙,让系统在100到90之间保持之前的报警状态,从而消除边界抖动。10µg/m³这个值不是固定不变的,传感器噪声大时适当加大到15或20。
参数说明:报警阈值由三个按键调节,键值需要放全局变量里保存。仿真中可以通过虚拟终端打印报警标志来判断是否进入迟滞区间,而不必观察蜂鸣器能不能听到声音。
5.2 用电压表做虚拟标定
PM2.5浓度和电压的线性关系来自数据手册,但Proteus里电位器输出电压不一定精确到小数点后三位。比较稳妥的验证方法是在电位器中心抽头处放一个DC Voltmeter,读取当前电压,再对照虚拟终端输出的pm25字段。例如电压表显示2.5V,终端应该输出约250µg/m³;如果偏差超过5%,优先检查ADC_VREF宏定义和VREF+引脚接线。
实际温度湿度同理:DHT11在Proteus中给出的读数默认为环境仿真值,为了主动模拟高温高湿环境,可以断开DHT11的数据脚,用两个电位器分别接ADC通道临时顶替。定标完成后,记得把DHT11的数据脚重新连回P1.0,并把通道地址改回0。
5.3 从仿真到实物移植时的验证参数
仿真能验证逻辑,但传感器差异必须靠实物修正。GP2Y1010数据手册里的输出电压并不是完美直线,低浓度段灵敏度高,高浓度段趋于饱和,所以实物里建议用两段折线查表而不是单一满量程乘系数。第一段0到100µg/m³用斜率较大的公式,第二段100到500µg/m³降低斜率。将这个分段表写入常量数组后,再在Proteus里把电位器电压按分段节点回扫一遍,确认LCD显示值与预期值误差小于5µg/m³就能收工。
本文还有配套的精品资源,点击获取