51单片机空气质量检测系统设计:从MQ传感器到Proteus仿真
2026/9/12 12:36:12 网站建设 项目流程

简介:一套基于51单片机的空气质量检测系统资料包,面向单片机课程设计、毕业设计及环境监测项目开发人群。系统以 STC89C51 为控制核心,集成温湿度、硫化氢、氨气、甲烷、一氧化碳传感器,配合 A/D 转换、按键、液晶显示与声光报警模块,可实时采集并显示气体浓度,对温湿度越限给出报警,适用于污水处理厂等场景的电子鼻设计。资源共 60 个文件,压缩包约 1.56MB,涵盖 C 源码(主程序、DHT11、ADC0832、LCD1602 驱动)、Proteus 仿真工程、原理图、流程图、元件清单及 PDF 预览等,其中位图流程图直观展示了 LCD1602、ADC0832 和主程序的执行逻辑,目录区分明确,便于对照学习。已有 152 人学习下载。使用者可借此快速复现完整功能,理解多传感器数据采集与单片机处理流程,也可根据原理图和清单自行搭建硬件,进行二次开发。

1. 基于51单片机的空气质量检测系统在课程设计里到底难在哪

很多人看到这个标题,第一反应是“接线多、代码长”,但真正卡住进度的不是这些。MQ系列气体传感器输出的是毫伏级模拟电压,51单片机内部没有ADC,你得先把信号调理和采集通道设计出来;Proteus元件库里又根本没有MQ-7、MQ-136这些现成模型,仿真时只能找替身;DHT11的单总线时序在仿真环境里经常跑不通。所以这个项目从原理图到源代码,走的是一条“先搭采集通道、再做传感器仿真替代、最后标定显示”的路线。下面从传感器选型开始,把这个方案一步步拆开讲,适合正在做51单片机课程设计的人,也适合要在Proteus里做多路模拟量采集的工程师参考。

2. 传感器选型与原理图设计:MQ系列输出的是模拟量,不是数字量

2.1 六路检测对象和对应的传感器选型

先明确检测对象:温湿度、H2S、NH3、CH4、CO,一共六路信号。温湿度用DHT11,单总线数字输出,一根线搞定;五种气体全部来自MQ系列半导体传感器。选型对照如下:

检测对象传感器型号输出类型典型量程功耗特点
温度/湿度DHT11单总线数字量0-50℃,20-90%RH低功耗
H2SMQ-136模拟电压1-200ppm加热功耗约0.9W
NH3MQ-137模拟电压5-500ppm加热功耗约0.9W
CH4MQ-4模拟电压300-10000ppm加热功耗约1W
COMQ-7模拟电压(需脉冲加热)20-2000ppm高低温循环加热

选型理由很直接:MQ系列在Proteus仿真里没有模型,但在实物制作中最容易买到,驱动电路简单,一个电阻分压加一个ADC就能读。需要注意MQ-7比较特殊,它的加热丝需要高低温交替供电,低温1.4V加热60秒、高温5V加热90秒,固定5V会带来较大的零点漂移。课程设计里如果不做加热控制,测出来的CO浓度只能看趋势,不能当绝对值用,这一点要在文档里写清楚。

2.2 MQ系列传感器信号调理电路的设计要点

MQ传感器的经典电路是一个分压结构:传感器内部的敏感电阻Rs与负载电阻RL串联,RL两端电压就是输出信号。原理图设计时三个参数最容易错。

第一,加热回路和检测回路必须分开走线。传感器的1、2脚(加热丝)单独接加热电源,3、4脚(敏感体)接RL分压网络,两个回路共地但在PCB上要靠近传感器引脚处汇合,避免加热电流脉动干扰取样电压。

第二,RL的取值决定了输出电压量程。RL越小,负载电压越低,分辨率越差;RL太大,传感器分压不足,输出容易饱和。MQ系列典型RL取1kΩ到4.7kΩ,建议原理图上画成可调电阻,调试时先用10k电位器,把洁净空气下的输出电压调到0.15V到0.3V之间,再换成固定电阻。给一个实用取值表:

