简介:基于STM32与Proteus的智能家居环境监测系统设计,是一套面向嵌入式、自动化、电子信息类专业学生及初学者的完整仿真项目。系统以STM32微控制器为核心,通过四个独立按键切换温度、湿度、光照强度和气体浓度检测任务,数据在数码管实时显示并经由串口输出,参数越限时触发蜂鸣器声光报警。针对仿真环境缺少气体传感器的问题,采用滑动变阻器模拟气体浓度变化,兼顾功能完整性与教学可演示性。压缩包内共137个文件,整体约8.94MB,除C语言源码(.c/.h)和Keil工程配置(.uvprojx)外,还提供Proteus仿真工程(.pdsprj)、编译产物(.hex/.axf)、演示视频(.avi)及说明文档(.pdf),从源码到运行效果均有覆盖;源码按外设驱动、按键扫描、定时控制、串口通信等模块划分,方便逐段研读复用,适合课程设计、毕业设计或单片机项目原型开发。目前已有71人学习下载,既可直接运行验证逻辑,也可配合仿真深入理解传感器数据采集、人机交互和报警联动的完整链路。
1. 基于STM32与Proteus的智能家居环境监测系统:为什么我建议先仿真再买模块
做智能家居环境监测,很多人的第一步是去购物网站下单温湿度、光照、气体模块,等快递到了才发现:DHT11时序对不上、MQ-2的加热电阻烫手、光照模块的I2C老读不出来,排线插了一桌子,demo还是起不来。基于STM32与Proteus的智能家居环境监测系统设计这个题目,本质上是把硬件的“黑匣子”提前撕开——用STM32F103C8T6作为主控,在Proteus里把温湿度、光照、气体三路传感器的采集、显示、阈值报警逻辑全部仿真跑通,再决定要不要花钱买实物。对毕业设计来说,它可以让你在交方案前就把核心指标验证掉;对入门工程师来说,它能帮你把“代码问题”和“硬件问题”分开定位,翻车成本低很多。
2. 系统拆解与选型:三路传感器在Proteus里的仿真模型怎么选
2.1 温湿度、光照、气体传感器:仿真模型与实物型号的对应关系
在做选型之前,先把系统的数据流说清楚:传感器把物理量变成电压或数字信号,STM32通过ADC或GPIO读进来,经过校验和阈值判断后,送到LCD1602显示,同时用串口打印到Proteus的虚拟终端。这个数据流从仿真到实物都是一样的,差别只在于“传感器在Proteus里用什么元件代替”。
温湿度这块没有悬念,DHT11在Proteus元件库里直接就是DHT11,模型自带温湿度和时序响应。它用单总线协议,DATA引脚需要外接一个4.7k上拉电阻到3.3V,这个上拉在仿真里不能省——我曾经见过一个工程,DHT11数据偶尔读出255,查半天发现是把上拉电阻漏了,仿真里浮空引脚的表现比实物更玄学。
光照传感器是个容易踩坑的点。实物上很多人用BH1750这类I2C数字光照模块,但Proteus没有BH1750的现成模型,硬用I2C总线搭一个也不划算。常见做法是改用光敏电阻(在元件库搜LDR或Photoresistor),串联一个10k固定电阻分压,把中间抽头接到STM32的ADC引脚。旋动LDR的阻值,ADC电压就跟着变化,效果等价于光照强度改变。如果版本库里实在没有LDR,用电位器模拟光照电压也是常规操作,我在仿真阶段经常这么干,逻辑验证完全够用。
气体检测的实物首选MQ-2,Proteus里也有MQ-2元件。它的输出有两类:数字输出DO(配合板上比较器阈值)和模拟输出AO。仿真里建议直接读AO,经过负载电阻进ADC,因为数字输出的阈值是板级固定的,改成模拟量以后你才能在代码里调报警阈值。
2.2 主控引脚与ADC通道规划:PA0、PA1、PB0怎么分配
主控选STM32F103C8T6,原因很实际:Proteus对Cortex-M3的仿真支持成熟,元件库里直接搜STM32F103C8就能放出来,F1系列的HAL库资料满天飞,毕业设计和入门都不缺参考资料。它的ADC1有10个输入通道,我规划时只用了两个,留了余量。
我自己一般这样分引脚,可以直接抄:
| 功能 | 引脚 | 外设连接 | 说明 |
|---|---|---|---|
| 光照采样 | PA0 | ADC1_IN0 | 光敏电阻分压中点,0~3.3V |
| 气体采样 | PA1 | ADC1_IN1 | MQ-2的AO输出,0~3.3V |
| 温湿度数据 | PB0 | GPIO(开漏+上拉) | DHT11 DATA线 |
| 蜂鸣器报警 | PB1 | GPIO输出 | 报警声光提示 |
| 报警LED | PB2 | GPIO输出 | 报警指示 |
| LCD1602控制 | PB3、PB4、PB6~PB9 | GPIO输出 | RS、EN、D4~D7 |
| 继电器/风扇 | PA8 | GPIO输出 | 气体超标时联动通风 |
LCD1602的RW引脚直接接地,这样只需要控制线和数据线,刚好避开PB10和PB11——那两根留给USART1,接Proteus虚拟终端做调试输出。如果你用带I2C转接板的LCD1602,还能再省几个引脚,但仿真里PCF8574模型的时序反而会引入额外调试点,我一般不推荐在仿真里用I2C屏幕。
这里有一个ADC通道选择的细节:F103的ADC1_IN0固定对应PA0,ADC1_IN1固定对应PA1,引脚和外设通道是一一对应的。在CubeMX里配置时,不要只看通道号不看引脚,否则会出现“我以为采的是PA1,实际ADC1_IN0只能从PA0取数”这种低级但难查的错误。
2.3 显示与报警方案:为什么LCD1602比OLED在Proteus里更省心
显示方面,OLED在Proteus里的仿真支持并不好,SSD1306驱动模型不是每个版本都有,I2C时序在仿真里慢了快了都会花掉你大量的调试时间。LCD1602就稳得多,元件模型通常是LM016L,4位模式接两根控制线加四根数据线就能跑,字符显示直观,和绝大多数毕业设计的验收场景匹配。如果题目要求OLED,我的建议是先在外设抽象层留好接口,仿真用LCD1602,实物再用OLED,不要两边同步调。
报警方案我习惯分成两级:一级是蜂鸣器加LED的声光报警,一级是继电器驱动风扇的联动通风。蜂鸣器在Proteus里用BUZZER元件,注意有源蜂鸣器模型上电就响,必须用GPIO控制它的通断,不能一直给它高电平;继电器用RELAY模型,线圈接PNP三极管驱动,触点串在风扇回路里。
选型不是越复杂越显得高级,而是“仿真能复现、实物能迁移、代码能共用”。三路传感器在仿真和实物之间尽量选同型号对接,否则你会在两套硬件之间反复改代码,那是最浪费时间的。
3. Proteus仿真电路搭建:元件库对照表与三路传感器接线参数
3.1 Proteus元件库对照表:实物型号与仿真关键字的映射
这里把我在Proteus里常用的元件映射整理成一张表,按表接线基本不会在“找不到元件”上卡住。注意不同Proteus版本的库分类名称略有差异,我写的是常见路径,以你实际安装的版本为准。
| 功能 | 实物型号 | Proteus搜索关键字 | 接线要点 |
|---|---|---|---|
| 主控 | STM32F103C8T6 | STM32F103C8 | 加载hex文件,时钟设8MHz |
| 温湿度 | DHT11 | DHT11 | DATA接PB0,4.7k上拉到3.3V |
| 光照 | 光敏电阻GL5528 | LDR或Photoresistor | 与10k电阻串联分压,中点接PA0 |
| 气体 | MQ-2 | MQ-2 | AO经负载电阻接PA1,加热端接电源 |
| LCD | LCD1602 | LM016L | RW接地,4位模式接线 |
| 蜂鸣器 | 有源蜂鸣器 | BUZZER | 三极管驱动,GPIO控制通断 |
| 继电器 | 5V继电器 | RELAY | 线圈经三极管到地,触点接风扇 |
| 虚拟终端 | 无 | VIRTUAL TERMINAL | TX接PA9,RX接PA10,波特率匹配 |
3.2 最小系统搭建:电源、晶振、复位与虚拟终端
在Proteus里放STM32F103C8之后,第一步不是接传感器,而是把最小系统确认好。双击芯片,在Program File里加载编译好的hex文件——很多新手在Keil里编译通过就关掉了,没有勾选生成hex,结果仿真里芯片一动不动。在Keil的Options for Target里的Output页签,勾上Create HEX File,编译后工程目录下就有hex了。
时钟配置上,Proteus的STM32模型外接8MHz晶振,代码里的时钟树再倍频到72MHz。这里要注意:双击芯片打开的属性对话框里,如果需要填晶振频率,保持8MHz;CubeMX里的HSE也配置成8MHz Crystal/Ceramic Resonator,两边对不上会出现串口波特率偏差,虚拟终端输出乱码。
然后放一个虚拟终端,接在PA9和PA10上。Proteus的VIRTUAL TERMINAL默认波特率是9600,如果你的代码用115200,就要双击虚拟终端把BAUD值改掉。电源方面,仿真模型对VDDA、VSSA的处理相对宽松,但我还是习惯把VDDA接3.3V、VSSA接地,避免某些版本在ADC仿真时出现浮空导致的读数漂移。
这块的验证标准很简单:程序烧进去,虚拟终端能按预期打印输出,LCD初始化后第一行能正常显示字符,最小系统就成了。传感器接线如果出错,至少你能确认问题出在传感器链路上,而不是主控没跑起来。
3.3 三路传感器接线:分压电阻、负载电阻与上拉电阻的参数设置
光照分压电路我常用3.3V供电:LDR一端接3.3V,另一端接10k电阻到GND,中间抽头接PA0。LDR模型在Proteus里可以通过属性调整当前光照强度对应的阻值,仿真运行时你双击LDR改阻值,PA0电压会立刻变化。这个电路在实物上也成立,只是实物LDR的阻值变化范围更宽,可能需要按实际情况把10k换成4.7k或15k,仿真阶段不用太纠结。
MQ-2的接法需要稍微注意:它的AO端要经过一个负载电阻到地,在AO和GND之间取电压进PA1。这个负载电阻在实物模块上已经集成好了,仿真里需要自己放一个,一般用1k。加热端H接3.3V或5V都行,但要和传感器回路共地,否则电压全是浮的。MQ-2的输出电压随检测浓度升高而上升,具体电压范围在双击元件的属性里可以配置浓度,相当于给你一个虚拟的“标准气体浓度”旋钮。
DHT11的接法最简单,但有两个细节别漏:一是DATA引脚必须有一个上拉电阻到3.3V,仿真里没有这个电阻,读数据稳定性和噪声表现会变差;二是DHT11的电源引脚给3.3V,不要接5V,虽然实物DHT11在5V下也能工作,但仿真模型对5V供电的时序响应并不友好。
三条传感器链路都按0~3.3V的ADC量程设计,好处是代码里不需要做电压缩放,用同一个转换公式就能把两个通道的原始值换算成电压,这也是我刻意不接5V传感器供电的原因:仿真里归一化到3.3V,ADC的分辨率用满,阈值判断的精度更高。
4. STM32固件实现:CubeMX配置、ADC多通道与DHT11时序代码
4.1 用CubeMX生成工程:时钟、ADC、UART与GPIO的关键项
固件开发我用STM32CubeMX生成初始化代码,HAL库版本由CubeMX根据所选芯片自动匹配,不用单独去下载。打开CubeMX选STM32F103C8T6后,几个关键页签的配置如下。
SYS页签里Debug选Serial Wire,JTAG占用PB3、PB4等引脚,不关掉的话LCD的控制线没法用PB3和PB4。RCC页签选HSE为Crystal/Ceramic Resonator。时钟树里HSE输入8MHz,PLL倍频到72MHz,APB2总线挂的ADC时钟记得设到12MHz以内,超过ADC最大工作频率会导致转换结果整体偏移。
ADC1配置成Scan模式,Continuous模式关闭,转换数量设2,Rank1选Channel0(PA0),Rank2选Channel1(PA1),采样时间选55.5周期。这个采样时间在仿真里够用,实物上如果传感器输出阻抗较高,可以拉到239.5周期,代价是转换速度变慢。USART1开启异步模式,波特率115200,PA9、PA10自动映射。GPIO页签里把PB0设成开漏输出并内部上拉、PB1和PB2设成推挽输出、PA8设成推挽输出。DMA页签给ADC1添加一个循环模式的DMA通道。
生成工程后你会发现代码里已经包含MX_ADC1_Init、MX_DMA_Init和main里的DMA句柄声明,接下来要手写的是数据读取和应用逻辑。注意不要在MX生成的初始化循环里插入大段业务代码,初始化完成后回到main的while(1)里做应用逻辑。
4.2 ADC多通道采集代码:stm32 adc切换通道的两套写法和一个坑
ADC读取我直接贴一套能用的写法。CubeMX生成初始化函数后,在main.c里补充DMA缓冲区和转换完成回调。
// 两个通道的ADC原始值,由DMA自动搬运 __IO uint16_t adc_raw[2]; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc->Instance == ADC1) { // 每个通道对应的电压 = 原始值 / 4095 * 3.3V light_mv = adc_raw[0] * 3300 / 4095; gas_mv = adc_raw[1] * 3300 / 4095; } }主循环里启动转换:
while (1) { // 每轮启动一次2通道转换,DMA自动把结果搬进adc_raw HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw, 2); HAL_Delay(100); // 用 light_mv / gas_mv 做显示和阈值判断 }第一套写法是HAL库的DMA方式:初始化时DMA1通道1配置成循环模式,每次调用HAL_ADC_Start_DMA,DMA自动把ADC1两个通道的转换值搬进adc_raw数组。转换完成回调里,adc_raw[0]对应当前Rank1通道PA0,adc_raw[1]对应当前Rank2通道PA1。
第二套写法是标准外设库时代的经典方式:每次读之前手动切换通道,再触发单次转换,读EOC标志。如果手里是标准库代码,可以参考这个逻辑:
// 标准库风格多通道切换,仅作逻辑对照 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); light_raw = ADC_GetConversionValue(ADC1); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); gas_raw = ADC_GetConversionValue(ADC1);这里有两个stm32 adc切换通道的常见坑。第一个是HAL库不开DMA、手动切换通道时,很多人忘了在切换后重新触发软件转换,导致第二次读到的还是上一个通道的值;第二个是Scan模式配合DMA时,如果DMA传输宽度设成了字节而不是半字,低8位数据会丢失,读出来的数值永远是256的倍数。仿真中这种数值一眼就能看出异常。
4.3 DHT11时序读取:仿真的时序宽容度比实物高,但初始化不能省
DHT11的单总线时序看起来简单,但单片机引脚要在输出和输入之间来回切换,HAL库切换GPIO模式需要重新调用HAL_GPIO_Init,这一步的代码量比想象中大。读取流程分四段:主机拉低18ms触发、拉高20us、等待DHT11响应低电平、然后读40位数据。
先做一个微秒级延时函数,DHT11时序里有26us和70us级别的差别,HAL_Delay精度不够:
void delay_us(uint32_t us) { DWT->CYCCNT = 0; while (DWT->CYCCNT < (SystemCoreClock / 1000000) * us); }DWT延时在CubeMX生成的工程里需要手动使能,否则CYCCNT不计数,程序会死等:
void dwt_delay_init(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; }如果你的Proteus版本对DWT仿真支持不完整,延时函数会卡在while循环里,这时可以改用TIM2做us延时,计数频率设为72MHz,逻辑是等价的。
启动DHT11的数据触发逻辑:
void DHT11_Start(void) { GPIO_InitTypeDef gpio = {0}; // 先配置为输出模式,拉低至少18ms gpio.Pin = DHT11_PIN; gpio.Mode = GPIO_MODE_OUTPUT_OD; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, &gpio); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); delay_ms(18); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); delay_us(30); // 立即切换为输入模式,等待DHT11拉低总线 gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, &gpio); }然后读一个字节,用高电平持续长度判断数据位是0还是1:
uint8_t DHT11_ReadByte(void) { uint8_t value = 0; for (int i = 0; i < 8; i++) { while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_RESET); delay_us(40); if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET) { value |= (0x80 >> i); } while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET); } return value; }40us后判断电平高低是识别0和1的通用做法:26us的高电平是0,70us的高电平是1,卡在中间用40us做分界,实测和仿真都能得到稳定结果。完整读取40位后,需要校验第五个字节是否等于前四个字节累加和,校验不通过的数据直接丢弃,不要送给LCD刷新。
4.4 阈值判断、LCD1602显示与串口打印
数据到手后,业务逻辑就可以串起来了。先设定三个阈值:温度超过30度报警、气体电压超过2.0V报警、光照电压低于1.0V提示光照不足。阈值用宏定义放在源文件顶部,方便后期改成按键可调。
#define TEMP_ALARM 30 #define GAS_ALARM_MV 2000 #define LIGHT_LOW_MV 1000 // 主循环里的报警判断 if (temp >= TEMP_ALARM || gas_mv >= GAS_ALARM_MV) { HAL_GPIO_WritePin(ALARM_LED_Port, ALARM_LED_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(BUZZER_Port, BUZZER_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(ALARM_LED_Port, ALARM_LED_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(BUZZER_Port, BUZZER_Pin, GPIO_PIN_RESET); }LCD1602的初始化用标准的4位模式时序,PB3做RS、PB4做EN,D4到D7接PB6到PB9:
void LCD1602_Init(void) { delay_ms(50); LCD_SendNibble(0x03); delay_ms(5); LCD_SendNibble(0x03); delay_ms(5); LCD_SendNibble(0x03); delay_ms(5); LCD_SendNibble(0x02); delay_ms(5); LCD_WriteCmd(0x28); // 4位模式、2行、5x7点阵 LCD_WriteCmd(0x0C); // 显示开、光标关 LCD_WriteCmd(0x01); // 清屏 LCD_WriteCmd(0x06); // 写入后光标右移 }串口这边把fputc重定向到USART1,printf就能直接打印到Proteus虚拟终端:
int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 10); return ch; }主循环里每两秒打印一次当前状态,DHT11的采样间隔也要求不小于1秒:
printf("T=%d H=%d L=%dmV G=%dmV\r\n", temp, humi, light_mv, gas_mv);5. 仿真联调避坑:Proteus里跑不起来的5个常见排查
5.1 芯片不运行、点击Play后界面毫无反应
现象:Proteus启动仿真后,虚拟终端不打印任何内容,LCD不初始化,点击暂停也看不到CPU活动。
原因排查时第一件事是看芯片有没有加载程序文件。我在前面提到的Keil不勾选Create HEX File是这个坑最常见的原因;其次是加载了hex但芯片属性里的Clock Frequency和程序里的时钟树不匹配,程序卡在启动阶段的延时循环里。
解决:Keil Output页签勾选Create HEX File,编译后确认文件生成。回到Proteus双击STM32,在Program File里重新选择hex,同时把晶振属性保持8MHz。这个操作每次修改固件都要重复,养成“重新编译、重新加载hex、再启动仿真”的三部曲习惯。
5.2 虚拟终端乱码或完全无输出
现象:虚拟终端显示一串乱码,或者光标停在起始位置不动。
原因:乱码绝大多数是波特率不匹配,虚拟终端的默认值是9600,很多人的程序用115200,两边没有对齐;无输出则是引脚接错,TX和RX的交叉关系没有梳理清楚——STM32的PA9是USART1_TX,要接虚拟终端的RX,PA10是RX要接虚拟终端的TX。
解决:双击虚拟终端把BAUD改成和代码一致;检查TX/RX交叉接线。另外确认CubeMX里USART1的GPIO复用配置没有被后加的GPIO初始化覆盖,改完引脚后重新生成代码再编译。
5.3 DHT11读出的全是255或校验不过
现象:温度湿度显示255,或者校验和分析始终失败;修改DHT11元件属性里的温湿度值,屏幕读数纹丝不动。
原因:DHT11的DATA引脚浮空,或时序延时精度不够。排在第一位的是漏画4.7k上拉电阻,其次是延时函数用的HAL_Delay而非微秒级延时,时序判断全乱。
解决:在DATA引脚和3.3V之间补4.7k上拉电阻。如果想快速验证,也可以先用GPIO推挽输出模式把触发时序简化,确认DHT11模型的响应正常后再切回开漏模式加内部上拉。微秒延时用DWT实现,不要用软件循环空转,仿真暂停时软件循环的时间计算会不可信。
5.4 ADC读数固定不变,旋转电位器没有反应
现象:程序里打印的光照电压或气体电压是一个固定值,在Proteus里无论怎么调节电位器或改变元件参数都不变化。
原因:多半不是代码问题,而是电路问题。电位器的三个引脚接线不完整——仿真里电位器中间抽头悬空,ADC输入就浮在某个电平上,转动旋钮改变不了分压。另一种情况是ADC通道没选对,比如PA1电压接到了ADC1_IN0上,程序采的实际是另一个引脚的电压。
解决:先用Proteus的电压探针量采样引脚对地电压,确认旋钮转动时电压在变化;再核对CubeMX里通道号和引脚号的对应关系。PA0永远对应ADC1_IN0,PA1对应ADC1_IN1,中间不能有错位。
5.5 安装芯片包后依然搜不到STM32F103C8,许可证报错反复弹出
现象:元件搜索框输入STM32F103C8没有任何结果,或者只有别的厂商型号;更麻烦的是启动时就提示license没有正确配置。
原因:大部分情况是ST的Cortex-M3设备包没安装成功,或主程序版本和设备包版本不一致,元件索引没生成;少数情况是关键字输入不完整,输入STM32F103或STM32F103C8才命中。许可证弹窗则多半是授权文件路径和安装版本不匹配。
解决:打开软件的库管理功能,确认ST ARM的器件包处于已安装状态,装完退出并重开软件。全局搜索不到时,按分类路径找Microprocessor ICs -> ST Microelectronics -> STM32F1,顺着路径找比全局搜索更稳。许可证问题也一样,提示出现时先检查授权文件的安装路径和当前Proteus版本是否对应,用正规渠道的授权能省掉大量反复弹出错误的时间。
6. 从仿真到实物:验证三路数据的置信度与两个升级方向
仿真通过不等于项目交付,但要先学会验证仿真结果本身是对的。我的验证习惯分三层:第一层,用电压探针测PA0、PA1的对地电压,和程序里打印出来的电压对比,两个值在0.05V以内说明ADC链路没有问题。第二层,把DHT11元件属性里的温度和湿度改成几个已知值,比如25度、60%湿度,看LCD和串口显示的是不是同样的值,校验码逻辑能不能兜住错误数据。第三层,把气体浓度和光照强度从低到高逐步调节,观察报警触发点是否落在阈值附近,这一步能同时验证阈值判断和滞回逻辑。
两个升级方向值得做。第一个是按键调节阈值:用两个按键接在PA11、PA12上,一个切换调节项,温度、气体、光照三选一,一个加减设定值,LCD第二行实时显示当前值和目标值。这个功能无论毕设答辩还是工程演示都很有说服力,而且Proteus仿真里点按按键的交互非常直观。第二个是继电器联动风扇:气体超标时PA8拉高,继电器吸合,风扇模型开始转动,相当于一个自动通风系统。联动逻辑做成“超标自动开、回落后延迟30秒关”,用软件定时器而不是单纯的延时,仿真里也能看到风扇的实际启停节奏。
仿真环境有个无法回避的局限是信号太干净。我早期在仿真里把报警阈值调到很窄,LED和蜂鸣器反复闪,后来发现是真值连续抖动闯过阈值边界所致,加上滞回区间才稳住。实物比仿真更容易翻车:模块供电噪声会把ADC读数上下抖动几十毫伏,DHT11在快速连续读取时偶尔丢时序,这些都是仿真复现不出来的。所以我现在的习惯是仿真过了之后,先拿最小系统板加单个模块逐一调,再拼完整系统,不直接照着仿真全套采购。这个流程虽然慢一点,但每一步的失败都能定位到具体环节。希望帮到你。
本文还有配套的精品资源,点击获取