ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32充电桩环境安全监测系统:温湿度烟雾报警与Proteus仿真

STM32充电桩环境安全监测系统:温湿度烟雾报警与Proteus仿真 如果你最近去过地下车库或者商业综合体停车场大概率会看到一排排新装的充电桩。恰恰是这种没人值守、相对封闭、又潮湿的环境最容易积累安全风险充电枪线缆过热、电池热失控前冒出的烟、暴雨过后从排水口倒灌进来的积水每一样都可能从小问题变成大事故。我前后用STM32F103C8T6做了两版充电桩环境安全监测系统现在整理成开源项目配全套代码、原理图和Proteus仿真文件。整套系统做的事情很简单实时采集充电桩周边的温度、湿度和烟雾浓度分级给出预警和报警同时驱动蜂鸣器、LED和继电器——继电器可以联动排风扇或者直接切断充电回路。适合三类人看一是想学STM32、需要一个真实小项目的学生二是想给自家充电桩加一道保险的DIY玩家三是做充电桩配套设备、需要快速出一版参考设计的嵌入式工程师。1. 整体设计思路为什么充电桩要配环境监测1.1 充电桩不是“插上就能充”那么简单很多人觉得充电桩就是一个大号充电器里面是成熟的BMS和充电模块不需要额外监测。实际去过现场的人不会这么想。公共充电桩多数装在户外雨棚、地下车库、路边配电箱旁边环境差异极大。最常见的安全隐患有三个。第一是线缆和枪头过热。电动车充电电流动辄几十安培枪头里面的端子如果接触不良接触电阻一上去局部温度会迅速超过80℃外壳都烫手。第二是热失控前的烟雾。锂电池热失控不是瞬间发生早期一定会冒烟如果烟雾能被提前探测到哪怕只早一分钟都有机会切断充电、疏散人员。第三是潮湿和积水。地下车库在梅雨季湿度经常到85%以上配电箱和枪座容易结露绝缘性能下降如果车位地势再低一点暴雨积水还可能直接淹没设备。所以这套系统的核心思路不是去接管充电桩的充电逻辑而是做“旁路哨兵”独立供电、独立传感、独立报警环境参数异常时给出声光告警再通过继电器对外做一级联动。哪怕充电桩本身的保护设计失效这套系统也能兜底。1.2 监测参数怎么选、阈值怎么定我在最初设计时考虑过很多传感器火焰传感器、水浸传感器、振动传感器、CO传感器都有接触但最终基础版本只保留三个参数温度、湿度、烟雾浓度。温度是充电桩过热最直接的体现。环境温度阈值建议预警45℃、报警60℃。要注意这个阈值是“环境温度”而不是设备表面温度传感器要避免紧贴大功率发热器件否则会频繁误报。湿度主要防绝缘下降。预警80%RH、报警90%RH这两个值参考了大多数低压电气设备的运行环境要求。湿度超了不一定立刻出故障但是长期高湿会导致凝露、爬电、金属件锈蚀所以超过90%并且持续一段时间就要报警。烟雾浓度用MQ-2气体传感器它同时对油烟、液化气、酒精和烟雾有响应用来做“早期火灾”探测非常合适。报警阈值我建议用标定后的原始AD值不要直接用百分比。因为MQ-2模块一致性一般不同模块在同浓度下的输出电压可能有差异。后面软件部分我会详细讲标定流程。1.3 为什么选STM32F103C8T6而不是51、Arduino或者ESP32老实说这种监测系统用51单片机也能做但体验会差很多用Arduino开发最快但不适合深入学习也不适合后面接复杂协议栈。我做了一个简单的对比。方案价格外设资源调试手段仿真支持学习价值STC89C526元左右AD、定时器都有但资源紧张串口/SWD困难Proteus支持好偏基础Arduino UNO20-60元库丰富、上手快串口方便一般对硬件细节理解浅STM32F103C8T68-15元2路12位ADC、3路USART、I2C/SPI/TIM齐全SWD串口很成熟Proteus/Wokwi均支持体系完整适合入手ESP3215-30元外设多、带WiFi/蓝牙JTAG/USB/串口部分在线支持侧重联网应用选择STM32F103C8T6最关键的原因是3.3V逻辑电平和丰富的ADC资源。OLED、DHT11都是3.3V器件和STM32天然匹配。12位ADC读MQ-2模拟量精度足够4个定时器做延时和调度也绰绰有余。另外这块芯片的参考资料多到爆炸不管是CubeMX配置还是HAL库问题遇到坑基本都能搜到答案。如果未来要加WiFi远程上报方案上也留了扩展口加一块ESP8266-01S就行不会推翻整版设计。2. 硬件与原理图拆解从选型到画板的关键细节2.1 系统框架与引脚分配整套系统的数据流是这样的DHT11通过单总线把温湿度送给STM32MQ-2模块的AO引脚输出电压经过分压和滤波后进入ADCSTM32在OLED上刷新显示同时按状态机驱动LED、蜂鸣器和继电器UART1把实时数据打印到调试串口。引脚分配表格我直接放出来开源原理图里的网络标号和代码里的宏定义是完全对应的功能模块接口类型STM32引脚说明DHT11温湿度单总线PB12数据线板载4.7k上拉MQ-2烟雾AO模拟量PA1ADC1_IN1分压后输入OLED SSD1306I2CPB6SCL、PB7SDA地址0x3C或0x3D有源蜂鸣器GPIO输出PB0S8050三极管驱动绿色LEDGPIO输出PB1正常运行指示黄色LEDGPIO输出PB10预警状态指示红色LEDGPIO输出PB11报警状态指示继电器控制GPIO输出PA8低电平触发继电器模块调试串口UART1USARTPA9TX、PA10RX115200或9600SWD仿真调试SWDPA13SWDIO、PA14SWCLK4Pin插针引出这里有个细节PB6和PB7是I2C1的硬件引脚用作I2C是标准用法PB10和PB11是I2C2的引脚被我拿来当普通GPIO驱动LED完全没问题。DHT11放在PB12是为了避开UART引脚这样以后如果要接ESP8266UART2的PA2、PA3可以直接用不会打架。2.2 电源、传感器和驱动电路电源部分我建议采用“5V总线3.3V总线”的双轨结构。如果是从充电桩取电220V进线先接一个隔离AC-DC模块输出5V/3W例如HLK-PM01这类模块尺寸小、带隔离安全性比直接线性降压高很多。5V给MQ-2的加热回路和继电器模块供电再经过一个AMS1117-3.3给STM32、OLED、DHT11供电。不要小看MQ-2的功耗它的加热丝工作电流大约150mA如果直接从一个AMS1117-3.3后面取电会拖垮3.3V电压。所以MQ-2模块必须挂在5V总线上这点很重要。MQ-2模块的AO输出在5V供电下会接近0~5V而STM32的ADC输入范围是0~3.3V直接接会烧引脚。这一点很多人第一次都不注意。正确做法是加一个分压网络AO出来接20k对地再串联10k到PA1这样最高大约3.3V同时在PA1处并联一个104电容做低通滤波滤掉传感器输出上的高频抖动。仿真阶段如果你的MQ-2模块AO就是0~3.3V输出那这一段画不画分压都行但实物一定不能省。蜂鸣器我用的是5V有源蜂鸣器通过S8050三极管驱动PA8接1k电阻到三极管基极发射极接地集电极接蜂鸣器负极蜂鸣器正极接5V。GPIO输出高电平蜂鸣器响。LED都串330Ω限流电阻。继电器用一个1路5V低电平触发的光耦隔离模块IN接PA8继电器触点不要直接带220V负载而是去控制交流接触器线圈接触器再切断充电回路或启动排风扇这样强弱电彻底隔离安全上才说得过去。2.3 画原理图时容易漏掉的细节开源原理图我是用KiCad画的如果你偏好嘉立创EDA也可以直接迁移元件封装都不复杂。有几个细节是我打样之后才补上的现在都写在原理图里。BOOT0必须加10k下拉电阻。很多最小系统板把BOOT0直接接地也能跑但自制PCB上悬空是不行的容易受干扰导致启动模式异常。NRST上拉10k到3.3V并对地并100nF。8MHz晶振两个负载电容用22pF。STM32的每个VDD引脚旁边都要放104去耦最好再加一个10uF钽电容做储能ADC采样瞬间电流很大电源纹波会直接影响AD值。另外建议把SWD调试口做成4Pin插针VCC、SWDIO、SWCLK、GND四根线平时接ST-LINK下载调试都靠它。如果空间允许再引一个PA9/PA10的调试串口。有了串口你在现场调阈值、看实时数据会省太多事。3. 软件实现STM32驱动、状态机与报警逻辑3.1 工程搭建与代码结构软件基于STM32CubeMX生成HAL库工程用Keil MDK5编译。我推荐这么搭而不是用标准外设库是因为HAL库的ADC、I2C、UART配置在CubeMX里勾选就能生成省去大量初始化代码而且网上基于HAL库的问题案例非常多。如果你习惯用VS Code加EIDE插件和arm-none-eabi-gcc编译理论上一套代码也能编译通过但本文默认按Keil流程走。工程目录刻意做了分层不要一气呵成写在main.c里ChargingPile_Monitor/ ├── Core/ # 启动文件、中断、时钟配置 ├── Drivers/ # STM32F1xx_HAL_Driver ├── BSP/ # 板级驱动 │ ├── bsp_dht11.c/h # DHT11单总线驱动 │ ├── bsp_mq2.c/h # MQ-2 ADC采集与滤波 │ ├── bsp_oled.c/h # SSD1306驱动 │ └── bsp_alarm.c/h # LED、蜂鸣器、继电器控制 ├── APP/ # 应用层 │ ├── app_monitor.c/h # 采样调度、报警状态机 │ └── app_ui.c/h # 屏幕页面刷新 └── MDK-ARM/这样分层的好处是BSP层负责和寄存器、引脚打交道APP层只管业务逻辑。以后换传感器或者换屏幕只改BSP不动APP如果你拿去参加毕业设计这个结构也符合软件工程的评审要求。3.2 DHT11单总线驱动时序是最容易翻车的地方DHT11用的是单总线协议一根线既做控制又做数据回传。时序上分四步主机拉低至少18ms发起开始信号拉高20~40us后释放DHT11响应把总线拉低80us再拉高80us随后输出40位数据高位在前。每一位数据都是50us低电平开头随后高电平持续26~28us表示“0”持续70us表示“1”。判断逻辑就是等50us低电平结束后延时至位的中间再读电平。上电延时和响应检测必须分开写。很多人在“等待响应”阶段直接死循环一旦传感器没接好程序就卡死在那。我在开源代码里加了超时保护。uint8_t DHT11_ReadData(uint8_t *humid, uint8_t *temp) { uint8_t data[5] {0}; uint16_t time_out 0; DHT11_Start(); // 拉低18ms发起 if (!DHT11_CheckResponse()) return 1; for (int i 0; i 40; i) { time_out 0; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { if (time_out 1000) return 2; // 低电平超时保护 } delay_us(40); // 延时至位中间 uint8_t bit (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) ? 1 : 0; data[i / 8] (data[i / 8] 1) | bit; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { if (time_out 4000) return 2; } } if (data[4] ! (data[0] data[1] data[2] data[3])) return 3; // 校验和 *humid data[0]; *temp data[2]; return 0; }注意一个坑HAL库自带的HAL_Delay只能到毫秒级而DHT11读位需要微秒级延时。我用的方案是基于DWT数据观察点的微秒延时不占用定时器精度也够。static void delay_us_init(void) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; } void delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t cycles us * (SystemCoreClock / 1000000); while ((DWT-CYCCNT - start) cycles); }还有一点读DHT11期间最好先把中断关掉否则定时器中断或串口中断插进来时序就乱了。实测下来中断一多校验和就频繁失败。代码里在DHT11_Start前后加__disable_irq()和__enable_irq()但要注意别把SysTick中断关了那样HAL_Delay会失效。3.3 MQ-2模拟量采集ADC滤波与标定MQ-2的AO接到PA1之后软件上就是循环读取ADC。用HAL库最简单的查询模式就行不用开DMA因为采样频率不高。uint16_t MQ2_ReadRaw(void) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, HAL_MAX_DELAY); uint16_t raw HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); return raw; }直接读原始值一定很跳因为MQ-2内部是半导体气敏元件输出本身就有噪声。我用了“10次采样去掉最大最小再取平均”的软件滤波实测滤波后波动可以控制在±20个AD值以内。uint16_t MQ2_GetFiltered(void) { uint16_t buf[10]; for (int i 0; i 10; i) { buf[i] MQ2_ReadRaw(); HAL_Delay(5); } // 简单选择排序去掉最大值和最小值 for (int i 0; i 9; i) { for (int j i 1; j 10; j) { if (buf[j] buf[i]) { uint16_t tmp buf[i]; buf[i] buf[j]; buf[j] tmp; } } } uint32_t sum 0; for (int i 1; i 9; i) sum buf[i]; return (uint16_t)(sum / 8); }阈值标定是很多人忽略的一环。MQ-2在干净空气中的输出电压并不是0而且受温湿度影响有漂移。正确的标定流程是上电后先让模块预热3~5分钟连续采集50次取平均得到“干净空气基准值”base_adc。然后用打火机气体或者点燃的香靠近传感器记录一个“典型报警值”smoke_adc。实际报警阈值取base_adc (smoke_adc - base_adc) * 0.7比较合理留出余量。我在项目里把这三个阈值都做成了宏放在bsp_mq2.h里实装时只需要改三个宏然后重新编译。3.4 OLED显示与UI布局OLED用的是SSD1306驱动芯片0.96寸128x64分辨率I2C接口。软件驱动在开源库里已经写好底层支持硬件I2C和软件模拟I2C两种方式。我建议用硬件I2CPB6/PB7上拉了4.7k电阻HAL库配置为I2C1速率100kHz。UI布局我按“一行数据、一行状态”来排四行内容如下T:25.3C H:62% SMOKE: 45% STATE:NORMAL TH:1.2V/2.0VOLED显示浮点数有个技巧直接用sprintf打印%.1f在嵌入式环境里占资源而且有些编译器默认不启用浮点打印。我都是拆开打印sprintf(buf, T:%d.%dC, (int)temp, (int)(temp * 10) % 10)用整数运算代替浮点格式化速度和Flash占用都友好很多。UI刷新频率不要太高200ms一次就够了。OLED是I2C慢速设备如果每50ms刷一次屏幕会闪还会占用大量CPU。我在app_monitor.c里用一个uwTick计数做分时调度每200ms刷屏、每500ms采一次传感器、每1000ms打印一次串口日志互不干扰。3.5 分级报警状态机报警逻辑如果写成“一旦超阈值就响”是很容易误报的尤其MQ-2偶尔会冒尖峰。我采用了一个带消抖计数的三态状态机NORMAL、WARNING、ALARM。逻辑核心是温度、湿度、烟雾任一参数达到预警线时进入WARNING达到报警线并持续10个采样周期每秒1次就是10秒才进入ALARM触发蜂鸣器和继电器状态一旦进入ALARM不会立刻回到NORMAL需要连续20秒恢复正常读数才会自动解除防止噪声导致继电器反复吸合。typedef enum {STATE_NORMAL, STATE_WARNING, STATE_ALARM} AlarmState; AlarmState state STATE_NORMAL; uint8_t alarm_cnt 0; uint8_t recover_cnt 0; void Alarm_Update(float temp, float hum, uint16_t smoke_raw) { uint8_t over_alarm (temp TEMP_ALARM) || (hum HUM_ALARM) || (smoke_raw SMOKE_ALARM); uint8_t over_warn (temp TEMP_WARN) || (hum HUM_WARN) || (smoke_raw SMOKE_WARN); if (over_alarm) { if (alarm_cnt 10) alarm_cnt; if (alarm_cnt 10) state STATE_ALARM; recover_cnt 0; } else if (state STATE_ALARM) { // 报警解除需要连续20秒正常 if (recover_cnt 20) { state STATE_NORMAL; alarm_cnt 0; recover_cnt 0; } } else if (over_warn) { alarm_cnt 0; state STATE_WARNING; } else { alarm_cnt 0; state STATE_NORMAL; } // 根据state驱动外设 switch (state) { case STATE_NORMAL: // 绿灯蜂鸣器关继电器不动作 break; case STATE_WARNING: // 黄灯蜂鸣器每隔2s短响 break; case STATE_ALARM: // 红灯蜂鸣器快速响继电器吸合 break; } }蜂鸣器在WARNING状态用“响200ms、停1800ms”的节奏ALARM状态变成“响200ms、停200ms”听起来急促很多现场人员能明显区分。继电器低电平触发进入ALARM时HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET)吸合退出后释放。3.6 串口调试与WiFi扩展UART1我在CubeMX里配置成1152008N1重定向printf到串口方便调试。每秒钟打印一行[T25.3C H62% S045%] STATE:NORMAL仿真阶段这行数据直接接到Proteus的Virtual Terminal验证逻辑非常直观。如果你要加WiFi远程上报把ESP8266-01S接到UART2PA2、PA3波特率设成115200代码里每秒往UART2丢一行JSONsprintf(json, {\t\:%d.%d,\h\:%d,\s\:%d}\r\n, (int)temp, (int)(temp*10)%10, (int)hum, smoke_raw); HAL_UART_Transmit(huart2, (uint8_t*)json, strlen(json), 100);ESP8266出厂默认是AT固件用AT指令连上家里WiFi和MQTT服务器就能上报。这部分我在开源代码里留了app_mqtt.c的骨架你自己填服务器地址和Topic就行。4. Proteus仿真搭建先跑通逻辑再打板4.1 仿真能解决什么不能解决什么仿真最大的价值是把“传感器数据→状态判断→外设动作”这条逻辑链路完整跑一遍。你不需要焊板子不需要买元件就能验证代码里的阈值判断、消抖逻辑、OLED画面有没有错位、串口输出是否正常。对于学生党来说Proteus仿真截图还可以直接放进课程设计报告里比干巴巴的文字说明强太多了。但仿真的局限也很明显。它能模拟逻辑电平但模拟不了真实传感器的物理特性比如MQ-2预热漂移、DHT11的供电毛刺、模拟I2C受外部干扰导致的误码。另外Proteus里的DHT11模型响应是“瞬间”的而真实DHT11每次读取间隔要1秒以上仿真里你连续读也读得出来实物上就会失败。所以我的建议是先用仿真验证逻辑再用实物验证手感两者不是替代关系。4.2 Proteus建仿真工程的步骤我用的是Proteus 8 Professional版本以下步骤在这个版本上实测可行。第一步新建工程选择“New Project”芯片型号搜索“STM32F103C8T6”在微处理器类别里能直接找到。如果没有可能是你的库版本太旧需要用ST的Proteus库升级包。第二步放置外围元件。DHT11直接搜索“DHT11”能出来模型OLED的话如果你的Proteus版本支持SSD1306模型直接搜“SSD1306”或“OLED_SSD1306”版本较老或没装第三方库的话建议先用LCD1602模型代替或者干脆用Virtual Terminal查看串口数据因为仿真阶段我们更关心的是采样值是否进入预期范围。MQ-2用滑动变阻器POT-HG代替一端接3.3V一端接地滑动端接PA1转动变阻器就能模拟烟雾浓度从低到高的过程。第三步连线。注意ST芯片模型上VDD接3.3VVSS接地NRST通过10k电阻接3.3VBOOT0接GND。如果你的模型有VREF和VREF-引脚VREF接3.3VVREF-接地否则ADC读取全是0。OLED的SCL接PB6SDA接PB7两个引脚各加一个4.7k上拉到3.3V。第四步加载HEX文件。双击STM32芯片在“Program File”一栏选择Keil编译出来的hex文件位置一般在工程目录下的MDK-ARM/xxx/xxx.hex。Processor Clock Frequency这个参数非常关键后面单独说。第五步放置Virtual TerminalRX接STM32的PA9TX接PA10波特率设成和代码里UART1一致运行就能看到串口日志。第六步运行仿真。常态下绿色LED亮转动变阻器让PA1电压上升烟雾值超过预警阈值后黄色LED亮继续旋转超过报警阈值红色LED亮、蜂鸣器响、继电器模型吸合。4.3 时钟频率设置是仿真的最大坑在Proteus里跑STM32仿真十有八九会遇到HAL_Delay不准、串口乱码、OLED刷新过快或过慢的问题根因都是时钟频率对不上。CubeMX默认生成代码是外部8MHz晶振PLL倍频到72MHz。但在Proteus的STM32模型里芯片主频是由模型属性“Processor Clock Frequency”直接决定的跟原理图上画不画晶振关系不大。如果模型属性设成8MHz而代码里SystemCoreClock是72MHzSysTick的延时计算就会快9倍HAL_Delay(1000)实际只有111ms。我推荐的稳定组合是仿真阶段代码里使用内部HSI时钟并关闭PLL让主频稳定在8MHz同时把Proteus里的Processor Clock Frequency也设成8MHz。这样SysTick和串口波特率全都能对齐。实际打板时再改回外部晶振72MHzCubeMX里重新配置一下RCC就行。这个切换我在开源项目里用宏PROTEUS_SIM区分仿真和实物共用一份代码。4.4 仿真效果与实物差异仿真跑通后我的验证顺序是先看正常状态显示再人为让变阻器滑到高位触发预警和报警观察LED和蜂鸣器是否按状态机切换再用Virtual Terminal确认串口日志和屏幕数据一致。这些都通过后我会继续修改TEMP_WARN和SMOKE_ALARM等阈值宏重新编译加载hex确认改阈值没有引入逻辑回归。实物上要注意的差异点有几个DHT11上电后前1秒读出来的值往往是0所以我在代码里加了一个“上电丢弃第一次采样”的处理MQ-2模块刚上电的几分钟内基线会一直漂表现为AD值缓慢下降这是正常现象等稳定后再标定相关阈值OLED在真实硬件上如果I2C地址不对会一直黑屏记得先用I2C扫描程序确认地址是0x3C还是0x3D。5. 常见问题排查实录从编译到实装的坑5.1 ST-Link连接失败no target found这类问题这是STM32新手遇到最多的报错完整信息通常是“Error: no STM32 target found! If your product embeds Debug Authentication, please ...”之类。出现这个问题的原因分散在硬件、固件和软件三层。先把物理连接检查一遍SWDIO接到PA13SWCLK接到PA14GND必须共地目标板要有独立供电。BOOT0要确认是低电平如果BOOT0悬空或者被拉高芯片会进入系统存储器模式而不会正常启动Flash中的程序。很多自制板子就是这里翻车。如果连接硬件都没问题用STM32 ST-LINK Utility或STM32CubeProgrammer打开软件点连接软件会尝试读芯片ID。如果读不到ID试试“Connect Under Reset”模式也就是在复位期间建立连接。这个方法能救回很多“死锁”的芯片。克隆版ST-LINK在Win10/Win11下还经常遇到驱动问题用Zadig把驱动换成WinUSB再试。另一个可能性是调试接口被代码复用了。如果你在CubeMX里不小心把PA13/PA14配成了普通GPIO程序一运行SWD口就失效了。这属于“烧录一次后再也连不上”的典型案例处理办法是进入Bootloader模式BOOT0拉高用串口ISP把Flash擦掉再改代码。5.2 Keil编译与下载问题Keil5装的时候如果既要玩51又要玩STM32记得选“C51”和“MDK”两个组件然后分别和谐别共用破解文件。工程第一次编译报core_cm3.h找不到通常是Keil的Device Pack没装在Pack Installer里搜“STM32F1xx”安装即可。HEX文件没生成也是常见问题。默认情况下Keil只在输出目录生成axf文件要在Options for Target → Output里勾选“Create HEX File”编译后才能在Objects或Listings目录找到hex。仿真加载的就是这个文件。5.3 DHT11读数异常或校验失败DHT11最典型的现象就是串口打印出来全是255或者温度湿度一直不变。先检查供电DHT11支持3.3V到5.5V3.3V下也能工作但信号线上拉电阻一定要有。如果用的是裸传感器而不是模块必须自己加4.7k上拉否则读出来全乱。第二个原因是读数间隔太短。DHT11每次采样间隔要大于1秒代码里如果每200ms读一次传感器来不及刷新返回的永远是上一次数据。我给DHT11_ReadData加了个调用限制两次读取间隔小于1秒直接返回上一次缓存。第三个原因是中断打断时序。读取过程中如果被UART中断插入时延可能偏掉几十微秒位就判错了。解决方案在3.2已经说过读取期间关中断。点亮OLED之后还会出现一个问题OLED的I2C中断优先级如果比DHT11读取高也会造成干扰建议把DHT11读取期间的全局中断关闭。5.4 OLED不显示、花屏或者显示一半先分清是硬件问题还是驱动问题。用逻辑分析仪抓I2C时序最直接没有的话可以先跑一个I2C扫描程序看能不能扫到0x3C或0x3D。OLED模块背面的地址电阻决定地址大多数是0x3C但有些模块默认0x3D。我在Proteus仿真里也碰到过模型默认地址0x3D而代码写0x3C导致黑屏的情况一度以为驱动写错了。花屏通常和电源纹波有关。OLED驱动芯片内部电荷泵在升压时对电源要求较高如果3.3V供电线上没有104电容会出现刷新往一半花。在模块电源脚并联一个10uF电解电容能明显改善。5.5 ADC数值跳动剧烈ADC测MQ-2数值乱跳先做两件事第一PA1的输入信号有没有经过RC滤波我原理图里加的那个104电容不能省第二软件是否做了平均值滤波如果没有用3.3里的滤波函数。这两步做完跳动幅度至少下降70%。如果还跳检查给STM32供电的电源是否稳定USB供电在传感器负载波动时波动尤其明显改成DC电源或者加一级LC滤波。还有一点MQ-2在预热阶段读数本来就会持续漂移等3分钟再判断阈值。5.6 实装到充电桩之前的安全提醒这套系统最大功率路径是220V交流供电所有改造都必须断电作业。继电器模块只能用来控制接触器或者排风扇这类设备不要用继电器触点直接去切断大电流充电回路。监测板本身要装在充电桩的弱电腔体里和强电区域做好物理隔离。如果安装在户外或地下车库PCB打样回来先刷三防漆接线端子用防水接头避免凝露导致板卡短路。还有一点要特别说明这套系统是辅助监测不是替代充电桩原有保护的合规设备。它适合做预警、做联动、做数据采集但如果要用于商业运营场所必须再经过电气安全认证不能拿DIY板卡直接上线。6. 后续扩展方向与我的迭代体会6.1 扩展方向这套系统的硬件资源其实留了不少余量可以往上叠功能。最容易加的是水浸传感器地下车库充电桩最怕泡水在板子底部加两个探针做成干接点检测进水就报警成本几块钱。其次是火焰传感器可以检测红外火焰特征适合装在充电桩枪座附近和烟雾形成双保险。如果你想做多桩联网可以把RS485模块加上让多块监测板走MODBUS总线到汇聚网关网关再通过4G或者以太网把数据送到云端平台。这样就是一套小规模的充电站环境监测网络了。SPI引脚我恰好没有占用加一块SPI接口的以太网模块也很方便。6.2 我的迭代体会这套系统我前后迭代了三版。第一版直接在面包板上飞线逻辑验证通过后第二版画了PCB打样结果犯了两个经典错误MQ-2的输出没加分压直接接到了ADC上电就烧坏了一个引脚BOOT0悬空导致板子在下载器拔掉后偶尔起不来。这两个错误让我明白原理图上最不起眼的细节往往是实测时最要命的坑。后来把阈值都改成宏定义、加上消抖状态机、再补了Proteus仿真文件整个项目才算真正可以从零复现。如果你要拿这个项目练手我建议按“先仿真→再面包板→最后打板”的顺序来。仿真跑通代表你的逻辑没问题面包板跑通代表你的传感器接线没问题打板跑通才代表你的硬件设计没问题。千万不要跳过前两步直接画PCB尤其是第一次画STM32板子省下的打样钱远不够填调试时间。开源包里我放了完整的keil工程、KiCad原理图和Proteus仿真工程拿去就能直接开工。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进