传感器RL推荐值洁净空气下输出电压(Vcc=5V)
MQ-1362.2kΩ0.1-0.3V
MQ-1371kΩ0.15-0.35V
MQ-44.7kΩ0.3-0.6V
MQ-71kΩ0.1-0.2V(低温和高温段均值)

第三,如果用带LM393比较器的传感器模块,输出端是TTL电平,只能判断“超没超阈值”,读不到连续浓度值。标题要求做空气质量检测、显示浓度,所以必须从模块的模拟输出端(AO)取信号,原理图上标识清楚“接ADC通道”。

2.3 用CD4051扩展ADC采集通道:五个气体通道怎么接

51单片机没有内置ADC,常见的做法是外接ADC0832,但ADC0832只有两个模拟输入通道,五个气体传感器不够用。我一般用CD4051做8选1模拟开关,把五路传感器输出分时切换到ADC0832的CH0上,P1.0到P1.2三个IO口做通道地址选择。

CD4051地址C/B/A选通通道接入信号
000X0MQ-136(H2S)
001X1MQ-137(NH3)
010X2MQ-4(CH4)
011X3MQ-7(CO)
100X4备用/扩展
101X5备用

H2S是X0,开机默认采这一路,方便自检。CD4051的INH(禁止)脚接地,VEE接-5V或直接接地。如果输入信号可能到5V,VEE接地时输入范围是0到VDD,够用;要处理负电压信号才需要负电源。CD4051导通阻抗约80Ω,对后级ADC0832的输入阻抗影响不大,但在信号线上串一个100Ω电阻加100nF电容做RC滤波,能明显降低采样抖动。

2.4 DHT11温湿度、LCD1602和报警电路的工程化接线

DHT11数据线接P3.7,数据线和VCC之间接4.7kΩ上拉电阻,连接线尽量短于20cm。LCD1602标准接法:RS接P2.5、RW接P2.6、EN接P2.7、D0-D7接P0.0-P0.7,P0口必须接10kΩ排阻上拉,否则LCD花屏。液晶的VO脚(对比度)串一个10k电位器到GND,这个电位器在仿真里容易被忽略,但在实物上不加,屏幕经常显示不出字。

报警电路用有源蜂鸣器,接一个PNP三极管(S8550)驱动。基极串1kΩ电阻接P2.0,发射极接VCC,集电极接蜂鸣器负极,蜂鸣器正极接VCC。这比直接接IO口靠谱,因为蜂鸣器工作电流20mA左右,超过单片机IO口安全范围。每路气体阈值对应一个LED指示灯,LED串470Ω电阻接P2.1到P2.4,哪一路超标就亮哪一盏,方便快速定位是哪路气体报警。

3. 软件框架与核心代码:从主流程到C51源码的落地

3.1 主程序流程设计:采集、标定、显示、报警四件事

主程序不需要操作系统,一个while循环加一个定时器中断就够了。流程设计成四步循环:第一步切换CD4051通道并延时等待信号稳定;第二步读取ADC0832转换结果,每一路连续采16次去掉最大最小值再取平均;第三步把平均后的AD值换算成浓度并与阈值比较,决定继电器或蜂鸣器状态;第四步刷新LCD1602显示,然后进入下一路采集。整个轮询周期控制在200ms左右,按键扫描放在主循环的空闲位置,用软件消抖。

这个流程里有一个容易写错的细节:CD4051切换通道后不能立刻读ADC,模拟开关的导通建立时间加上传感器输出端的RC滤波时间常数,大概需要5到10ms。如果切换后立刻采样,读到的还是上一个通道的残留电压。做法是在切换函数里加一个10ms延时,或者把通道切换放在上次显示刷新之前,让延时被其他任务吃掉。

3.2 ADC0832读取函数与CD4051通道切换代码

这是整套源代码里最核心的部分。下面这段C51代码可以直接拷贝进Keil工程,芯片选AT89C52或STC89C52都可以。

