ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32实验室消防预警系统:从传感器选型到状态机仿真

2026/9/29 1:57:09 拓冰建站 浏览量
STM32实验室消防预警系统:从传感器选型到状态机仿真 实验室消防这块我一直觉得是个“听起来简单做起来全是坑”的方向。前阵子我把自己的一个STM32消防预警控制系统整理成了开源包包含完整代码、原理图和Proteus仿真工程断断续续有人私信问细节干脆写一篇完整的拆解文从方案选型、硬件设计、软件状态机到仿真调试一次性说清楚。这个项目最适合三类人正在做嵌入式课程设计或毕设的学生、想入门STM32但不知道做什么的小白以及实验室里真正有消防监控需求的技术人员。它能解决的核心问题是用低成本传感器组合实现烟雾、温度、火焰多重探测分级报警并联动排风、断电等执行设备避免实验室火灾发现不及时或者误报乱报。先说好这篇文章不会只贴代码让你自己看我会把每个设计决策背后的“为什么”也讲明白。比如为什么选STM32F103C8T6而不是51单片机为什么MQ-2传感器上电后不能立刻采数据为什么继电器驱动必须加续流二极管这些坑我都是亲身体会过的。1. 项目概述实验室消防预警到底要解决什么问题实验室和家庭环境最大的区别是火灾风险源完全不同。实验室里可能有酒精灯、易燃试剂、高温烘箱还有大量长期通电的仪器设备很多事故发生在夜间或者人员离开之后。普通的烟雾报警器在家庭有效在实验室里却容易出两个问题一是灵敏度不合适有实验本身就产生少量烟雾容易误报二是只有声音报警没有联动动作——响归响没人听见就等于没响。这套系统在设计上就把“预警、报警、联动”分成了三个层面。预警阶段由传感器持续监测当温度和烟雾浓度超过正常范围但还没有到危险值时系统先打开排风扇降低可燃气体积聚报警阶段触发声光报警并能在OLED屏幕上直接看到是哪个指标超标、当前状态是什么到了严重阶段继电器动作切断非必要设备电源防止二次灾害。整个过程不是单一阈值判断而是一套分级状态机在驱动这也是这个项目和网上很多“烟雾报警器demo”的本质区别。项目开源包里包括三部分内容STM32CubeMX基础工程和HAL库源码、立创EDA绘制并整理好的原理图、Proteus仿真工程文件。原理图和仿真我都特意做了降级处理方便用最常用的工具打开不需要额外装高端软件。整体物料成本大概在五六十块左右可以说性价比很高。1.1 系统的核心功能清单功能模块具体表现实现方式烟雾浓度探测实时显示烟雾浓度百分比超阈值触发报警MQ-2传感器 STM32 ADC采集温度监测实时显示当前温度超温报警DS18B20单总线读取火焰探测检测明火产生的红外光谱火焰传感器模块 数字/模拟输入分级预警正常/预警/报警/严重四级状态切换软件状态机声光报警蜂鸣器报警、双色LED指示PWM驱动无源蜂鸣器联动执行排风扇启动、继电器切断电源MOS管/继电器驱动人机交互OLED显示数据与状态按键静音/设置I2C SSD1306 按键调试输出串口打印关键数据USART1 GPIO这个功能清单看起来不大但真正把所有模块稳定跑起来涉及的知识点跨度其实不小ADC采集与滤波、单总线时序协议、I2C驱动OLED、PWM发声、状态机设计、Proteus数字仿真串口调参。做完这一个项目STM32大部分基础外设就都过了一遍。1.2 开源文件的目录结构LabFire_System/ ├── Core/ // 启动文件、时钟、内核外设 ├── Hardware/ // 各外设驱动oled.c, ds18b20.c, mq2.c, fan.c, key.c ├── App/ // 应用层fire_state-machine.c, monitor_task.c ├── Doc/ // 原理图PDF、BOM清单、接线图 ├── Sim/ // Proteus仿真工程 └── README.md // 项目说明、快速上手我自己平时写代码喜欢把硬件驱动和应用逻辑分开这个习惯强烈推荐保留。驱动的只管把传感器的值读回来、把外设的动作执行掉应用层只关心状态怎么切换、联动的策略是什么。这样每个文件都很短出了问题也好定位改起来不需要翻几百行的大文件。2. 整体方案与关键选型为什么是STM32而不是51或Arduino如果你已经有开发板没必要再买别的如果还是零基础选型阶段我建议从STM32入门而不是51。不是因为51不能用而是STM32F103C8T6这颗芯片的资源对这类项目来说“刚刚好”64KB Flash、20KB RAM足够放FreeRTOS和一套完整的应用层内部有3个ADC、多个定时器、I2C、SPI、USART几乎不用外扩任何东西价格在四五块钱左右做产品也常见学了不浪费。2.1 三种主流主控方案的实际感受对比项STC89C52Arduino UNOSTM32F103C8T6主频12MHz16MHz72MHzADC需要外扩ADC0809内置10位ADC12位ADC多通道多定时器2个资源紧张1-2个4个各有通道开发生态Keil 烧录线Arduino IDECubeMX Keil/GCC功能扩展性做教学可以适合快速原型适合真正做产品/毕设如果你只是验证一个传感器Arduino确实效率高但项目一旦涉及多路模拟量采集、定时器PWM、单总线、串口上报并行处理51的定时器和中断资源就开始捉襟见肘了。我有一次在51上做多路ADC轮询同时还要跑DS18B20时序因为中断嵌套问题排查了一整天换成STM32后直接一个ADC扫描DMA解决体验不是一个量级。2.2 传感器选型与信号链路传感器选择上网上很多方案喜欢堆各种高级传感器比如激光粉尘传感器、进口恒温烟雾传感器。我的观点是做学习和毕业设计M级别的工业精度没有必要核心是把信号链路跑通、把逻辑做对。MQ-2烟雾传感器检测可燃气和烟雾输出模拟电压成本低响应时间约10秒。缺点是加热电阻需要长时间预热初始漂移明显但这个“缺点”刚好可以倒逼你学会基线校准不算坏事。DS18B20温度传感器单总线数字协议精度0.5℃一行GPIO就能读。缺点是时序要求严格第一次写驱动大概率要花时间调时序。火焰传感器探测红外火焰光谱输出数字量或模拟量。实验室明火反应非常快适合当“最后一道防线”。模块上通常有电位器可以调灵敏度。信号链路方面烟雾浓度经过MQ-2电阻变化分压后被STM32的ADC采样温度通过单总线数字协议直接进入数据总线火焰传感器输出一路模拟或数字信号我选了模拟输出方便在状态机里做阈值调节而不用拧电位器。2.3 执行器件驱动方式选择蜂鸣器我特意选了无源蜂鸣器而不是有源的。有源蜂鸣器电平一给就响简单但声音死板只能“滴滴滴”。无源蜂鸣器需要MCU输出PWM才能发出声音频率可调可以实现预警间歇声和报警急促声两种不同音调还能通过变频模拟消防车的警报声效果。代价只是需要两个定时器通道对STM32来说毫无压力。风扇用AO3400 N-MOS管驱动逻辑电平3.3V就能完全导通压降小、发热低比三极管方案靠谱得多。继电器用来切非必要设备电源驱动方式类似但必须加续流二极管这点后面展开讲。3. 硬件原理图要点解读原理图我本来想手画后来为了让大家好编辑顺手用立创EDA重画了一份。立创EDA免费、浏览器直接打开就能看、还支持从开源广场导入对学习者非常友好。原理图按功能分成电源、最小系统、传感器接口、输出驱动四个模块每个模块都用框线隔开并加了文字注释方便对照实物。3.1 电源与最小系统设计系统整体由5V USB供电5V直接给MQ-2加热丝、风扇和继电器供电AMS1117-3.3把5V降到3.3V给主控和传感器逻辑供电。这里有个很重要的原则大电流器件风扇、继电器和模拟信号电路要尽量分路供电不要公用一根细走线否则风扇一启动ADC采样值瞬间跳变还可能导致单片机复位。F103C8T6最小系统必须有的部分是8MHz晶振加两个20pF负载电容、NRST复位引脚上拉10k电阻加0.1uF电容到地、BOOT0下拉到地保证从Flash启动、VCAP引脚按手册接2.2uF陶瓷电容。每个电源引脚旁边放一个100nF去耦电容算是最基本但最容易被新手忽略的要求。3.2 传感器接口电路细节MQ-2模块虽然有板载比较器和电位器但最好还是直接从模块的模拟输出端引出信号给ADC。我习惯在ADC引脚前面加一个RC低通滤波器用10k电阻和100nF电容截止频率大约159Hz能有效滤掉传感器输出上的工频和高频毛刺。DS18B20的数据线必须接4.7k上拉电阻到3.3VSTM32的IO配置为开漏输出。这个很多人会忘记导致读回的全是0x85或者是FF。上拉电阻的意义在于单总线空闲时保持高电平设备通过拉低总线来发起通信没有上拉时序根本没法做。火焰传感器模块一般自带比较器输出数字高低电平同时也有AO模拟口。我接了模拟口到PA1方便在软件里动态调节灵敏度而不是每次都得用螺丝刀拧电位器。3.3 输出驱动与保护电路蜂鸣器用NPN三极管8050驱动基极串联1k电阻接PB0发射极接地集电极接蜂鸣器到5V。这里不要用IO直接驱动蜂鸣器无源蜂鸣器工作电流有二三十毫安超过GPIO的安全输出范围。继电器模块驱动用的是S8050三极管线圈两端必须反向并联一个1N4007二极管。继电器线圈是感性负载断电瞬间会产生几百伏的反向电动势不加续流二极管轻则干扰系统运行重则打坏GPIO甚至芯片。实习带过不少新人十个里有八个第一次画继电器电路都会忘记这个二极管属于“看起来没事一接继电器就死机”的经典坑。3.4 原理图绘制时的可读性建议原理图布局尽量遵循信号流从左到右左侧是传感器输入和电源输入中间是MCU右侧是输出驱动。每个模块用灰色的矩形框框起来写上模块名。网络标号命名要有意义比如SMOKE_ADC、DS18B20_DQ、FAN_CTRL而不是NET1、NET2。这样别人看原理图的过程就是读代码的过程出问题的时候排查效率也会高很多。4. 软件架构与核心代码实现工程基于STM32CubeMX生成使用HAL库。我用CubeMX配好时钟和引脚后再把应用代码放进去保留用户代码区注释这样重新生成不会覆盖自己的逻辑。时钟这部分要强调外部8MHz晶振PLL倍频到72MHz主频。F103最大就是72MHz配高了不稳定配低了外设反而复杂不必要。4.1 工程结构与模块划分驱动层每个文件只管一件事oled.c管显示ds18b20.c管温度mq2.c管ADC和烟雾换算fan.c管风扇和继电器key.c管按键扫描beep.c管蜂鸣器PWM。应用层只有两个核心文件monitor_task.c负责周期采集传感器fire_state_machine.c负责状态转移和联动策略。说句实在话我刚学STM32时喜欢一个main.c里堆几百行代码后面调试功能多了根本顶不住。哪怕毕业设计只做一次性的项目分层写也会让你在答辩的时候讲得清楚很多——老师问“你的软件架构是什么”你能说出驱动和应用的划分比只说“我用了HAL库”强得多。4.2 数据采集ADC滤波与单总线时序ADC采集烟雾浓度。CubeMX里配置ADC1通道IN0为12位分辨率采样时间建议拉长到239.5周期因为MQ-2输出阻抗比较大采样时间短了采样值会偏低。单次采集不够稳定我做了个简单的中值平均滤波连续取11次排序后去掉最大最小剩下9个求平均。稳定性和响应速度平衡得很好实测ADC抖动从±50降到±5以内。uint16_t MQ2_Read_Filtered(void) { uint16_t buf[SAMPLE_NUM]; ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_0; sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLETIME_239CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); for (uint8_t i 0; i SAMPLE_NUM; i) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); buf[i] HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); } // 冒泡排序取中值平均 for (uint8_t i 0; i SAMPLE_NUM - 1; i) for (uint8_t j 0; j SAMPLE_NUM - 1 - i; j) if (buf[j] buf[j 1]) { uint16_t t buf[j]; buf[j] buf[j 1]; buf[j 1] t; } uint32_t sum 0; for (uint8_t i 1; i SAMPLE_NUM - 1; i) sum buf[i]; return (uint16_t)(sum / (SAMPLE_NUM - 2)); }DS18B20读取则需要严格遵循单总线时序初始化复位脉冲、存在检测、写ROM命令跳过ROM匹配、发出读温度命令最后读取两个字节的暂存器数据。时序对延时非常敏感DS18B20要求读时隙在15微秒内完成采样判断所以我驱动里用了阻塞延时并关闭了优化。float DS18B20_GetTemp(void) { uint8_t data[2] {0}; if (!DS18B20_Reset()) return -200.0f; // 设备不在线 DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 HAL_Delay(750); // 12位精度转换时间 DS18B20_Reset(); DS18B20_WriteByte(0xCC); DS18B20_WriteByte(0xBE); // 读暂存器 data[0] DS18B20_ReadByte(); data[1] DS18B20_ReadByte(); int16_t raw (data[1] 8) | data[0]; return (float)raw * 0.0625f; }4.3 三级预警状态机状态机是整个软件的灵魂。我定义了四个状态NORMAL、WARNING、ALARM、ALARM_HIGH每次采集完数据后调用一次状态更新函数。状态转移不是简单的瞬间跳变而是加入了去抖计数。比如烟雾超标不会立刻进ALARM连续3个采集周期每500ms一个周期都超标才转移这样就避免了一次偶尔的烟雾波动就狂响报警器实验室里谁做实验冒个烟就全家拉闸那就没法用系统了。typedef enum { FIRE_STATE_NORMAL 0, FIRE_STATE_WARNING, FIRE_STATE_ALARM, FIRE_STATE_ALARM_HIGH } Fire_State_t; void FireSM_Update(float temp, uint16_t smoke_percent, uint8_t flame_present) { static Fire_State_t state FIRE_STATE_NORMAL; static uint8_t confirm_cnt 0; // 判定当前是否需要报警如果需要则增加计数 if (temp SMOKE_WARN_T || smoke_percent SMOKE_WARN_P || flame_present) confirm_cnt; else confirm_cnt 0; switch (state) { case FIRE_STATE_NORMAL: if (confirm_cnt 3) state FIRE_STATE_WARNING; break; case FIRE_STATE_WARNING: if (temp TEMP_ALARM_T || smoke_percent SMOKE_ALARM_P || flame_present) { confirm_cnt 0; state FIRE_STATE_ALARM; } else if (confirm_cnt 0) state FIRE_STATE_NORMAL; break; case FIRE_STATE_ALARM: if (temp TEMP_HIGH_T || smoke_percent SMOKE_HIGH_P) state FIRE_STATE_ALARM_HIGH; else if (confirm_cnt 3) { confirm_cnt 0; state FIRE_STATE_WARNING; } break; default: if (confirm_cnt 0) state FIRE_STATE_ALARM; break; } FireSM_ApplyAction(state, temp, smoke_percent, flame_present); }为什么状态机比if-else连环判断好因为状态机把“当前处于什么模式”和“该干什么动作”分开后续你想加新的联动设备、改灵敏度只需要改对应状态里的动作函数不用动转移逻辑。这个思路放到工业控制、智能家居里也是一样的学会了受用很久。4.4 联动控制与报警输出每种状态下执行的动作不同。NORMAL状态只更新显示WARNING启动风扇低速运转并让LED黄灯快闪蜂鸣器每2秒短鸣一声提醒注意ALARM状态风扇全速运转、蜂鸣器急促间歇报警、OLED显示告警信息ALARM_HIGH则加上继电器吸合切断高危设备电源蜂鸣器切换为连续长鸣。风扇我用PWM控制一半占空比对应低速预警排烟。PWMTIM3的CH3映射到PB0频率20kHz来驱动无源蜂鸣器这个频率对人耳来说接近安静阈值听起来更像高频警示音而不是刺耳的杂音风扇则用普通IO接MOS管不需要PWM调速直接高电平驱动全速运转省一个定时器通道给后面扩展。4.5 人机交互OLED显示与按键处理OLED用的SSD13060.96寸、I2C接口。CubeMX里把I2C1配置为100kHz标准模式PB6接SCLPB7接SDA。初始化驱动直接套用开源的SSD1306库按字符分页显示四行信息第一行温度、第二行烟雾浓度、第三行火焰状态、第四行当前工作状态。状态文字我用红绿黄三色区分红底表示ALARM方便快速瞄一眼就知道系统状态。按键我分配了三个KEY_MODE切换显示页面或进入设置菜单KEY_SILENCE用于报警时暂时静音。按键扫描放在10ms周期的定时器中段里完成带有20ms软件消抖避免机械弹跳导致一次按压触发多次。void Key_Scan(void) { static uint8_t key_filter 0x00; uint8_t key_current (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) GPIO_PIN_SET) ? 0x01 : 0x00; key_filter (key_filter 1) | key_current; if ((key_filter 0x07) !key_pressed) // 连续3次读到高电平确认按下 { key_pressed 1; Key_EventHandler(KEY1); } if ((key_filter 0x00) key_pressed) key_pressed 0; }串口调试部分我用USART1以115200波特率不断输出“温度/烟雾/火焰/状态”四元组。调试这个项目的时候串口就是你的眼睛。尤其是传感器数据异常的时候先用串口看原始值有没有跟上环境变化再判断是硬件问题还是软件问题比自己盲猜定位快得多。5. 仿真环境搭建与验证很多人拿到仿真工程不知道从哪下手这里我把仿真设计的思路讲透。Proteus仿真文件里主控用STM32F103C8T6模型仿真不需要实际接晶振直接配置好时钟参数就可以。比较关键的一点是Proteus没有MQ-2气体传感器模型所以烟雾信号我用了两个电位器来模拟——两只电位器分压后接到STM32的PA0和PA1模拟烟雾浓度和火焰信号在0~3.3V之间变化。5.1 仿真电路搭建要点仿真电路搭建时最好在ADC模拟输入到STM32引脚之间加一个电压表方便实时观察当前电压。Proteus里的LED、蜂鸣器、继电器模型和实物差别不大可以直接拖过来用。风扇和继电器的驱动部分Proteus里可以用虚拟按钮代替外部GPIO动作不需要真实的三极管和MOS管模型仿真验证的是MCU逻辑和外部响应的关系。在仿真里设置好之后测试用例这样设计上电启动后应该处在NORMAL状态OLED显示正常蜂鸣器不响。接着把模拟烟雾电压的电位器缓缓旋到2V以上此时软件应该先进入WARNINGLED开始闪烁风扇输出引脚电平变高。继续旋到接近3.3V状态应进入ALARM并触发蜂鸣器。反过来把电压调低状态应退回WARNING再退回NORMAL。整个过程可以帮助验证状态机的转移条件是否都符合设计。5.2 仿真与实物的真实差异仿真能验证逻辑但绝不能完全代替实物测试。Proteus里的ADC模型非常理想输入电压稳定得和直流电源一样不会像真MQ-2那样自带几百分贝的噪声电压波动DS18B20的仿真模型时序也比真实芯片宽松你在仿真里调通的驱动拿到真芯片上可能照样读错。我见过有人仿真跑得很漂亮实物一上电完全不是一回事原因就在这。所以我强烈建议仿真和实物同步推进在仿真里验证状态机逻辑在实物上验证传感器数据链和驱动电路。两者各有侧重缺一不可。仿真工程我用Proteus 8.9及以上版本打开低版本可能打不开工程文件。6. 常见问题与调试心得下面这些坑都是我实际踩过、也在开源仓库issue里被反复问到的直接给你做成速查表。现象可能原因排查思路解决方案上电就误报警MQ-2未预热初始读数飘高串口打印冒烟前的原始ADC值开机增加60秒预热倒计时结束后采集基线值作为零点ADC数值跳变供电纹波大、滤波不足用示波器或万用表看传感器输出端电压ADC引脚加RC低通程序中用中值平均滤波继电器动作导致复位线圈反电动势没有吸收检查继电器模块是否自带续流二极管线圈两端并联1N4007继电器电源单独走线OLED不显示或花屏I2C地址错误或电压不匹配确认模块背面的地址电阻0.96寸I2C OLED一般地址0x3C检查是否焊接好后上电DS18B20一直读0x85上拉电阻缺失或GPIO配置错误测量数据线空闲电平是否为高数据线上加4.7k上拉到3.3VGPIO配置为开漏蜂鸣器声音很小驱动管基极电阻过大或GPIO模式不对测蜂鸣器两端电压基极电阻用1kGPIO用推挽输出风扇不转MOS管栅极电压不够或接地不共地测量栅极和漏极电压确认使用逻辑电平MOS管3.3V电压可直接驱动AO3400烧录失败BOOT0配置问题或供电不稳检查BOOT0是否接地BOOT0接GND用ST-Link时选择“连接复位”选项6.1 MQ-2预热与基线校准是头等大事MQ-2的敏感元件是一个加热到一定温度的半导体刚上电加热丝温度不够阻值飘忽不定直接采集就是灾难。我第一版程序没有预热逻辑上电直接进监测循环每次开机都报警排查了很久才意识到这个问题。后来在程序里加了预热状态开机显示倒计时60秒预热结束后读取当前环境值作为基线把后续ADC值减去基线除以满量程得到烟雾百分比。这个“动态基线阈值”策略比传统固定阈值实用很多你放在通风条件好的实验室和密闭储物间灵敏度都能自适应。6.2 ADC滤波其实是在跟噪声博弈烟雾传感器的模拟输出噪声主要是两个来源一是传感器本身加热元件引起的微小电压波动二是环境里的工频干扰。只靠硬件RC滤波不能完全消除数字端的抖动所以软件滤波必须有。中值平均滤波的思路是先剔除异常毛刺再平滑兼顾了对突发噪声的抗性和响应速度。如果滤波器窗口取的过大系统对真实烟雾变化的响应也会变慢预警就失去意义了所以采样11次取中值平均是我试过的一个最佳平衡点。6.3 继电器干扰是嵌入式系统的老熟人继电器模块的干扰几乎是无处不在的。线圈断开瞬间产生的高压会沿着电源线反向传播导致同供电回路上的MCU复位甚至会让OLED显示乱码。解决办法分三个层次硬件上线圈两端加续流二极管PCB布线时把继电器供电和MCU供电分开了电源入口加一个大容量的电解电容软件上继电器吸合前后加5ms延时不占用主循环关键路径如果干扰实在严重再加一个隔离光耦。这个排查顺序基本能解决90%的继电器干扰问题。6.4 按键消抖别用死循环延时很多教程教按键消抖用delay10ms这在简单项目里勉强能用但在多任务系统里就是一个隐患。我在系统时基里维护了一个10ms标志位按键扫描函数每次被标志位调用的时候读取一次按键状态并用状态机消抖。这样消抖过程不会阻塞传感器采集、OLED刷新和状态机运行。同样是20ms消抖一个阻塞主循环一个完全不影响其他运行体验差距很大。6.5 调试这种多模块项目的顺序建议最后给一套我自己的调试流程。第一步先把串口打印跑通保证printf能输出第二步单独测量三个传感器的数据链确认ADC和温度数值符合环境变化第三步写一个开机自检函数上电时让蜂鸣器响一声、LED全闪、风扇转两秒验证所有执行器件都正常第四步再启动状态机联调最后才做仿真和真实场景测试。这样每一层都是可信的出问题时永远能在最近的一层找到原因而不是整机跑起来数据一乱就不知道该怀疑硬件还是软件。7. 扩展方向与最后想说的话做完这套系统之后我发现它最大的意义不是“防住了一场火灾”而是让你把嵌入式开发的整条链路完整走了一遍芯片选型、电路设计、传感检测、逻辑决策、执行控制、调试验证。这个经验放到任何单片机项目里都能复用。如果学有余力我会建议继续往三个方向扩展第一把串口数据换成Wi-Fi模块上报到MQTT服务器做成一个简易的物联网消防监测平台手机上实时看实验室状态第二加入多节点组网每个实验室放一个从机通过RS485总线上报给机房主机一台主机管理整层楼第三把阈值参数做成菜单可调用OLED和按键设置界面不用改代码重新烧录就能适配不同实验室的灵敏度需求。我做实际项目的体会是不要追求一次把功能做全先把最小可用的闭环跑通再一点点加东西。这期内容里的图纸、代码和仿真我已经全部整理进了开源仓库可以直接照着搭硬件、烧程序。嵌入式这东西光看不练永远学不会拿着这块几十块钱的板子把每一行代码调通的感觉真的比自己刷十天教程都来得快。