news 2026/9/18 18:10:20

基于STM32的图书馆环境监测系统:硬件、代码与Proteus仿真

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于STM32的图书馆环境监测系统:硬件、代码与Proteus仿真

STM32做环境监测项目,说难不难,说简单也不简单。这次我把自己做的图书馆环境监测系统完整开源出来,包括STM32F103C8T6主控的完整代码、原理图、Proteus仿真工程,整套资料可以直接对着复现,也可以用在毕设、课程设计甚至产品原型里。这套系统主要解决的是图书馆这类室内公共空间里,温湿度、光照、烟雾三类环境参数的实时采集、显示与控制问题,采集到的数据会实时显示在OLED屏上,同时根据阈值自动控制风扇、补光灯和声光报警器,整个过程不依赖上位机,独立运行。

这套硬件方案还把传感器选型、继电器驱动、ADC采样链路都讲清楚了,仿真部分也做了完整的适配说明。说实话,图书馆环境监测这个题目非常适合作为STM32从入门到进阶的练手项目:传感器种类多,GPIO、ADC、I2C、定时器、中断几乎全用上了,代码逻辑又不算复杂,一个人两三周完全可以拿下。下面我把整个项目从设计思路到硬件原理图、再到代码实现和仿真验证的完整过程,尽量用说人话的方式讲明白。

1. 项目概述与核心设计思路

1.1 为什么做“图书馆环境监测”?

最早是朋友在高校图书馆做馆员,跟我抱怨纸质书库的温湿度控制全靠人工经验,空调开不开、除湿机什么时候启动,没有一个量化依据。北方冬天湿度能掉到20%以下,纸张发脆;南方梅雨季湿度能飙到80%以上,书页会发霉、起皱。更别说古籍特藏库,对光照和温湿度的要求远高于普通阅览区。所以表面上看这是个嵌入式项目,本质上是在解决一个真实的空间管理痛点。

顺着这个场景想下去,你会发现需求其实非常清晰:第一要能实时采集温湿度、光照强度和烟雾浓度;第二要在本地直观显示这些数据;第三要有自动控制能力,超温就开风扇、光线不足就补光、烟雾浓度异常就声光报警。这三条需求正好对上了一套典型的传感器采集加执行器控制的嵌入式闭环系统。更重要的是,这套方案不只适用于图书馆,档案馆、机房、实验室、仓库、恒温恒湿鱼缸、农业大棚这些场景,改一改传感器参数和阈值就能直接复用。

1.2 系统架构与硬件选型

主控我选了STM32F103C8T6,Cortex-M3内核,72MHz主频,64KB Flash、20KB SRAM,40引脚封装,三四块钱一片,资料多到看不完,CubeMX还能一键生成初始化代码。对于这个项目来说性能冗余很大,但正是因为冗余,后面想加无线模块、加屏幕、加按键都会很从容。

传感器选型上,温湿度用的是DHT11,光照用光敏电阻模块,烟雾用MQ-2。这三个模块几乎是最经典、最便宜的入门组合,加起来不到十块钱,却能把环境监测系统该有的几类物理量都覆盖到。当然你也完全可以把DHT11升级成SHT30,把光敏模块换成BH1750数字光照传感器,代码结构上我做了模块化封装,换传感器只需要改底层驱动文件,上层逻辑完全不用动。

系统整体架构分为四层:传感器采集层、主控处理层、显示与交互层、执行器控制层。采集层负责把温湿度、光照、烟雾浓度转换成电信号;主控层负责数据的读取、滤波、标定和逻辑判断;显示层用0.96寸OLED实时刷新数据;执行层用继电器控制风扇、补光灯,用蜂鸣器做报警。这个分层思路是嵌入式项目能不能快速迭代的关键,后面扩功能、换传感器、加通信都是往对应层里填代码,不会牵一发动全身。

项目开源文件里,我把文档、硬件、软件、仿真分别放在Docs、Hardware、Firmware、Simulation四个目录下。Hardware下面是立创EDA格式的原理图和PCB文件,转成PDF也放了一份;Firmware是完整的Keil工程;Simulation是Proteus工程文件,可以直接打开跑仿真。整个项目的定位是“开箱即用”,但是我希望拿到这套资料的读者不要只跑一遍演示就丢在一边,能把每个模块的实现逻辑吃透,才算真正有收获。

2. 硬件设计:原理图与PCB要点

2.1 最小系统电路与传感器接口设计

STM32F103C8T6的最小系统不复杂,但是每一颗电阻电容都有讲究。8MHz晶振配两个20pF负载电容,复位引脚接10k上拉电阻和104电容到地,BOOT0引脚直接下拉到GND让芯片从Flash启动,电源部分在VDD引脚就近放104去耦电容,再加一个10uF钽电容做低频滤波。这里我特别说一句,很多同学画原理图习惯把所有电容堆在一个角落,这在低速数字电路里可能没问题,但在ADC采样和继电器动作的项目里,电源去耦不到位会导致采样值跳来跳去。

DHT11的数据引脚接在PB12上,这里有两个细节容易被忽略。第一,DHT11是单总线协议,数据线是开漏结构,必须外接4.7k到10k的上拉电阻,否则读出来的数据永远是0xFF。第二,主控引脚要配置成开漏输出模式,这样在输出高电平时引脚实际是被上拉电阻拉高的,符合单总线释放总线的逻辑。我见过不少人在GPIO模式上翻车,用推挽输出去模拟单总线,短时间能工作,但遇到长线或干扰就容易时序错乱。

OLED用I2C接口挂到PB6和PB7上,这是STM32F103的I2C1引脚。SSD1306驱动芯片正常工作需要有上拉电阻,4.7k比较合适。OLED模块上一般已经焊了上拉,但如果用杜邦线延长超过10厘米,建议在主控端再补一组上拉,否则屏幕偶尔会花屏。

MQ-2模块的处理要格外小心。这个模块内置了一个LM393比较器,可以输出数字信号DO,同时还有一路模拟电压AO。我的接法是AO接PA1做ADC采样,DO接PB1做快速阈值中断输入。这里有一个很多人踩过的坑:MQ-2模块如果5V供电,AO输出范围是0到5V,而STM32的ADC输入范围是0到3.3V,直接接上去轻则采样值长期满偏,重则烧坏引脚。我在原理图里加了一组分压电阻,R1取20k、R2取33k,5V经过分压后最大输出电压约为3.1V,给ADC留了安全余量。计算过程很简单:Vout=5×(33/(20+33))≈3.11V。

2.2 ADC采样信号链路的完整性

ADC采样看起来就是把传感器输出接到PA1引脚,但实际工程里信号链路的完整性对测量精度影响很大。首先,所有模拟信号的地线要统一接到一个模拟地网络,再通过单点连接的方式接到数字地上,这样继电器、蜂鸣器工作时产生的地弹噪声不会直接叠加到传感器信号上。其次,传感器供电建议从3.3V经过一个LC滤波后再供给,避免DHT11和光敏模块之间通过电源轨互相干扰。

光敏电阻模块的接线简单,AO接PA2,DO接PB0。这类模块有个特点是输出会受供电电压影响,供电稳不稳直接决定测量值可不可信。我实际测试过,用USB供电和用独立5V适配器供电,光敏模块的输出电压在同一光照下能差出0.3V左右。所以正式版原理图里我用了AMS1117-3.3稳压芯片,5V适配器进来先经过稳压再给传感器供电,效果好了很多。

执行器驱动部分,继电器不能直接接在GPIO上,原因很简单,STM32引脚最大输出电流约20mA,而继电器线圈吸合电流通常需要50到100mA,直接驱动会烧引脚。原理图里我用了一个S8050三极管做放大,GPIO通过1k电阻控制基极,线圈反向并联1N4007续流二极管。这个二极管非常关键,继电器断电瞬间会产生一个反向电动势,没有续流二极管的话,轻则导致主控复位,重则击穿三极管。蜂鸣器同样采用了三极管驱动,用PNP管做低电平触发,和继电器的NPN方案错开,方便软件上做逻辑区分。

电源部分我统一从5V适配器取电,然后分成两路:一路直接给继电器和蜂鸣器的驱动级供电,另一路经过AMS1117降到3.3V给主控和传感器供电。继电器驱动级和逻辑级电源分开,是为了避免继电器吸合瞬间的大电流把3.3V拉垮,这个经验是从实际故障里总结出来的。

2.3 PCB布局与打板经验

原理图画完之后转PCB,布局有几个原则性建议。传感器接口器件放在板子边缘,方便接外设;继电器放在板子一角,远离DHT11,防止继电器线圈的磁场和热量干扰温湿度测量;蜂鸣器不要贴近晶振,否则蜂鸣声的机械振动会传导给晶振导致主频抖动。DHT11本身建议焊接成插件并把感应头露出PCB之外,不要紧贴PCB表面,这样可以减少板上其他器件发热带来的温漂。

电源走线要加粗,至少40mil以上。模拟信号线尽量短,避免和继电器控制线并行。我在第一版PCB上因为贪图走线方便,把ADC输入线绕到了继电器下面,结果继电器一动作,ADC采样值就跳几十个LSB。后来重新拉线,把模拟线缩短并包地处理,问题就消失了。

打板的话建议用立创EDA直接下单,5块钱5片,一周内到货。板子拿到手先别上芯片,用万用表量一下3.3V对地有没有短路,再量一下各关键节点的电压是否正确。我习惯先焊电源部分,确认稳压输出正常后再焊主控,最后接传感器和外设,这样排查问题范围会小很多。

3. 软件架构与核心代码实现

3.1 工程结构与主流程设计

软件工程用STM32CubeMX加Keil MDK搭建,HAL库版本1.8.0以上即可。CubeMX里需要配置的资源有GPIO、ADC1、I2C1和SysTick定时器。DHT11的单总线时序我放在普通GPIO上直接模拟,没有用额外的定时器输入捕获,因为DHT11的时序宽度是微秒级别,SysTick延时足够了。

工程代码按功能模块拆分,每个模块一个.c和.h文件:dht11.c负责温湿度读取,oled.c负责屏幕显示,adc_util.c负责多通道ADC采样和滤波,control.c负责阈值判断和控制逻辑,bsp_led.c和bsp_buzzer.c负责指示灯和报警器。这样一个典型的嵌入式工程结构,好处是编译时间短,逻辑清晰,遇到问题能快速定位到具体文件。

主循环的逻辑非常简单,伪代码如下:

while (1) { // 读取温湿度,DHT11两次读取间隔至少1秒 if (s_tick - last_dht11_read >= 1000) { dht11_read(&humidity, &temperature); last_dht11_read = s_tick; } // 每200ms采集一次ADC并做均值滤波 if (s_tick - last_adc_sample >= 200) { smoke_voltage = adc_util_read_avg(SMOKE_ADC_CH); light_voltage = adc_util_read_avg(LIGHT_ADC_CH); last_adc_sample = s_tick; } // 刷新OLED显示 if (s_tick - last_display >= 500) { oled_show_all(temperature, humidity, smoke_voltage, light_voltage); last_display = s_tick; } // 执行控制逻辑 control_update(temperature, humidity, smoke_voltage, light_voltage); }

这里有一个很重要的设计决策:所有外设的读取和刷新都放在主循环里用时间片轮询方式调度,不用复杂的RTOS。因为系统状态机很简单,任务只有四五个,用时间片轮询足够稳定,还能避免多线程带来的共享资源竞争问题。等以后要加网络通信、加多级菜单交互,再迁移到FreeRTOS也不迟。

3.2 DHT11单总线驱动细节讲解

DHT11是整个项目里最容易让人抓狂的外设,核心在于单总线协议对时序要求比较严格。读数据的完整过程是:主机先把数据线拉低至少18ms,让DHT11识别到开始信号,然后释放总线并延时20到40us,再切换成输入模式等待DHT11的响应。DHT11正常会先拉低80us表示响应,然后再拉高80us准备发送数据。

紧接着就是40位数据,每位数据都由一个50us的低电平和一段高电平组成,高电平持续26到28us代表逻辑0,持续70us代表逻辑1。40位数据分别是湿度整数、湿度小数、温度整数、温度小数和校验和,校验和的规则是前四个字节相加取低八位,如果等于校验字节则数据有效,否则丢弃本次读取。

DHT11读取的完整驱动函数如下:

uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature) { uint8_t buf[5] = {0}; uint8_t i, j; // 主机拉低至少18ms,触发DHT11发送数据 HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); delay_ms(20); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); delay_us(30); // 切换为输入模式,等待DHT11应答 GPIO_InitTypeDef gpio = {0}; gpio.Pin = DHT11_PIN; gpio.Mode = GPIO_MODE_INPUT; gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, &gpio); // 等待低电平响应,超时返回失败 if (WaitPinLevel(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET, 100) != HAL_OK) { SetDHT11OutputMode(); return 1; } if (WaitPinLevel(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET, 100) != HAL_OK) { SetDHT11OutputMode(); return 2; } if (WaitPinLevel(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET, 150) != HAL_OK) { SetDHT11OutputMode(); return 3; } // 依次读取8个字节里的每一位 for (j = 0; j < 5; j++) { for (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) { buf[j] |= (0x80 >> i); } while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) == GPIO_PIN_SET); } } // 恢复为输出模式,准备下一次读取 SetDHT11OutputMode(); // 校验和验证 if ((uint8_t)(buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) { *humidity = buf[0]; *temperature = buf[2]; return 0; } return 4; }

这段代码踩过的坑我总结成三条。第一,读取完数据后一定要把GPIO恢复成输出模式并拉高,否则下次读取时总线电平不确定,DHT11不会应答。第二,微秒级延时函数在设计时要注意,如果使用HAL_Delay的话最小单位是1ms,完全不够用,需要自己基于SysTick写一个delay_us函数。第三,等待电平跳变时一定要加超时机制,因为DHT11一旦损坏或者接线不良,read函数会卡死在while循环里,整个系统就像死机了一样。我加了一个带计次上限的等待函数,超时直接返回错误码,问题就好排查多了。

3.3 OLED显示与ADC多通道采样

OLED驱动基于经典的SSD1306,我用的是I2C接口方式。初始化流程包括打开显示、设置显示时钟分频、设置复用率、设置显示偏移、开启显示等,一般直接用成熟的驱动库代码就行。显示内容上,第一行显示温度和湿度,第二行显示烟雾浓度和光照状态,第三行显示系统运行状态。

OLED显示频率不需要太快,500ms刷新一次足够,因为人眼对屏幕内容的变化感知在100ms级别,再快只会浪费CPU周期。我还加了两个小设计:超温时屏幕上显示的温度会加上感叹号,报警状态时屏幕会变亮,这些细节通过改变OLED的对比度寄存器实现。

ADC部分,STM32F103的ADC1可以配置多个通道,我用的是PA1采烟雾电压、PA2采光照电压。每次采样连续采集10次,去掉最大值和最小值后取平均,这样处理之后采样值非常稳定。ADC采样时间配置为55.5个周期,虽然在快速变化的信号上这个采样时间偏慢,但对于烟雾和光照这类缓变信号完全够用。电压计算公式为:voltage = adc_value × 3.3 ÷ 4095,这个4095对应12位ADC的最大值,很多新手容易写成4096,虽然数值上差别不大,但严格的转换应该用4095。

3.4 控制逻辑与阈值设置

控制逻辑是整个系统的灵魂。如果只是简单地对阈值做比较,继电器会在临界点附近频繁吸合断开,不仅噪声大,还会缩短继电器寿命。我的做法是加入滞回控制,也就是到达上限后必须低于下限才会复位,中间留出2℃左右的回差。比如温度控制:温度升到30℃开启风扇,降到28℃才关闭风扇,这中间的2℃回差就把振荡问题消除了。

烟雾报警的逻辑更严格一些。因为MQ-2传感器刚上电的前几分钟输出会漂移,所以我做了一个开机预热流程:系统启动后前60秒不参与报警判断,只更新显示,同时记录当前烟雾电压作为零点基准。正常运行时的报警阈值是“零点电压+0.5V”,这样在不同环境下都能自适应。光照控制相对简单,光照电压低于某个阈值就打开补光灯,高于阈值再关闭,也做了0.3V的回差处理。

控制输出的实现全部由control.c模块统一管理,传感器读取和输出控制分层隔离。这样做的好处是,如果以后想改控制策略,比如温度改成PID控制风扇转速,只需要改control.c,其他模块完全不用动。

4. Proteus仿真搭建与验证

4.1 仿真工程搭建步骤

完整的Proteus仿真工程我放在了Simulation目录下,这里说一下搭建过程。Proteus版本建议8.9以上,我用的是8.13。新建工程后,在元件库里搜索以下几个关键元件:STM32F103C8、DHT11、LM016L、POT-HG、LED-BIBY、SOUNDER、RESPACK。

一个让很多人困惑的点是Proteus里没有SSD1306的I2C OLED模型。我仿真方案里用的是LM016L这个字符型LCD来替代OLED,代码里做了一个显示驱动抽象层,通过宏定义切换:

#define DISPLAY_USING_OLED 1 #define DISPLAY_USING_LCD 0

仿真工程里把宏改成使用LCD,就能显示同样的内容。这样做有个额外的好处,验证了显示驱动层的可移植性,以后换屏幕器件成本很低。

Proteus中STM32F103C8的模型需要导入.hex文件才能仿真。先通过Keil编译工程生成hex,然后在Proteus里双击芯片,在Program File一栏选择生成的hex文件。Crystal Frequency填8000000,对应板上的8MHz晶振。POT-HG电位器用来模拟烟雾传感器和光敏电阻的模拟电压输出,通过调整电位器旋钮就可以改变ADC输入电压,从而模拟环境参数变化。

4.2 仿真代码适配与易踩的坑

仿真环境下的代码和实物代码有几个关键差异。第一,仿真中不需要处理DHT11的上拉电阻,Proteus模型内部已经处理好了,所以如果你在实物上遇到DHT11读取失败,不要直接怀疑仿真逻辑。第二,仿真中GPIO翻转速度远快于实物,我曾经遇到过因为延时函数写得不准确、在仿真里能跑而实物不工作的情况,所以代码里的所有延时我都用了基于SysTick的微秒级延时函数,保证时序一致。

第三,Proteus仿真STM32的ADC输入范围要注意,仿真中如果给ADC引脚接入超过3.3V的电压,采样值会饱和为4095,不会烧毁仿真芯片,这会掩盖实物的分压问题。因此我在仿真中特意保留了ADC输入分压电路,确保仿真流程和实物一致。

通电仿真时还要注意一个性能问题:Proteus仿真STM32本身比较吃CPU,如果打开实时帧率模式又会变慢。建议把Proteus的帧率限制在每秒20帧,仿真运行速度反而更稳定。另外,如果仿真过程中DHT11读不到数据,可以先检查芯片有没有导入hex文件,很常见的错误是把hex导入了LCD模型上。

4.3 仿真验证流程与测试用例

仿真工程的验证流程我按五步走。第一步,运行Keil工程编译生成hex,确认0错误0警告。第二步,在Proteus中加载hex并启动仿真,观察LCD屏幕上温度和湿度是否正常显示。第三步,拖动温湿度传感器的滑动条改变数值,观察显示是否同步更新。第四步,调整烟雾电位器的输出电压,观察报警器和蜂鸣器是否触发。第五步,调整光照电位器,观察补光灯的开关逻辑是否正常。

我习惯用表格把每一个测试场景记录下来,方便对比和排查:

测试场景操作方式预期结果实测结果
正常运行上电后观察温湿度、电压值刷新正常2秒内显示稳定
超温报警温度变量设为32℃风扇开启,屏幕提示温度高风扇动作正常
烟雾报警调整烟雾电位器超过阈值蜂鸣器长鸣,报警灯亮1秒内进入报警态
光照不足调整光照电位器低于阈值补光灯打开补光灯正常
阈值恢复反向调整各变量各执行器恢复正常回差逻辑有效

从仿真结果来看,整个系统的逻辑链路是完整的,传感器、主控、执行器闭环能够在Proteus环境中正确响应。但我也要说句实话:仿真通过只代表逻辑层没有问题,不代表实物可以高枕无忧。

4.4 仿真与实物的差异分析

仿真永远替代不了实物验证。DHT11在仿真里是一个理想模型,没有任何电气噪声和信号完整性干扰,但真实的DHT11会被电源纹波、线缆长度、上拉电阻阻值甚至空气流动影响。ADC采样在仿真中极其干净,而实物的模拟信号会叠加各种干扰。继电器在仿真中只是一个符号,但实物继电器动作瞬间的电流冲击和磁场干扰是真实存在的。

所以我的建议是:先用仿真验证逻辑,再用实物验证时序和信号完整性。仿真阶段主要发现逻辑设计问题,实物阶段主要发现电路设计问题。两者的关系是互补,不是替代。

5. 常见问题与调试技巧实录

5.1 传感器与显示问题速查

做这个项目遇到问题最多的集中在三个模块,我把典型现象和解决方法整理成表格:

故障现象可能原因解决方案
OLED屏不亮或白屏I2C地址不对,或初始化时序问题扫描I2C地址确认器件地址,0.96寸OLED通常是0x3C
OLED偶尔花屏I2C上拉电阻过小或线路过长检查上拉电阻,杜邦线长度控制在10cm内
DHT11读到的湿度永远是0引脚上拉电阻缺失或GPIO模式不对确认DHT11数据引脚有4.7k到10k上拉,引脚配置为开漏输出
ADC采样值反复跳变电源纹波大或采样未加滤波改用均值滤波,检查3.3V去耦电容是否靠近引脚
继电器频繁吸合断开阈值没有滞回区间代码中增加2℃或0.3V的回差处理
MQ-2刚上电报警传感器预热期间输出漂移增加开机预热60秒,期间禁用报警逻辑
蜂鸣器声音很小驱动三极管基极电阻偏大基极电阻改为1k,确保蜂鸣器工作电流足够

遇到问题第一步永远是把现象描述清楚,第二步缩小范围。比如OLED不亮,先用逻辑分析仪看I2C总线上有没有波形,再看应答位是否正确,最后再看代码初始化顺序,一层层排查比拿着万用表乱戳效率高得多。

5.2 继电器干扰与电源稳定性

这个项目里最典型的复杂环境干扰来自继电器。继电器吸合瞬间的浪涌电流会通过电源轨传导到传感器,导致ADC采样值跳变、OLED闪烁。我实测过几次,解决办法有三个。第一,继电器驱动级独立供电,不要把继电器电源和传感器电源用同一个LDO。第二,继电器线圈必须并联续流二极管,这个是原理图层面的硬性要求。第三,主控程序和ADC采样要避开继电器动作的瞬间,可以在控制函数发出继电器切换指令后延时20ms再进行ADC采样,这个思路叫软件避让。

如果想让系统更稳定,可以把继电器换成固态继电器或者MOS管开关。固态继电器没有机械触点,不会产生电弧干扰,但是成本和导通压降会高一些。对于图书馆环境监测这种每天动作不是特别频繁的场合,普通电磁继电器加续流二极管完全够用。

5.3 开源工程目录说明与二次开发建议

整个开源工程我按下面的目录组织:

LibraryEnvMonitor/ ├── Docs/ # 项目文档、芯片手册、README ├── Hardware/ │ ├── Schematic/ # 立创EDA/PDF格式的原理图 │ └── PCB/ # PCB源文件和Gerber文件 ├── Firmware/ │ ├── Core/ # CubeMX生成的启动代码 │ ├── Drivers/ # HAL库驱动 │ └── User/ # 用户代码(dht11、oled、adc、control) ├── Simulation/ │ └── proteus/ # Proteus仿真工程文件 └── README.md # 项目说明和快速上手指引

拿到工程后建议先从README开始看,里面我写了快速上手的五步流程:打开原理图理解电路连接、打开CubeMX工程看引脚配置、打开Keil工程编译烧录、打开Proteus跑仿真、按调试手册排查问题。如果只是复制粘贴代码,不把引脚和电路对应起来看,遇到问题会非常被动。

二次开发方向上,我给出三个思路供参考。第一,把显示模块换成带触摸的TFT屏幕,增加按键设置菜单,可以实现在设备端直接修改报警阈值。第二,增加ESP8266或ESP32模块,通过串口把数据发到MQTT服务器,手机端就能实时查看环境数据,这是目前物联网方向最常见的做法。第三,接入低功耗模式,使用STM32的STOP模式配合RTC定时唤醒,可以让设备用锂电池撑很久,适合在档案馆这种不方便布线的场景部署。

5.4 关于代码可维护性的一点心得

代码的可维护性往往比代码本身能不能跑更重要。从一开始写这个项目,我就坚持每个功能模块一个源文件、每个源文件对外只暴露必要的接口。比如主循环只调用dht11_read()获取数据,根本不需要关心DHT11内部时序怎么实现的。这样有两个好处:一个是排查问题范围小,另一个是换硬件时只需要改底层实现,上层逻辑完全复用。

CubeMX生成的代码我尽量不手动修改,所有用户代码都放在User目录下。因为CubeMX重新生成工程时会覆盖Core目录下的代码,如果把用户代码混在里面,重新生成一次就全丢了。这是很多开源项目令人头疼的问题,我见过太多人改完CubeMX生成的main.c,然后重新生成后所有修改全部消失。

代码里我还加入了串口打印调试信息的功能,USART1通过9600波特率输出日志。调试阶段串口打印变量值非常有用,比看OLED屏幕直观。量产阶段可以把日志功能关掉,把CPU资源省下来。

最后说一个我在实际调试中的心得:这个项目我在仿真环境里跑通只花了两天,但在实物上调试DHT11和ADC花了一周。后来总结发现,问题基本集中在引脚模式配置、上拉电阻、延时精度这三个方面。所以如果你在复现时遇到问题,优先检查这三个点,八成问题都能解决。做嵌入式就是这样,原理图、代码、仿真、实物,每一层都会有惊喜,但解决一个问题的过程,就是对这个系统理解加深一层的过程。希望这套开源资料能帮你少踩几个坑,把更多时间花在真正有意思的功能扩展上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 18:09:13

MacOS 装 SAP 的 JAR 报 JAVA 错?让 OpenClaw 借 TaoToken 排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 18:02:46

U-Net实战:基于PyTorch的CT肿瘤分割完整流程与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 17:59:55

Java在线考试系统课程设计:从技术选型到自动判分的完整实现

简介&#xff1a;这份资源是面向软件工程、计算机专业学生的Java Web课程设计参考资料&#xff0c;以PDF形式呈现《基于Java的在线考试系统课程设计说明书》及配套源程序&#xff0c;适合正在做课程设计或想了解Java Web完整开发流程的学习者。内容围绕在线考试系统的需求分析、…

作者头像 李华
网站建设 2026/9/18 17:59:50

Redis List做消息队列的工程实践:从基础命令到可靠设计

1. 项目起点&#xff1a;为什么我在有MQ的时代还想用Redis List做队列先交代一下背景。我们有一个中小规模的业务系统&#xff0c;用户量不大&#xff0c;但业务链条却很长。比如用户提交一个导入任务&#xff0c;后台要经过文件解析、数据清洗、格式校验、落库、生成报表通知&…

作者头像 李华
网站建设 2026/9/18 17:59:47

Rufus 4.15免安装版制作Windows 11启动U盘完整教程

做系统维护这些年&#xff0c;U盘里最不能缺的一个工具就是Rufus。这次拿到的是Rufus 4.15免安装版&#xff0c;正好也是Windows 11装机频率最高的时候&#xff0c;我把制作Windows 11启动U盘的完整流程、选项原理、踩坑经验一次性写清楚。这篇文章适合所有想自己装系统、做维护…

作者头像 李华