#include <reg52.h> sbit ADC_CS = P1^3; // ADC0832片选,低有效 sbit ADC_CLK = P1^4; // ADC0832时钟 sbit ADC_DAT = P1^5; // ADC0832数据线(DI/DO复用) sbit MUX_A = P1^0; // CD4051地址位A sbit MUX_B = P1^1; // CD4051地址位B sbit MUX_C = P1^2; // CD4051地址位C // 读取ADC0832指定通道,ch: 0=CH0, 1=CH1 unsigned char read_adc0832(unsigned char ch) { unsigned char i, dat = 0; ADC_CS = 0; ADC_CLK = 0; ADC_DAT = 1; // Start位 ADC_CLK = 1; ADC_CLK = 0; ADC_DAT = 1; // SGL位,单通道模式 ADC_CLK = 1; ADC_CLK = 0; ADC_DAT = ch; // 通道选择,0或1 ADC_CLK = 1; ADC_CLK = 0; ADC_DAT = 1; // 释放总线,之后由DO驱动 for (i = 0; i < 8; i++) { ADC_CLK = 1; dat <<= 1; if (ADC_DAT) dat |= 0x01; ADC_CLK = 0; } ADC_CS = 1; return dat; } // 切换CD4051通道并等待输出稳定,ch: 0~7 void switch_mux(unsigned char ch) { unsigned char i; MUX_A = ch & 0x01; MUX_B = (ch >> 1) & 0x01; MUX_C = (ch >> 2) & 0x01; for (i = 0; i < 20; i++); // 空延时等待稳定 }

代码逻辑是先给CD4051三条地址线赋值,选通对应传感器通道,再启动ADC0832串行转换。ADC0832的数据读取顺序是从最高位开始的,串行时钟产生方式是把P1.4拉高再拉低,每次上升沿数据线上出现一位数据。DI和DO共用一根线时,读取阶段要把P1.5配置成输入模式,51的准双向IO口可以直接读,不需要切换方向的语句。

参数上需要注意三点。第一,ADC_CS低电平保持时间不能太短,两个转换周期之间至少间隔50μs,所以read_adc0832调用频率不要超过5kHz。第二,ADC_CLK高低电平时间由空指令延时决定,12MHz晶振下约1μs,符合ADC0832的时钟上限。第三,CD4051延时函数里的空循环是20条指令,实际延时约7μs,实测够用,如果传感器输出端RC滤波电容加得大,这里要加到100以上。

3.3 气体浓度标定与报警判断:AD值怎么变成ppm

ADC读回来是0到255的数字量,对应0到5V电压,但用户要看到的是ppm浓度,中间隔了一条标定曲线。MQ系列传感器在双对数坐标下,传感器电阻Rs与浓度近似线性关系,直接用线性公式算会越往高浓度偏差越大。课程设计里没有标准气体,常见做法是查表法加线性插值。

查表法逻辑:预先在原理图上通过电位器模拟几个已知电压点,记录对应的AD值和标称浓度,做成两个数组。程序里先判断当前AD值落在哪两个标定点之间,再做比例插值。这样比用一个固定的拟合公式灵活,换传感器批次时只需要改表的数值。

// 标定表:AD值与对应浓度(ppm),以MQ-136 H2S为例 unsigned char code adc_table[] = { 10, 40, 80, 130, 200 }; unsigned int code ppm_table[] = { 0, 5, 30, 80, 160 }; unsigned int calc_ppm(unsigned char adc_val) { unsigned char i; unsigned long ppm; if (adc_val <= adc_table[0]) return ppm_table[0]; if (adc_val >= adc_table[4]) return ppm_table[4]; for (i = 0; i < 4; i++) { if (adc_val <= adc_table[i + 1]) { // 线性插值公式 ppm = (unsigned long)(ppm_table[i + 1] - ppm_table[i]) * (adc_val - adc_table[i]) / (adc_table[i + 1] - adc_table[i]); return ppm + ppm_table[i]; } } return 0; }

插值用的是整数运算,先乘后除避免精度丢失。查表法比直接套公式更贴合传感器的实际曲线,代价是标定点越多越费ROM,五个标定点对大多数气体检测够用。报警判断放在浓度算完之后:P2.0蜂鸣器、P2.1到P2.4四个LED对应四路气体,哪路浓度大于等于对应阈值就输出低电平点亮,蜂鸣器用软件延时做1秒断续鸣叫,不要一直长鸣,否则现场噪音太吵。

3.4 DHT11温湿度采集和显示刷新的两个常见坑

DHT11单总线协议是以微妙级延时为基石的,51单片机在仿真里延时和实物不一致,最常见的问题就是读出来永远是0或255。写采集代码时,延时函数要用普通循环不要用NOP,因为仿真器的指令周期计算和Keil里看到的周期数一致,但不同版本的Proteus对延时精度模拟不一样,所以代码里要在读完数据后加一个50ms的采集间隔。

// DHT11读数在Proteus里的常见调试点 // 1. 起始信号低电平必须保持18ms以上 // 2. 读每一位时,先等IO拉低,再等40us后读高电平持续时间 // 3. 仿真不通过时,把延时整体乘以1.5或者2试试

显示刷新策略也不要每路都刷新,LCD1602写一次要几十微秒,如果50ms内写六路数据,主循环一半时间耗在写屏上。做法是维护一个显示缓冲数组,采集完一轮数据后一次性把六路数值格式化字符串写入,再调用LCD显示函数。这样按键响应速度会快很多,实测按键延时在100ms左右感觉不到卡顿。

4. Proteus仿真与排错:气体传感器模型怎么搭出来

4.1 Proteus里没有MQ传感器,用电压源和电位器替代

Proteus元件库翻遍了也找不到MQ-136、MQ-137、MQ-4、MQ-7的现成模型,这是所有做这个课程设计的人都会卡住的地方。仿真替代方案很成熟:用直流电压源加电位器模拟传感器输出电压,因为单片机和ADC关心的就是那个0到5V的模拟信号,传感器本身只是一个电压源。

具体做法是拖一个DC POWER SOURCE,用POT-HG或POT-LIN电位器分压,电位器滑动端接CD4051的X0到X3输入端。调整电位器阻值比例,输出不同电压,就等于模拟不同浓度气体环境。电位器的数值换算法:输出电压等于VCC乘以电位器比例,比如5V供电下要模拟1V,就把比例调到20%。这样仿真代码写完后,可以通过转动电位器验证ADC采样、浓度换算和报警阈值三个环节。

4.2 DHT11元件在不同Proteus版本里的差异

DHT11在Proteus 8.9及以上版本自带的库里有模型,直接搜DHT11能搜到。坑在旧版本和某些精简版里没有,此时替换方案是用DS18B20先验证温度采集流程,因为两个器件都是单总线协议,代码结构相似。替换时注意三点:DS18B20引脚定义和DHT11不同,VCC和GND要重新接;DS18B20只出温度,湿度通道在程序里保留但显示固定值;读时序的延时时间要按DS18B20的规范改,两个器件严格来说时序不同。

仿真原理图里DHT11的数据线要加上拉电阻,很多同学在仿真里忘记画这个4.7kΩ上拉,导致数据线上没有默认高电平,逻辑分析仪看波形全是乱的。

4.3 仿真共地、时钟频率和ADC参考电压的坑

Proteus仿真最容易出的问题集中在共地上。电压源、传感器模拟源、单片机和LCD1602必须共用一个GND网络,如果有两个地网络没有连在一起,ADC采集到的电压就是悬浮的,读数像抽风一样跳。检查方法是点击右下角的电压探针,分别量传感器输出点和ADC输入端,两点电压不一致,问题肯定出在CD4051和ADC0832之间的连线上。

第二个坑是晶振频率。Proteus里双击单片机可以改时钟频率,很多人默认是12MHz在跑,但代码里延时都是按12MHz调的,如果改成了11.0592MHz,串口时序会变,定时器中断周期也会偏。这个项目里串口只用来看日志,11.0592MHz和12MHz都行,但LCD和DHT11的延时算法在两种频率下表现不同,建议整个仿真和实物统一用12MHz。

第三个坑是ADC参考电压。ADC0832的参考电压就是VCC引脚,如果仿真里VCC用了5V电压源,ADC满量程对应5V;但CD4051输入的模拟电压如果超过5V,ADC读数会饱和到255。仿真时把电位器滑动端电压控制在0到4.5V范围内,别接近满量程。

4.4 用Proteus虚拟工具验证采集链路的正确性

仿真不出来的问题时,不要来回来去改代码,用虚拟示波器和电压探针定位。接好电路后,把电位器调到0V,运行程序,看LCD显示的H2S浓度是否接近0;再调电位器到0.5V,看AD值是否在25左右(0.5除以5乘以255)。如果AD值有变化但浓度显示不对,问题在标定表;如果AD值根本没变化,问题在CD4051地址切换或ADC0832时序。

Proteus的虚拟示波器接在ADC_CLK和ADC_DAT引脚上,能看到串行数据波形。正常工作时,CS拉低后CLK上应该有八个上升沿,DAT线上有一位一位跳动的数据。如果CLK没有波形,是ADC_CS没有拉低;如果DAT不动,是DI和DO方向配置错了。这一套检查流程下来,10分钟内能定位大多数仿真问题。

5. 让检测结果更可信:标定、老化与零点漂移处理

5.1 没有标准气体,用电压等效法做标定记录

实验室没有标准气体很正常,但不代表不能做定量标定。电压等效法的思路是:已知MQ传感器在某个浓度下的输出压大致范围,用精密电位器或信号发生器直接输出那个电压,让系统记录下对应的AD值,建立“电压等效浓度”标定表。这套做法做出来的数据虽然不能直接对标真实ppm,但能完整验证软件的量程、分辨率和报警阈值是否合理,答辩时能拿出数据和曲线,比空口说“传感器太贵没标定”有说服力得多。

实际操作步骤:在CD4051的X2通道输入端接一个精密电压源,从0.2V开始,每增加0.3V记录一次液晶显示值,做成表格:

输入电压(V)ADC读数显示浓度(ppm)理论浓度(ppm)
0.21000
0.84165
1.6823130
2.41227980

发现实测显示值和理论值偏差在10%以内,说明标定表建得合理;偏差太大就重新调整adc_table和ppm_table,不要直接改系数强行拟合。

5.2 在代码里预留零点和量程校准入口

标定表写死在code区里没问题,但实物上每个传感器个体差异明显,建议程序里留两个可校准参数:零点偏移量zero_offset和满量程修正系数k_scale。每次计算完ppm后做一个线性修正:

unsigned int corrected_ppm(unsigned int raw_ppm) { // zero_offset: 洁净空气下的零点偏移 // k_scale: 满量程修正系数,默认100表示不修正 unsigned long temp; if (raw_ppm > zero_offset) temp = (unsigned long)(raw_ppm - zero_offset) * k_scale / 100; else temp = 0; return (unsigned int)temp; }

这两个参数通过按键加P2.5进入校准模式:长按按键3秒进入零点校准,此时自动读取当前通道值作为零点;再按一次进入量程校准,用电压源输入标准电压,按键调整k_scale直到显示值匹配。这样换传感器时不用改代码,重新走一遍校准流程就行。

5.3 整机老化与长期运行时的零点漂移控制

MQ传感器有个特性:刚上电时读数会漂移,特别是MQ-7,高低温循环交替加热状态下,一开始的零点能漂移0.5V以上。程序里要在主循环开始前加一个预热延时,常见的做法是把预热计时做进主循环:读取次数累计30秒内不显示浓度,只显示初始化的WAIT标志。预热期间同时采集零点并取平均值作为零点参考。

老化是物理层面的处理。新传感器首次使用前,建议通电老化24到48小时,让敏感材料表面达到稳定状态。老化期间不要接单片机采集,直接用5V电源给加热丝供电即可。稳定后再做电压等效标定,标定值才能长期保留。如果发现同一电压下标定值和三天前差距超过15%,优先检查传感器的加热电压是否异常,而不是急着改标定曲线。

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

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

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

立即咨询