STM32F103环境监测终端:DHT11、MQ2与OLED多阈值调节实战
1. 项目概述与整体设计1.1 这个项目到底做了什么这几年智能家居的概念满天飞但真正适合嵌入式初学者上手的方案STM32F103绝对算一个经典选项。这个项目不是那种纯跑马灯或者点个LED的教学板实验而是把温湿度采集、烟雾检测、光照感知、OLED可视化、阈值调节这几块硬功能全部打通做成了一个真正意义上的环境监测小终端。整套系统的输入是三个传感器输出是一块0.96寸OLED屏幕中间由STM32F103完成所有数据的采集、处理、判断和显示。我最初做的版本只有“显示数据”这个功能后来发现光显示没意思真正有用的是把“阈值调节”做进去。项目里提到的“多阈值调节”指的是温湿度、烟雾浓度、光照强度各自独立设置报警门限超过门限后可以驱动蜂鸣器报警、继电器吸合或者状态灯翻转。这样就不只是一个数据采集器而是带决策能力的控制系统。对做毕业设计、课程设计或者入门智能家居开发的朋友来说这套东西的覆盖度非常合适从传感器时序到ADC采集、再到I2C驱动OLED和按键交互全都过了一遍。1.2 硬件选型与材料清单先说主控STM32F103C8T6这颗芯片在小项目里几乎是万金油般的存在。72MHz主频、20KB RAM、64KB Flash跑DHT11、MQ2、光敏加OLED这套组合绰绰有余。如果你手头只有最小系统板也没关系这个项目对引脚资源要求不高核心只需要ADC引脚、I2C引脚、几个GPIO再加上可选的按键与蜂鸣器IO口。传感器方面DHT11选那种四脚的蓝色模块或者三脚裸传感器都行它内部已经集成了电阻式湿敏元件和NTC测温元件通过单总线协议把温湿度数据一次性送出。MQ2我建议直接买带比较器输出的模块版本板子上除了模拟量输出AO之外还有数字量输出DO这在接线时能多一种选择。光敏模块同样有AO和DO两种输出不过既然要显示光照数值就必须用AO接ADC去读模拟电压。OLED屏幕选0.96寸I2C接口的最方便SSD1306驱动芯片128x64分辨率四根线搞定。如果单片机主控的I2C外设出过问题也可以用GPIO软件模拟I2C这个后面我会重点讲。其余材料还包括5V电源如果传感器模块需要5V供电、蜂鸣器模块、轻触按键若干、杜邦线和面包板。整体成本控制在一百块以内属于非常典型的学习型项目配置。1.3 系统架构与信号流向整个系统从信号链路的视角看非常清晰。DHT11输出的是单总线数字信号接到普通GPIO口通过时序读取得到温度和湿度两个值。MQ2烟雾传感器模块通电后内部的加热丝会把敏感材料加热到工作温度当空气中可燃气体浓度变化时敏感材料的电阻发生改变经过模块上的电压比较电路和放大电路最终在AO引脚输出一个与浓度正相关的模拟电压。光敏模块的原理也是类似的光敏电阻的阻值随光照变化经过分压电路后AO引脚输出对应的电压值。这两路模拟信号进入STM32F103的ADC引脚12位分辨率参考电压3.3V转换出的数字量经过换算就能还原成烟雾浓度百分比和光照强度百分比。OLED屏幕挂在I2C总线上扮演的是“数据输出窗口”的角色实时刷新当前四个环境参数。按键负责阈值调节阈值参数存放在STM32内置Flash中掉电不丢失。整条链路中MCU是所有数据的汇聚点所有判断和联动都在主循环里完成。DHT11信号 - GPIO读取 - 温湿度数值 -----\ \ MQ2 AO - ADC通道1 - 烟雾浓度数值 ------ STM32F103 - OLED显示 / - 蜂鸣器报警 光敏AO - ADC通道2 - 光照强度数值 -----/ - 继电器联动 - 状态LED2. 核心模块原理与接线细节2.1 DHT11温湿度传感器单总线时序到底怎么读很多人第一次接触DHT11就被它那条单总线协议搞懵了。单总线的意思就是数据线和时钟线共用一根线主机和从机通过严格的时序来区分“0”和“1”。DHT11的数据帧长度是40位依次是8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、8位校验和。这里有个容易踩坑的细节DHT11的湿度小数部分和温度小数部分在实际使用中经常读到0x00这是正常的因为DHT11本身精度只有1%RH和1℃小数字节基本是固定零值。只要校验和正确数据就是可信的。读取时序分两步。第一步是主机发起起始信号将数据线拉低至少18ms然后拉高20-40us释放总线等待从机响应。第二步是从机应答DHT11检测到起始信号后会先把总线拉低80us表示应答再拉高80us准备发送数据。接下来每一位数据的开始都是一个50us的低电平脉冲之后高电平持续的时间决定了这一位是“0”还是“1”。高电平在26-28us左右记为逻辑0在70us左右记为逻辑1。我在代码里采用了HAL库加微秒级延时的方案DHT11的时序容错范围不算苛刻主频72MHz下用简单的循环延时就能满足要求。但有一个关键点必须提醒在读取DHT11之前建议把引脚配置为推挽输出模式发送起始信号然后立即切换为开漏输入或上拉输入模式读取数据。如果你在切换模式时操作不当很容易出现一直读0xFF或者数据帧校验失败的情况。2.2 MQ2烟雾传感器与光敏模块模拟量采集的两种思路MQ2传感器的核心是一个二氧化锡半导体气敏材料它在洁净空气中电导率较低当接触到可燃气体或烟雾时电导率会迅速上升。模块上集成了电压比较器LM393可以直接输出数字量DO同时AO引脚输出的是随浓度连续变化的模拟电压。这里我要单独解释一下MQ2的“预热”问题。气敏传感器内部有一根加热丝上电后需要一段时间让敏感材料达到稳定工作温度否则前几分钟的数据漂移非常明显。我在实测中发现MQ2模块上电后约三到五分钟内AO输出电压会缓慢下降之后才趋于稳定。做这个项目时千万别一上电就急着标定阈值等预热完成再校准否则后面报警阈值怎么调都不准。光敏模块的原理简单很多光敏电阻的阻值在强光下会降到几千欧甚至几百欧在黑暗环境下升高到几兆欧。模块上的分压电路把这个阻值变化转换为0到3.3V的电压变化AO引脚输出模拟电压。需要注意的是不同批次的光敏模块在相同光照条件下的输出电压可能差距不小所以阈值调节功能在这个模块上尤为重要——你不能写死一个光照阈值而是要根据实际环境调。ADC采集方面STM32F103内部有3个SAR型ADC12位分辨率采样速度最快1MHz。本项目占用的两个通道可以挂到ADC1上使用扫描模式加连续转换模式配合DMA把转换结果自动搬运到内存数组里CPU零干预这是最省心的做法。如果不用DMA也可以在主循环里轮流启动转换然后等待只是会占用不少CPU时间显示刷新和按键扫描的响应速度都会受影响。2.3 OLED显示SSD1306驱动与I2C通信的读写细节0.96寸OLED的驱动芯片是SSD1306它内部有一块128x64bit的显存GDDRAM共8页Page0到Page7每页128字节。写入显示数据的大致流程是先设置页地址和列地址然后连续写入图形数据SSD1306会自动把这些数据映射到对应的像素点上。I2C通信是这个模块最容易出问题的地方。SSD1306的I2C从机地址通常是0x3C在7位地址模式下是0x3C完整8位写地址是0x78。但这里有个坑有些厂家的OLED模块把DC引脚数据/命令选择固定接在高电平此时从机地址会变成0x3D。买模块的时候最好问清楚地址或者在代码里做成宏定义方便切换。我强烈建议你在HAL库下用软件模拟I2C驱动OLED而不是用硬件I2C外设。原因很简单STM32F103的硬件I2C外设在某些情况下会出现总线锁死问题BUSY位置位后无法自动清除而OLED屏幕的操作频率不高软件模拟I2C的时序完全够用出问题后调试起来也更直观。当然如果你用的是标准库或者已经对硬件I2C非常熟悉用硬件外设也没问题。提到热词里那个“加了oled函数卡死”的问题我在开发中确实遇到过。结症在于OLED的I2C写入函数里必须有超时处理机制。如果你用HAL_I2C_Mem_Write这样的阻塞式调用一旦总线异常挂死函数会一直等待主循环直接卡住。解决方法是给HAL函数传入一个合理的超时时间比如100ms并检查返回值写失败就跳过本次刷新不要阻塞主循环。还有一个容易忽视的点OLED是慢速设备每次刷新整个屏幕128x64的数据大概需要1KB的空间I2C时钟频率如果太高超过400kHz容易出错稳妥起见设100kHz-400kHz之间。2.4 供电拓扑与电平匹配细节这个项目的供电看起来简单但实际处理不好会导致各种诡异问题。STM32F103核心板通常用3.3V供电DHT11模块可以工作在3.3V到5V之间而MQ2模块最好用5V供电因为它的加热丝工作电流较大3.3V下加热不充分会导致灵敏度下降。我使用的方案是USB的5V作为总电源输入分为两路——一路直接给MQ2模块供电另一路经过AMS1117-3.3降压后给主控、OLED和光敏模块供电。DHT11的数据引脚是开漏结构模块板载上拉电阻接到电源VCC如果DHT11用5V供电数据线高电平时会对STM32的3.3V引脚形成反向电流。稳妥的做法是把DHT11也接3.3V供电或者用两个电阻分压把数据线电压降到3.3V以下。同样OLED和蜂鸣器的I/O接口要注意与STM32电平匹配。3. 工程搭建与代码实现3.1 CubeMX配置方案STM32CubeMX生成工程是目前最快的方式。按下述方式配置RCCHSE外部晶振时钟树配置为72MHz主频SWD保留调试接口否则下载一次程序后第二次就连接不上了ADC1开启两个通道比如IN0和IN1扫描模式、连续转换模式使能DMA请求使能DMA设置为Circular循环模式I2C1标准模式100kHz或快速模式400kHzGPIODHT11数据引脚设为开漏输出实际读取时需要切换方向、按键引脚设为输入模式开启内部上拉、蜂鸣器与继电器引脚设为推挽输出定时器比如TIM2设为1ms定时中断用于延时函数和显示刷新计时时钟树别超过72MHzFlash等待周期设为2。生成代码时记得把“Generate peripheral initialization as a pair of .c/.h files per peripheral”打开这样每个外设的初始化代码分开代码结构清晰很多。3.2 DHT11驱动关键代码解析与延时校正DHT11读取函数的核心时序代码如下基于HAL库和微秒级延时实现。uint8_t DHT11_Read_Data(uint8_t *humidity, uint8_t *temperature) { uint8_t buf[5] {0}; uint8_t i, j; uint8_t timeout; // 主机发送起始信号 DHT11_DOUT_HIGH(); delay_us(10); DHT11_DOUT_LOW(); delay_us(20000); // 拉低至少18ms DHT11_DOUT_HIGH(); // 设置引脚为输入模式并读取应答 DHT11_PIN_AS_INPUT(); delay_us(40); // 检测从机应答低电平 if (DHT11_DIN_READ() 1) return 1; // 没有应答 timeout 100; while (DHT11_DIN_READ() 0 timeout--) delay_us(1); timeout 100; while (DHT11_DIN_READ() 1 timeout--) delay_us(1); // 读取40位数据 for (i 0; i 5; i) { for (j 0; j 8; j) { timeout 100; while (DHT11_DIN_READ() 0 timeout--) delay_us(1); delay_us(40); // 40us后判断高低电平 buf[i] 1; if (DHT11_DIN_READ() 1) buf[i] | 0x01; timeout 100; while (DHT11_DIN_READ() 1 timeout--) delay_us(1); } } // 校验和验证 if ((uint8_t)(buf[0] buf[1] buf[2] buf[3]) ! buf[4]) return 1; *humidity buf[0]; *temperature buf[2]; return 0; }这段代码里的关键点在于第48us的判断时机。50us低电平之后紧接的是数据位高电平其中逻辑0高电平持续26-28us逻辑1高电平持续约70us不同厂家的DHT11个体差异也有偏差。我在延时40us后采样理论上可以稳定区分两种电平。如果你发现读取的数据偶发校验失败可以微调这个延时时长从35us到50us之间多试几次。微秒级延时的实现也有讲究。我试验过几种方式DWTData Watchpoint and Trace计数器延时的精度最高但需要额外的寄存器配置HAL库自带的HAL_Delay只能精确到毫秒不适用于DHT11。最终我采用了一个简单的空循环延时函数在72MHz主频下大约一个空循环1us然后用示波器校准后修正循环次数。这个做法的优点是不依赖任何定时器资源。3.3 ADC多通道采集与DMA实现ADC部分的初始化在CubeMX中完成然后代码里只需启动DMA传输并等待循环填充即可。uint16_t adc_values[2]; // adc_values[0]对应烟雾adc_values[1]对应光照 void ADC_DMA_Init(void) { HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_values, 2); } float Get_Smoke_Percent(void) { // MQ2模拟电压转换为0-100%浓度百分比需要根据实际标定调整 return (float)adc_values[0] * 100.0f / 4096.0f; } float Get_Light_Percent(void) { // 光敏模拟电压转换为0-100%亮度百分比 return (float)adc_values[1] * 100.0f / 4096.0f; }DMA采用循环模式后adc_values数组会一直被硬件自动更新主循环里随时读取都能拿到最新的转换结果。但是这里有个坑读取数组时要注意数据竞争。如果DMA正在搬运数据而你恰好在同一时刻读取数组可能会读到高低字节错位的值。解决办法是在主循环中连续读取两次如果两次结果相差不大就采用或者干脆在读取前短暂关闭DMA读完再恢复。实际影响微乎其微因为两次DMA传输之间的时间间隔是微秒级的导致这个bug的概率极低但如果你的系统对恶劣电磁环境敏感建议加一层校验。ADC采样时间需要针对不同传感器做区分。烟雾和光敏模块输出的都是缓慢变化的直流信号采样时间设长一些如239.5周期可以提高抗噪声能力。如果采样时间太短转换结果会明显抖动后面的阈值判断就会误触报警。亲身经历告诉我ADC的值抖动问题很多时候不是硬件坏了而是采样时间设置不合理。3.4 OLED显示与菜单界面代码要点OLED驱动我已经封装了一个SSD1306的基本函数库常用的包括初始化、清屏、显示字符串、显示数字、显示汉字以16x16点阵为例等。对于界面的设计我采用了“轮播菜单”的模式主界面轮播四页数据每2秒切换一页分别显示当前温湿度、烟雾浓度、光照强度和系统状态。按键短按可以在任意时刻进入菜单页此时四个按键分别承担“上移”“下移”“减小”“增大”四个功能长按返回主界面。OLED刷新的一个性能瓶颈是SSD1306通过I2C写入数据时一次写入的数据长度如果超过32字节在某些模块上就会出现显示错乱。稳妥做法是把一帧数据分多次写入每次32字节中间加一点延时。虽然会拉低刷新率但对于状态量显示而言完全够用。void OLED_ShowString(uint8_t x, uint8_t y, char *str) { while (*str ! \0) { OLED_ShowChar(x, y, *str); x 8; if (x 120) { x 0; y 2; } } }显示数据的处理上建议把所有传感器数值统一格式化为字符串后再显示比如温度显示为“Temp: 25.0 C”——显示函数只管显示转换逻辑放在数据处理模块里。3.5 主循环任务调度逻辑整个系统的主循环采用“时间片轮询”方式不使用RTOS也能实现良好的多任务调度。核心是把各任务的执行周期分开DHT11每2秒读一次DHT11的采样周期本来就不能低于1秒ADC数据由DMA持续更新OLED每200ms刷新一次显示内容按键每20ms扫描一次并做消抖处理蜂鸣器状态每100ms检查一次。这种方式的优点在于即使某个任务的执行时间偶尔超出预期比如DHT11因校验失败重读其他任务的调度节奏也不会被完全打乱。主循环代码结构如下uint32_t tick_dht 0, tick_oled 0, tick_key 0, tick_alarm 0; while (1) { uint32_t now HAL_GetTick(); if (now - tick_key 20) { tick_key now; Key_Scan(); } if (now - tick_oled 200) { tick_oled now; OLED_Update(); } if (now - tick_dht 2000) { tick_dht now; DHT11_Read_Data(humidity, temperature); } if (now - tick_alarm 100) { tick_alarm now; Alarm_Check(); } }主循环的另一个关键经验是绝对不要在中断服务函数里做耗时操作。比如DHT11的读取时序依赖微秒级延时就不适合放进定时器中断中OLED的整屏刷新也不适合在中断里搞。所有耗时操作都统一放到主循环或任务函数中中断里只置标志位。4. 多阈值调节与联动控制4.1 阈值调节的交互逻辑设计项目的亮点在“多阈值调节”这个功能的交互逻辑决定了整个系统的可用性。我的设计如下系统预设四个阈值参数——温度上限、湿度下限、烟雾浓度上限、光照度下限。每个阈值都对应一个环境控制动作。按键操作流程为【KEY1】短按在主界面各页面间切换【KEY1】长按进入阈值设置菜单【KEY2】短按在四个参数项之间移动光标【KEY3】短按当前参数值减1长按持续减少【KEY4】短按当前参数值加1长按持续增加【KEY1】再次长按保存参数并退出菜单阈值设置菜单界面上除了显示参数数值之外还会用一个小箭头指示当前选中的配置项。为了避免误触在退出菜单时还会弹出“Save? Yes/No”的确认界面选“是”才真正把数据写入Flash。这套交互逻辑和家用空调遥控器的设置方式类似。不要低估这种交互细节的重要性——如果按键处理做得毛糙用户调节阈值时会非常痛苦。4.2 Flash存储与掉电保持阈值参数存在RAM里只是一时的掉电就丢作为智能家居设备不可接受。STM32F103C8T6自带64KB Flash没有外部EEPROM也能实现参数掉电保存。我使用了最后一个Flash扇区64KB版本最后一个扇区大小为1KB起始地址0x0800FC00来存储阈值配置结构体。写入Flash的步骤如下解锁FlashHAL_FLASH_Unlock擦除目标扇区FLASH_EraseInitTypeDef配置PageAddress按字写入数据HAL_FLASH_Program锁定FlashHAL_FLASH_Lock读取时直接用指针强转结构体地址读取即可。但要注意Flash擦除次数有限不能每次修改阈值都立即写入Flash那样寿命撑不了多久。我的方案是设定10秒无操作后自动把当前参数写入Flash或者手动选择“保存并退出”时才写。同时阈值参数校验使用CRC或简单的累加校验读取出错时回退默认值避免设备开机时加载到损坏参数导致控制逻辑混乱。4.3 联动控制的阈值判断策略阈值调节最终服务于联动控制策略具体判断逻辑如下温度高于温度上限值开启散热风扇继电器吸合OLED状态栏显示“Fan ON”湿度低于湿度下限值开启加湿器/水泵继电器吸合提示“Humidity Low”烟雾浓度高于烟雾上限值蜂鸣器报警红色LED点亮同时可联动开启排风扇光照强度低于光照下限值自动开启补光灯继电器吸合这个判断逻辑必须在主循环中周期性执行并且要加入“死区”控制阈值上下的2%范围内不做动作翻转防止传感器噪声导致继电器频繁通断。举个例子烟雾阈值设为50%那么超过52%才触发报警降到48%以下才解除报警。这个细节如果不处理继电器会有非常明显的哒哒哒抖动声继电器寿命也扛不住。此外报警状态需要做“自锁”还是“自动恢复”也要想清楚。烟雾报警如果只是浓度回到阈值以下就自动解除用户可能还没来得及发现。我在项目中加了一个蜂鸣器手动关闭位报警触发后蜂鸣器一直响用户按任意键可暂时静音10分钟但OLED状态栏仍然显示报警状态直到烟雾浓度正常后才会自动清除报警标志。这和安全系统“报警必须被知悉并确认”的设计思想是一致。5. 常见问题与排查技巧实录5.1 OLED点不亮与I2C卡死的排查OLED这类问题我遇到最多热词里“oled 0.96批量点不亮”和“加了oled函数卡死”几乎每个新手都会碰到。批量点不亮的最常见原因是买到的模块像素地址不是你默认的0x3C需要改成0x3D再试另外部分模块的VCC和GND标反了接反必烧所以接线前一定要对着模块丝印看清楚。加了OLED函数后系统卡死最常见的原因是I2C通信没有超时保护。我遇到过一批OLED模块在初始化阶段如果发送指令过多、波形质量变差总线上的SCL被拉死HAL_I2C_Mem_Write一直等待总线释放就死循环了。解决办法读写I2C前先检查hI2C1的ErrorCode如果出现BUSY错误则调用HAL_I2C_DeInit然后重新初始化I2C外设。这样即使偶尔通信失败系统也能自恢复。另外还有一种诡异情况OLED的初始化函数被放在DHT11读取之前而这个初始化函数内部有500ms以上的延时期间如果触发了什么中断可能导致DHT11应答窗口错过——代码执行顺序也会影响系统稳定性要注意时序上的依赖关系。5.2 DHT11读值异常排查记录DHT11读出来一直是0或者255基本可以确定是引脚模式切换出了问题。我之前用标准库时直接操作GPIO寄存器切换推挽和输入一直工作正常。换到HAL库后为了方便使用了GPIO的AF功能配置结果数据线电平被外设强行拉低读出来的数据全是0。后来我把DHT11数据引脚在读取前显式地配置为开漏输出、写1高电平读取时切换为输入模式问题就解决了。另一个DHT11的常见问题是采样频率太高。DHT11的数据手册明确写了“采样周期不得低于1秒”我在做演示时为了OLED数字跳得快用500ms读一次结果每隔一两次就会出现校验失败。改成2秒一次后非常稳定。如果DHT11数据偶尔有跳变除了检查线路接触也要考虑供电稳定性。DHT11对供电电压的纹波比较敏感如果5V和3.3V共用一组劣质USB供电且MQ2加热丝瞬间电流波动较大DHT11的数据就会不稳定。建议DHT11电源引脚就近并联一个100nF去耦电容。5.3 ADC数据跳变与其他信号干扰问题ADC采集值一直跳这是学习过程中最常见也最让人头疼的问题。排查顺序如下先用万用表测量传感器AO引脚对地的实际电压如果电压稳定但ADC数值跳则是MCU侧的采集配置问题如果电压本身就跳则是传感器供电或模块本身的问题。采样时间设太短是引发ADC读数跳变的最常见原因。STM32F103的ADC采样时间从1.5周期到239.5周期可调我的经验是接这些高阻输出的传感器至少设置55.5周期以上最好用239.5周期。信号线太长也会引入干扰。我做了一个延长线实验用20cm杜邦线连接光敏模块AO到MCU读数在亮暗临界点附近会有轻微抖动缩短到5cm后抖动明显改善。如果项目需要长距离传输建议模拟信号线上加一个1uF电容做低通滤波或者先用运放跟随器缓冲再去ADC。还有一点容易被忽略ADC参考电压VREF的稳定性。STM32F103C8T6的VREF内部直接连到VDDA如果你用LDO给主控供电且负载变化大VDDA的纹波会直接影响ADC精度。工业级做法是给VDDA加LC滤波学习项目至少也应该保证供电纹波较小。5.4 按键消抖与菜单状态机设计阈值调节菜单如果按键处理不好体验会很糟糕。我刚开始用简单的20ms延时消抖但进入菜单后按键长按连续加值的效果没法实现按键响应也很迟钝。后来我改成了“按下沿触发一次 长按后连续触发”的模式检测到按下开始计时200ms内如果松开则触发一次短按事件如果按住超过500ms则每100ms触发一次连发事件。这种模式在菜单调节中非常实用长按快速加减参数值短按精确微调。菜单界面用状态机实现每一个菜单状态对应一个显示界面和处理函数状态转移表清晰明了。比如STATE_MAIN显示主界面KEY1短按切页KEY1长按进入STATE_MENU_SELECTSTATE_MENU_SELECT光标选择参数项KEY2移动KEY3/KEY4加减KEY1长按进入STATE_MENU_CONFIRMSTATE_MENU_CONFIRM确认保存KEY3表示“否”KEY4表示“是”这种状态机设计在后续扩展功能时也很方便。比如你需要增加一个“阈值上限/下限”分开设置的模式只需要增加两个状态即可不需要大改原有的逻辑。6. 写在最后的调试经验谈这个项目做完一遍之后我觉得最值得记住的调试经验有三条。第一硬件上的问题优先用万用表和示波器排查不要一开始就在软件里打补丁——我调试OLED卡死问题浪费了整整一个下午最后发现是杜邦线接触不良第二任何一个外设驱动写好之后先做一个最小化的孤立测试确认工作正常再接进主循环不要一口气把所有代码都集成好才调试那会让问题定位变得极难第三代码里多用模块化的函数封装比如DHT11_Read_Data返回0表示成功、1表示失败这样主循环的逻辑非常干净每个模块都能单独测试。另外这个项目后续的扩展空间很足。比如在现有ADC通道上接入土壤湿度传感器就能扩展为植物养护系统把OLED换成带触摸的屏幕菜单交互会更友好加一个ESP8266或ESP32模块就能把数据上传到云平台变成真正的联网智能家居设备。底层的传感器采集、阈值判断和联动逻辑都是通用的。按我个人经验学STM32最好的方式就是找这样一个“麻雀虽小五脏俱全”的项目从原理图开始看到代码收尾把所有外设接口都过一遍。等你把这个项目完整调通了再去接触RTOS、LWIP这些更复杂的工程你会发现底子打得特别扎实。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →