1. 项目概述:为什么要给图书馆做一套环境监测系统
做嵌入式这些年,手上过过不少板子,从51到MSP430再到STM32,真正让我觉得"既有教学价值又有实际意义"的开源小项目,图书馆环境监测系统算是一个很典型的存在。
你可能要问,图书馆这种地方看起来安安静静,需要监测什么环境参数?如果你去过那种老式大型图书馆,应该会有体会:书库深处闷热潮湿,古籍区空气干得发脆,阅览室人多的时候二氧化碳浓度飙高、人昏昏欲睡。温度、湿度、光照、空气质量,这四个参数直接决定读者的舒适度,也影响馆藏图书的保存寿命。书页对温湿度极其敏感,湿度过大容易发霉,过于干燥则纸张变脆、发黄。光照方面,紫外线会让书脊褪色,而自然光直射的阅览区在不同时段照度差异巨大,靠人工开关窗帘效率太低。
这套STM32环境监测系统的价值就在这:用一块常见的STM32主控,配合温湿度传感器、光照传感器,再加个OLED显示屏和LED指示,就能搭出一个实时的环境监测节点。它能做三件事:实时采集环境数据、在本地屏幕直观展示、超阈值自动触发声光报警或风扇联动。整套项目包含完整的可编译代码、可打样的原理图、可直接跑的仿真工程,非常适合正在学STM32的学生、想接触传感器应用开发的工程师,以及需要快速搭建环境监测原型机的创客。
我选择在Proteus里做仿真验证,同时给出真实硬件运行方案。这两条路我都实际跑通过,仿真和实物之间的差异、坑点,后面会单独拿出一整节来说。
2. 整体设计与硬件方案选型
2.1 为什么选STM32F103C8T6做主控
提起环境监测,很多人第一反应是用Arduino,甚至用ESP8266直接连云端。但我最终把主控定在STM32F103C8T6,理由很现实:
第一,STM32F103C8T6是目前生态最成熟、资料最全的入门级Cortex-M3芯片。72MHz主频、64KB Flash、20KB RAM、丰富的外设接口,跑这类多传感器采集+显示的任务绰绰有余。它就像嵌入式界的"捷达",皮实耐用,网上随便一搜都是参考设计,踩坑了也容易找到答案。
第二,如果整套东西用Arduino写,传感器库一键装好、代码几十行搞定,学习价值反而被稀释了。用STM32裸机HAL库从时序到I2C协议全部手写一遍,你才能真正理解传感器是怎么"说话"的。对学习者来说,这种"麻烦"恰恰是最珍贵的学习机会。
第三,从成本角度看,F103C8T6核心板在市场上的价格很低,加上几个传感器模块,整体物料成本可以控制在几十元以内。对于学校实验室、个人学习者来说,这个门槛几乎不存在。
2.2 传感器选型:DHT11和BH1750的组合逻辑
环境参数怎么选、用什么传感器,是这个项目决策的核心环节。我最终确定了两颗传感器:
温湿度使用DHT11,光照使用BH1750。这个组合不是随手选的,背后是成本和调试成本的权衡。
DHT11的测温范围0到50摄氏度、湿度20%到90%RH,精度虽然一般(正负2摄氏度、正负5%RH),但胜在单总线协议简单、模块化程度高、价格低廉。图书馆这种室内环境,温湿度波动本来就不剧烈,DHT11的精度完全够用。你用SHT30确实能拿到更高精度,但多出来的成本和学习曲线,对这个项目来说并不划算。
光照用BH1750则是因为它直接输出数字量,内部集成16位ADC,量程从1到65535勒克斯,覆盖从深夜书库到白天窗边的全部场景。最方便的是它走I2C接口,两根线就能挂到STM32上,不占用太多GPIO。而且它内置了光敏二极管和运算放大器,不需要像光敏电阻那样自己做分压电路和校准曲线,对新手极其友好。
有人会问:要不要再加个空气质量传感器(比如SGP30或CCS811)?我在扩展版里确实留了I2C接口,也写好了预留驱动,但主版本没有纳入。原因有二:一是空气质量传感器普遍需要较长的预热时间,而且在Proteus里几乎没有仿真模型,不利于做纯仿真学习;二是很多东西一旦加了,项目的核心教学主线就容易被稀释。先做好温湿度加光照,再自己动手扩展气体检测,节奏更合理。
2.3 系统架构与工作链路
整个系统的数据链路其实很清晰:传感器采集物理量,转换成电信号,主控通过协议读取数值,做数据处理和阈值判断,最后把结果送到显示和报警模块。
具体到这套设计,STM32F103C8T6作为核心控制器,通过GPIO模拟单总线协议读取DHT11的温湿度数据;通过I2C总线读取BH1750的光照强度。数据经过简单的滤波和越限判断后,驱动一块0.96寸的I2C接口OLED显示屏完成本地展示。一旦温湿度超出设定区间,蜂鸣器会发出报警提示,同时板载LED状态灯变换闪烁模式。如果有需要,还可以通过GPIO控制一个继电器模块,外接风扇或除湿机做自动联动。
整机供电采用USB 5V输入,板载AMS1117-3.3稳压芯片降到3.3V给主控和传感器供电。功耗控制上,OLED和传感器都支持休眠模式,后续如果需要电池供电,可以在代码里加入待机模式,这个我在扩展建议部分会提到。
3. 原理图设计与硬件细节
3.1 最小系统电路
一套能跑起来的STM32系统,最小电路包含四部分:电源、复位、时钟、下载调试接口。这些在很多开发板上都集成好了,但如果想按自己的需求打样,理解这部分尤为重要。
电源部分,我用AMS1117-3.3把USB的5V降到3.3V,输入输出各加一个10uF电解电容和一个100nF瓷片电容滤波。AMS1117的最大输出电流是1A,对这套负载来说绰绰有余。注意一点:DHT11模块有的版本自带3.3V稳压,有的没有,接线前要看清楚模块上的丝印说明。
复位电路用一个10K上拉电阻加一个100nF电容到地,按键按下时把NRST引脚拉低实现手动复位。时钟部分用了8MHz无源晶振,配上两个20pF负载电容。这里有个细节:STM32F103的OSC_IN和OSC_OUT引脚对走线长度和电容容差比较敏感,如果画PCB时走线过长,可能会引起起振不稳,尽量让晶振靠近MCU。
下载调试接口我同时引出了SWD(四线)和UART1的Boot下载引脚,方便用ST-Link下载调试,也方便用串口ISP做备选方案。实际使用时,SWD的SWDIO和SWCLK各加了一个10K上拉电阻,能有效避免下载器连接不稳定。
GPIO分配上,我精心规划过引脚占用:PA0到PA3接四个独立按键,用于设定温湿度阈值;PA5、PA6、PA7接HC-SR04超声波模块(预留的扩展功能,后面做库区人员检测时用);PB0、PB1接DHT11和继电器;PB6、PB7接I2C总线的SCL和SDA,分别连OLED和BH1750;PB8、PB9备用一组I2C2给后续扩展传感器。蜂鸣器用PB5驱动,LED指示灯用PC13(板载)和PB3、PB4外接。
3.2 DHT11与BH1750的接入电路
DHT11的接法很简单:VCC接3.3V,GND接地,DATA引脚通过一个4.7K上拉电阻连接到主控的PB0。DHT11用的是单总线协议,空闲时为高电平,主机拉低总线发起通信。上拉电阻不能省,否则数据线上的高电平驱动能力不够,会导致读到的数据全是0xFF。
BH1750的接入稍微注意一下:它的VCC可以接3.3V到5V,但I2C的SDA和SCL引脚如果要和3.3V的STM32直接相连,最好确认模块上是否自带电平转换。大多数市售BH1750模块的VCC接3.3V时,I2C引脚输出也是3.3V电平,可以直接连接;但有些模块为了兼容5V系统做了上拉,此时建议在STM32侧串一个100到220欧姆的电阻做保护,稳妥不会烧引脚。ADDR引脚接地时I2C地址是0x23,接VCC时地址变成0x5C,画原理图时要标注清楚,方便后续写驱动时对照。
OLED显示屏同样挂在I2C1总线上,和BH1750共用SCL和SDA。这里有个总线负载问题:I2C总线上的设备越多,上拉电阻就需要越小。我在画完原理图后,把上拉电阻从默认的10K改成了4.7K,就是为了照顾两个I2C设备同时挂载时的信号完整性。实测下来,100KHz的标准模式通信非常稳定。
3.3 报警与联动电路设计
报警部分不能直接用MCU的GPIO驱动蜂鸣器,因为GPIO的灌电流和拉电流能力有限。我用了一个NPN三极管(S8050)做开关管:PB5通过1K限流电阻连接到三极管基极,蜂鸣器接在VCC和集电极之间,发射极直接接地。当PB5输出高电平时三极管导通,蜂鸣器得电发出声音;输出低电平时蜂鸣器关闭。
这种低边驱动方式的优点是控制逻辑简单,GPIO输出高电平即触发,而且三极管导通时的饱和压降很小,蜂鸣器能获得接近满额的驱动电压。如果你用的是有源蜂鸣器(自带振荡源),给电就响,不需要外部提供频率;如果用的是无源蜂鸣器,代码里需要通过定时器输出一个2到4KHz的PWM信号才能发声。我在原理图里画的是有源蜂鸣器,代码也更简单,新手不容易卡壳。
继电器联动电路类似,用一个NPN三极管加一个续流二极管来实现。继电器线圈是个大电感,断电瞬间会产生很高的反向电动势,如果不加续流二极管,这个反压可能直接击穿三极管。D1(1N4007)就承担了这个续流作用,这是绝大多数新手画继电器电路时最容易漏掉的地方。
3.4 我自己打样时遇到的硬件坑
原理图画好、板子打回来之后,有几个坑是实际调试中才暴露出来的,写在这里供大家参考。
第一个坑是DHT11的引脚顺序。不同厂家生产的DHT11模块,引脚排列居然不完全一致。我最早拿到的一批模块,丝印上的正反面标反了,插上去以后数据始终读不到。后来养成一个习惯:不管什么模块,拿到手先用万用表二极管档测一下VCC和GND之间的导通性,或者先看模块背面的文字标注再接线,不要完全依赖丝印。
第二个坑是去耦电容的放置。原理图上我虽然画了100nF去耦电容,但第一次画PCB布局时,电容放在了板子边缘,离MCU电源引脚很远,结果MCU在继电器吸合的瞬间偶尔会复位。后来把电容挪到MCU电源引脚旁边,并且每个VDD引脚就近放一个100nF,问题就消失了。对于高频数字电路来说,去耦电容离芯片电源引脚越近越好,这是PCB布局的硬规则。
第三个坑是OLED的I2C地址。买的0.96寸OLED模块大部分地址是0x78(7位地址0x3C),但也有少量模块是0x7A(7位地址0x3D)。如果屏幕一直不亮,先用I2C扫描程序确认一下实际地址,再修改代码里的宏定义,不要死磕一件事。
4. 代码架构与核心实现
4.1 工程结构与模块划分
代码我基于STM32CubeMX生成HAL库工程,开发环境用Keil MDK5。整个工程按功能模块划分得比较清晰,方便大家按需取用:
Library_Monitor/ ├── Core/ │ ├── Inc/ │ └── Src/ ├── Drivers/ │ ├── STM32F1xx_HAL_Driver/ │ └── BSP/ │ ├── dht11.c/h │ ├── bh1750.c/h │ ├── oled.c/h │ └── bsp_key.c/h └── User/ ├── main.c └── user_task.c/hBSP层的四个驱动模块是重点。dht11负责单总线时序读写,bh1750封装了I2C读写接口和光照数据转换,oled提供显示和绘图API,bsp_key做按键扫描和消抖处理。main.c里的逻辑只关心数据流和业务状态机,不直接操作寄存器,这样代码的可读性和可移植性都好很多。
4.2 DHT11驱动:用手写时序理解单总线协议
DHT11的驱动是整个项目里最值得细看的代码,因为它涉及严格的时间要求。单总线通信的流程分成两步:主机发起起始信号,然后读取40位数据。
主机先把总线拉低至少18ms,然后释放并延时20到40us,这是起始信号。DHT11收到后会拉低80us作为响应,再把总线拉高80us,之后开始逐位发送数据。每位的发送方式是:先拉低50us,然后拉高,高电平持续26到28us表示"0",持续70us表示"1"。
驱动代码里最核心的部分是读取一位数据的函数:
static uint8_t DHT11_ReadBit(void) { uint8_t retry = 0; while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_RESET) { if (++retry > 100) return 0xFF; // 超时保护 delay_us(1); } delay_us(40); // 跳过高电平前段的"0"判定区间 if (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET) { while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == GPIO_PIN_SET) { if (++retry > 100) return 0xFF; delay_us(1); } return 1; } else { return 0; } }这段代码的逻辑是:先等数据线变高(DHT11拉高开始发数据位),然后延时40us后再采样。如果此刻引脚仍然是高电平,说明这一位是"1"(高电平持续70us,40us后还没结束);如果已经变低,说明是"0"(高电平只持续26us,40us后已经结束)。这种方法用延时替代了更精确的输入捕获定时器,虽然牺牲了一点精度,但在主频72MHz下配合空循环延时,实际测试读出的温湿度数据相当稳定。
40位数据的组成为:16位湿度(整数+小数)、16位温度(整数+小数)、8位校验和。校验和的计算方式是前四个字节相加取低8位,如果和校验字节一致,说明这帧数据有效。我在代码里做了严格的校验判断,宁可这次采样丢弃,也不能把脏数据送进显示层。
4.3 BH1750驱动:I2C协议与光强换算
BH1750的驱动相对简单,关键是搞清楚它的测量模式和换算公式。
芯片上电后默认是连续H-分辨率模式,分辨率1勒克斯,测量时间约120ms。如果你需要更快的响应速度,可以切换到连续L-分辨率模式(4勒克斯分辨率,测量时间16ms)。图书馆环境变化不快,我选了H-分辨率模式,数据更平滑。
读取流程分两步:先发送测量命令,等待测量完成,再连续读取2字节数据。I2C通信的代码我直接用HAL库的接口封装:
uint8_t BH1750_ReadLight(float *lux) { uint8_t buf[2]; uint8_t cmd = 0x10; // 连续H-分辨率模式 if (HAL_I2C_Master_Transmit(&hi2c1, BH1750_ADDR << 1, &cmd, 1, 100) != HAL_OK) return 1; HAL_Delay(180); // 等待测量完成,留足余量 if (HAL_I2C_Master_Receive(&hi2c1, BH1750_ADDR << 1, buf, 2, 100) != HAL_OK) return 1; uint16_t raw = (buf[0] << 8) | buf[1]; *lux = (float)raw / 1.2f; // H-分辨率模式除1.2 return 0; }BH1750地址左移一位,是因为HAL库的I2C接口需要8位地址(7位地址加读写位),这和传感器数据手册上写的0x23(7位地址)存在差异,新手刚接触时容易在这里绕晕。
换算公式里的1.2是芯片手册给定的分辨率系数,在H-分辨率模式下,1勒克斯对应1.2个LSB。实测正午窗边能读到两万以上勒克斯,深夜关灯时读到个位数,量程覆盖非常理想。
4.4 主循环与业务逻辑:状态机思路
main函数里,初始化完外设后进入while(1)主循环。我采用了一个简单的状态机来管理采集、显示、报警三件事,避免所有功能堆在一个循环里互相阻塞:
typedef enum { STATE_IDLE, STATE_SENSOR_READ, STATE_DISPLAY_UPDATE, STATE_ALARM_CHECK, STATE_KEY_SCAN } SystemState;循环里每隔2秒触发一次完整的采集周期(因为DHT11的采样频率建议不低于1秒间隔),采集完成后通过标志位通知其他状态执行相应动作。显示更新和按键扫描使用短周期轮询,保证OLED刷新不卡顿、按键响应不迟钝。
报警判断的逻辑是:温湿度或光照的实测值超出设定区间后,蜂鸣器触发。为了避免临界值附近反复鸣叫导致刺耳,我加了5%的回差滞回控制,实测效果很理想。
显示屏的UI设计分两个页面:首页显示温度、湿度、光照三个数值,第二页显示当前设定的阈值范围。短按KEY1切换页面,KEY2和KEY3调整阈值大小。UI切换时只重绘变化的区域,而不是全屏刷新,OLED的刷新率会明显提高。
4.5 按键消抖与参数设置
按键处理看起来简单,但做不好用户体验很差。我用了10ms级定时扫描加状态机消抖的方案:检测到按键按下后,连续采样3次,每次间隔10ms,如果结果一致才确认有效按键事件。这个方案比简单的HAL_Delay(20)消抖更稳,不会因为主循环堵塞而让按键"卡键"。
阈值设定采用了长按加速的逻辑:短按一次,当前阈值加减1个单位;长按超过1秒后,每200ms自动加或减10个单位。这样设定温度上限从25度调到28度,短按三次即可,而大幅调整时也不会累手。
5. Proteus仿真搭建与联调验证
5.1 仿真工程的元件选型与连线
Proteus仿真是这套项目的一个亮点,也是很多初学者最需要参照的部分。很多人以为Proteus只能跑跑LED流水灯,实际上它支持DHT11和BH1750的仿真模型,只要库里有,就能完美跑出数据波形。
我使用的Proteus版本是8.9以上,新建工程后从元件库中依次添加:STM32F103C8T6、DHT11、BH1750、LM016L(用LCD1602代替OLED做显示展示,Proteus对OLED的仿真支持不稳定)、BUTTON、BUZZER、RESISTOR、LED。元件找不到时,用关键字搜索即可,比如DHT11在"Temperature and Humidity Sensors"分类下。
连线和实物电路一致:DHT11的DATA接PB0,BH1750的SCL接PB6、SDA接PB7,LCD的RS、EN、D4到D7分别接PC0到PC3,蜂鸣器接PB5。电源和地网络要仔细布置,Proteus对电源网络自动以三角形符号标注,千万不要遗漏。
5.2 仿真模型驱动的避坑指南
Proteus仿真最让人头痛的一点是DHT11的时序要求极其严格,而Proteus的虚拟时间片和真实硬件存在差异。我最早在仿真里跑实物代码,DHT11数据死活读不出来,折腾了很久。后来把延时函数从空循环Delay改为基于SysTick的精确微秒延时,仿真和实物都通了。
BH1750在Proteus的模型响应速度也比较慢,实测下来,读取命令发出后需要等待至少200ms才能读到有效数据。我在驱动里把延时从180ms提升到250ms,仿真稳定性明显提升,实物上也没有副作用。
LCD1602在仿真里替代OLED时,需要注意对比度调节。V0脚对地接一个10K电位器,调节到合适位置才能清晰显示。很多仿真实物能跑、但屏幕上白花花一片,基本就是对比度没调好。
5.3 仿真联调结果记录
仿真联调通过后,我对系统做了几个典型场景的模拟测试:
第一个场景是模拟正常阅读环境。DHT11的仿真模型可以通过滑块实时调整温湿度,我把温度调到25度、湿度调到55%RH,光照强度通过BH1750模型的滑动条调到500勒克斯,此时系统显示数据正常,蜂鸣器不响,LED为绿色常亮。
第二个场景是模拟库房高温预警。我把温度滑块推到32度,超过阈值30度后,LCD界面显示异常提示,蜂鸣器以1Hz频率鸣叫,LED变为红色闪烁。继电器输出引脚电平翻转,仿真里外接的风扇模型(可以用直流电机模型代替)开始转动。
第三个场景是模拟夜晚闭馆状态。光照调低到10勒克斯以下,系统进入低照度模式,OLED自动切换为夜间配色方案(黄字黑底)。这个功能是偏体验向的,但图书馆管理员反馈夜里巡检时很实用。
仿真工程里也包含了STM32的虚拟串口调试。我通过VSM Studio的虚拟终端观察了DHT11每帧数据的校验结果,确认了数据稳定性。
6. 常见问题与排查技巧实录
6.1 DHT11读取数据一直为0或0xFF
这个问题几乎每个做过DHT11的人都会遇到。排查思路从软件到硬件逐步排除:
第一,确认GPIO模式。DHT11的DATA引脚必须配置为开漏输出且带上拉。如果配置成推挽输出,单片机引脚输出高电平时的驱动能力和DHT11内部的下拉发生冲突,通信时序就会错乱。
第二,确认延时函数精度。DHT11的时序在微秒级别,用HAL_Delay只能延时毫秒,完全不符合要求。必须用SysTick实现微秒级延时,或者用定时器的输入捕获功能来测量高电平宽度,后者精度更高。
第三,检查上拉电阻。如果DATA引脚外部没有4.7K上拉到VCC,或者上拉电阻选得太大,信号边沿会变得平缓,导致单片机误判电平。
第四,降低采样频率。DHT11的官方手册说采集间隔建议大于1秒,连续快速读取会导致传感器不响应。我实测采样间隔低于500ms时,偶发读取出错,间隔拉到2秒后稳定性非常好。
6.2 I2C设备无响应或读到错误值
I2C总线上的设备总是不响应,先用逻辑分析仪或示波器看波形,没有仪器的话可以从以下几个方面排查:
总线地址是否写对。BH1750的7位地址是0x23,但HAL库要求8位地址,需要左移一位成为0x46。OLED是0x3C或0x3D(7位),左移后是0x78或0x7A。地址写错是最低级但最常见的错误。
上拉电阻是否合适。I2C总线需要上拉电阻,一般4.7K到10K都行。如果总线上挂载设备多、通信距离长,选用更小的上拉电阻(比如2.2K)有助于提升信号质量。
测量等待时间是否充足。BH1750在H-分辨率模式下测量时间约120ms,如果立即读数据,可能读到上一帧的旧值。我在代码里加了180ms延时,实际使用时可以根据情况调整。
6.3 OLED不显示或者显示乱码
OLED不显示的原因排在第一位的是地址配置错误,第二位是供电不足。OLED模块全亮时的电流约20到30mA,一般3.3V稳压芯片能扛住。但如果你的USB口供电能力弱或者线缆压降大,OLED上电瞬间可能导致整个系统掉电复位。
显示乱码则多半是I2C速率不匹配。OLED模块的标准I2C速率是100K到400K,如果你把I2C时钟配置成1MHz以上,部分屏驱芯片跟不上升级协议导致乱码。解决办法是把I2C时钟调整为100KHz或者重刷初始化的配置序列。
6.4 Proteus仿真不运行或运行卡死
Proteus仿真卡死大概率是模型冲突或时序死循环。DHT11模型如果长时间不响应,代码里的超时保护会卡在while(1)里出不来。我建议在读取函数里加入严格的重试计数,超过一定次数直接返回错误码,主循环继续执行其他逻辑,这是写嵌入式代码时应有的"容错思维"。
还有一个常见问题:仿真工程里STM32的晶振频率必须和代码里配置的一致。如果你代码用CubeMX配置了72MHz主频,但Proteus里设置的是8MHz外部晶振(没有配置PLL),系统时钟就会异常,所有延时和外设时序都会乱套。我建的仿真工程里,在HSE_VALUE上做了处理,让仿真环境能正确匹配并运行。
7. 项目扩展方向与后续规划
这套系统目前是一个完整的单点监测节点,但如果把视野放大,它其实是一个更大的物联网系统的"神经末梢"。我从做完板子之后,陆陆续续给它规划了三个扩展方向。
第一个方向是联网化。给F103C8T6外挂一个ESP8266模块,通过串口AT指令把温湿度数据上报到本地MQTT服务器,再接个简单的前端页面,就能做成一个多节点的图书馆环境监控网络。管理员在手机上就能看到每个楼层、每个库房的实时环境参数,报警信息也能推送到微信或钉钉。这个扩展的代码我已经在调了,核心就是给现有的采集任务增加一个网络上报的分支。
第二个方向是数据存储。用F103的SPI接口外接一个MicroSD卡模块,定时把环境数据写入CSV文件。这样就能做历史数据回放、温湿度变化曲线分析,甚至预测哪些区域在什么季节容易发生霉变。对图书馆这样的场景来说,长期数据积累比实时数据更有管理价值。
第三个方向是低功耗改造。如果要做无线节点,就得考虑电池供电。F103的待机模式电流可以做到微安级别,配合RTC定时唤醒,每隔10分钟采集一次并发送数据,两节18650电池撑几个月没有问题。当然,DHT11和BH1750在休眠模式下的功耗特性也需要一并考虑。
我个人在实际操作中最深的一个体会是:这类项目千万不要一味追求参数高配和功能堆叠。把最简单可靠的电路吃透,把时序和协议搞清楚,比多挂几个传感器有用得多。这套图书馆环境监测系统从零到一走下来,通信协议、传感器驱动、状态机设计、软硬件联调的功夫全都练到了,很多人卡了好久的"学完STM32不知道做什么",其实就差这样一个能把知识串起来的完整闭环。代码、原理图和仿真工程我都在网盘里共享了,需要的可以直接拿去参考,有问题也欢迎随时交流。