恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
STM32F1入门实战:DHT11温湿度采集与串口通信
首页
资讯中心
/
STM32F1入门实战:DHT11温湿度采集与串口通信
STM32F1入门实战:DHT11温湿度采集与串口通信
发布时间:2026/10/7 7:49:31
1. 为什么STM32F1到今天还值得花时间学我接触STM32F1系列差不多有七八年了从最早拿它点灯、调串口到后来用它做温湿度采集、电机控制、小型数据记录仪这颗芯片几乎贯穿了我整个嵌入式入门到进阶的过程。很多人现在一上来就问要不要直接上F4、H7或者国产替代我的看法是如果你连F1都没摸透换再高级的芯片也只是换个地方点灯。STM32F1系列基于ARM Cortex-M3内核主频常见72MHz片上Flash从16KB到1MB不等SRAM从6KB到96KB外设覆盖GPIO、USART、SPI、I2C、ADC、定时器、CAN、USB等基本上一个中小型嵌入式项目需要的东西它都有。它解决的问题很直接用极低的成本和学习门槛把“单片机控制外设”这件事做到标准化、可复用。适合谁来参考电子类专业学生、刚转嵌入式的软件工程师、做小批量硬件产品的开发者以及想用DHT11这类传感器快速搭环境监测节点的人。你不需要先精通寄存器手册但你需要知道每一行配置代码背后在干什么这也是我下面要重点拆的东西。2. STM32F1系列的整体设计与选型思路2.1 核心架构与资源分布STM32F1不是一颗芯片而是一个家族。常见型号按Flash和引脚数分比如F103C8T6是48脚、64KB Flash、20KB SRAMF103ZET6是144脚、512KB Flash、64KB SRAM。它们的内核都是Cortex-M3区别主要在存储容量、外设数量和封装。为什么这个设计思路重要因为你在选型时不能只看“能不能跑”还要看“跑完之后还剩多少”。我见过太多人用C8T6做带屏幕和文件系统的项目最后Flash爆了、RAM也爆了只能换芯片重画板。一个实用的估算方法是把代码编译后的.bin文件大小乘以1.2作为Flash余量把全局变量、栈、堆加起来乘以1.5作为SRAM余量。比如你的程序编译出来是45KB那至少选64KB Flash的型号如果全局变量加栈已经用了12KB那20KB SRAM的C8T6就比较紧张了。2.2 为什么外设设计适合入门与量产兼顾STM32F1的外设设计有一个很讨喜的地方寄存器结构相对规整同时官方提供了标准外设库和HAL库。标准库更贴近硬件代码量小适合资源紧张的项目HAL库抽象程度高移植方便适合快速开发。我个人的习惯是如果是小项目、对时序要求高用标准库或者直接写寄存器如果是多外设、需要跨型号移植用HAL库。这个选择背后的逻辑是标准库让你看清硬件HAL库让你看清流程。对于DHT11这种单总线传感器时序要求比较严格用标准库或者寄存器操作更容易控制微秒级延时而HAL库的延时函数在中断环境下可能会有偏差这一点后面会细说。2.3 开发环境与工具链的取舍开发STM32F1工具链无非几种Keil MDK、IAR、STM32CubeIDE、PlatformIO。Keil在国内资料最多编译速度快调试器兼容性好但编辑器体验一般STM32CubeIDE免费集成CubeMX配置工具适合新手快速生成初始化代码PlatformIO适合习惯VS Code的人库管理方便。我自己的主力组合是Keil MDK加ST-Link调试器原因是项目里经常要精确控制编译优化等级和查看反汇编Keil在这块比较顺手。如果你刚开始我建议先用STM32CubeIDE把外设配置跑通再根据项目需要决定要不要换。工具没有绝对好坏关键是你能不能快速定位问题。3. 核心细节解析与实操要点3.1 时钟树配置别让第一步就埋雷STM32F1的时钟树是很多新手的第一个坎。外部晶振常见8MHz经过PLL倍频到72MHz作为系统时钟。为什么是72MHz因为F1的Flash访问速度有限超过72MHz需要插入等待周期官方推荐的最高主频就是72MHz。配置时钟时要注意AHB、APB1、APB2的分频系数决定了外设时钟。APB1最高36MHzAPB2最高72MHz。如果你把USART1挂在APB2上波特率计算就用72MHz如果挂在APB1上就用36MHz。我见过有人串口乱码查了半天发现是时钟分频搞错了。实操建议用CubeMX配置时钟树时直接输入8MHz晶振和72MHz目标频率让它自动算分频系数然后对照手册检查一遍。标准库下SystemInit函数已经帮你配好了但如果你改晶振频率一定要重新算PLL倍频因子。3.2 GPIO模式选择输入输出不是随便设的GPIO有八种模式输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽复用、开漏复用。DHT11的数据线是单总线需要双向收发所以通常配置为开漏输出加外部上拉电阻或者推挽输出但在读取时切换为输入模式。为什么不用浮空输入因为浮空输入在悬空时电平不确定容易误触发。为什么不用内部上拉因为内部上拉电阻较大约40kΩDHT11的长线传输可能拉不高建议用4.7kΩ到10kΩ的外部上拉。推挽输出驱动LED时要注意灌电流和拉电流能力单个IO最大25mA但整个芯片有总电流限制驱动多个LED时最好加三极管或者驱动芯片。3.3 中断优先级与DHT11时序的冲突DHT11的通信协议是单总线主机发送起始信号后DHT11响应然后连续输出40位数据。每一位用高电平持续时间区分0和126到28微秒高电平表示070微秒左右表示1。这个时序对中断非常敏感。如果你在读取DHT11时开了其他中断比如定时器中断或者串口接收中断中断服务程序执行时间超过几十微秒就会导致位判断错误。我的做法是读取DHT11期间关闭全局中断或者把DHT11读取放在高优先级任务中确保不被打断。但关中断时间不能太长否则会影响系统实时性。实测下来DHT11一次完整读取大约4到5毫秒关中断这么久对大多数项目可以接受但如果你的系统有严格实时要求就要考虑用硬件定时器捕获或者DMA方式。3.4 电源与复位电路最容易被忽略的细节STM32F1的供电范围是2.0V到3.6V通常用3.3V。VDDA和VDD要接在一起VSSA和VSS也要接在一起中间可以加磁珠或者电感隔离。复位电路用10kΩ上拉加100nF电容到地这是标准配置。但很多人忽略的是BOOT0和BOOT1引脚的电平决定了启动模式。BOOT0接高电平BOOT1接低电平从系统存储器启动也就是进入BootloaderBOOT0接低电平从Flash启动。如果你用串口下载程序需要把BOOT0拉高下载完再拉低。我建议在PCB上留一个跳线帽或者按键方便切换。另外NRST引脚不要直接接大电容否则复位时间过长调试器可能连不上。4. 实操过程与核心环节实现4.1 从零搭建一个DHT11温湿度采集节点这个项目我做过很多次硬件清单很简单STM32F103C8T6最小系统板、DHT11传感器、4.7kΩ上拉电阻、ST-Link下载器、若干杜邦线。接线方式DHT11的VCC接3.3VGND接GNDDATA接PA0同时在DATA和VCC之间接4.7kΩ上拉电阻。为什么选PA0因为它支持定时器输入捕获如果以后想用硬件方式读DHT11可以直接切换。软件部分我用标准库因为时序控制更直接。第一步配置PA0为开漏输出输出高电平延时至少1毫秒让DHT11稳定。第二步主机拉低PA0至少18毫秒然后拉高20到40微秒切换到输入模式等待DHT11响应。第三步DHT11会拉低80微秒再拉高80微秒然后开始传输40位数据。每一位以50微秒低电平开始然后高电平持续时间决定数据。第四步读取完40位后校验和等于前四个字节之和的低8位。如果校验失败丢弃这次数据重新读取。整个流程的关键是延时精度。我用SysTick定时器做微秒级延时因为标准库的delay_us在72MHz下比较准。如果你用HAL库注意HAL_Delay是毫秒级微秒延时需要自己写或者用DWT。4.2 串口打印与数据可视化读到的温湿度数据通过USART1打印到电脑波特率115200。配置USART1的步骤使能GPIOA和USART1时钟PA9配置为复用推挽输出PA10配置为浮空输入设置波特率、数据位、停止位、校验位使能USART和接收中断。为什么用中断接收因为这样可以不阻塞主循环方便后续扩展命令解析。打印格式我习惯用“Temp: 25.3C, Humi: 60.2%”方便用串口助手或者Python脚本解析。如果你想做可视化可以用Python的pyserial读串口用matplotlib实时画曲线。这里有个小技巧DHT11的整数部分和小数部分要分开处理湿度小数部分通常为0温度小数部分可能是0到9但DHT11精度有限小数部分参考意义不大我一般只取整数。4.3 低功耗与定时采集的平衡如果这个节点用电池供电就不能一直全速运行。STM32F1支持睡眠、停止、待机三种低功耗模式。睡眠模式只关CPU时钟外设还在跑停止模式关掉所有时钟保留SRAM和寄存器唤醒后继续执行待机模式功耗最低但唤醒后相当于复位。我的做法是采集完数据后进入停止模式用RTC闹钟或者外部中断唤醒每隔30秒采集一次。停止模式下功耗大约20微安加上DHT11的待机功耗整体可以做到很低。但要注意进入停止模式前要把GPIO配置成模拟输入或者保持状态避免漏电流唤醒后要重新配置时钟因为停止模式会关掉PLL。实测下来用停止模式加RTC唤醒两节AA电池可以撑几个月。4.4 代码结构让项目可维护我见过很多人的STM32代码全部堆在main.c里几百行下来根本没法维护。我的习惯是按功能分文件dht11.c和dht11.h负责传感器驱动usart.c和usart.h负责串口delay.c和delay.h负责延时main.c只负责初始化和主循环调度。头文件里用宏定义引脚和端口方便换引脚。比如#define DHT11_PORT GPIOA #define DHT11_PIN GPIO_Pin_0这样如果硬件改版只需要改这两行。另外DHT11的读取函数返回一个结构体包含温度和湿度以及一个状态标志。状态标志用来区分“读取成功”“校验失败”“超时无响应”方便上层处理。这种分层设计在项目变大时优势非常明显。5. 常见问题与排查技巧实录5.1 DHT11读取失败的原因排查DHT11读不到数据我总结下来无非几种情况。第一上拉电阻没接或者阻值太大数据线拉不高。第二延时不准尤其是用HAL库的HAL_Delay做微秒延时实际误差可能很大。第三中断干扰读取过程中被其他中断打断。第四供电不足DHT11工作电流虽然不大但瞬间拉低可能导致电压跌落。第五传感器损坏DHT11比较脆弱焊接温度过高或者静电都可能损坏。排查顺序先用示波器或者逻辑分析仪看数据线波形确认主机起始信号和DHT11响应是否正常然后检查延时函数用示波器测量实际延时最后检查中断配置。如果没有示波器可以用LED指示读取状态比如读取成功亮绿灯失败亮红灯通过串口打印错误码。5.2 串口乱码的几种典型原因串口打印乱码最常见的原因是波特率不匹配。检查你的串口助手波特率和代码里设置的是否一致。第二个原因是时钟频率不对比如你外部晶振是8MHz但代码里按12MHz算波特率结果肯定错。第三个原因是GPIO配置错误TX和RX接反了或者没有配置为复用模式。第四个原因是电源干扰尤其是电机或者继电器附近串口线没有屏蔽或者没有共地。我的排查方法是先用一个最简单的发送固定字符串的程序测试排除传感器和业务逻辑的干扰然后用示波器测量TX引脚波形计算实际波特率最后检查硬件连接和电源。5.3 程序下载失败与调试器连接问题ST-Link连不上芯片提示“No target connected”先检查接线SWDIO、SWCLK、GND、3.3V四根线是否接好。然后检查芯片是否在运行状态如果程序里把SWD引脚配置成了普通GPIO调试器就连不上了。解决办法是按住复位键点击下载然后松开复位键让芯片在复位后短暂进入调试模式。或者把BOOT0拉高从系统存储器启动这样芯片不会运行用户程序调试器可以连接。另外Keil的调试设置里Debug选项卡要选ST-Link DebuggerSettings里Port选SWMax Clock可以调低一点比如1MHz提高连接稳定性。5.4 常见问题速查表问题现象可能原因排查方法解决措施DHT11无响应上拉电阻缺失测量数据线空闲电平接4.7kΩ上拉电阻温湿度数据跳动大延时不准示波器测延时用SysTick或DWT做微秒延时串口乱码波特率或时钟错误检查时钟树和波特率计算重新配置时钟和USART程序下载失败SWD引脚被占用检查GPIO配置复位后下载或拉高BOOT0功耗偏高未进入低功耗模式测量电流配置停止模式加RTC唤醒校验和错误中断干扰关闭中断再读取读取期间关全局中断6. 进阶扩展与项目演进方向6.1 从单节点到多节点组网单个DHT11节点只能测一个点如果要覆盖一个房间或者大棚就需要多个节点组网。STM32F1自带CAN控制器可以用CAN总线组网抗干扰能力强适合工业环境。也可以用RS485加Modbus协议成本低布线简单。如果不想布线可以用NRF24L01无线模块SPI接口STM32F1驱动起来很方便。我的经验是节点数量少、距离近用无线节点多、距离远、环境复杂用CAN或者RS485。组网后需要一个主机做数据汇聚主机可以用STM32F1加显示屏或者用树莓派做网关上传到服务器。6.2 数据存储与断网续传环境监测往往需要记录历史数据。STM32F1的Flash可以模拟EEPROM存储少量数据如果要存大量数据可以外挂SPI Flash或者SD卡。SD卡用SPI模式驱动FATFS文件系统可以存CSV文件。断网续传的思路是数据先存本地网络恢复后按时间戳补传。这里要注意Flash的擦写寿命STM32F1的Flash擦写次数约1万次如果频繁写同一页很快就坏了。解决办法是用磨损均衡算法或者外挂EEPROM。我一般用W25Q64 SPI Flash容量8MB擦写次数10万次足够存几个月的数据。6.3 固件升级与远程维护产品部署到现场后如果发现bug或者要加功能不可能每次都拆下来烧录。STM32F1支持IAP也就是在应用编程。思路是把Flash分成Bootloader区和App区Bootloader负责接收新固件并写入App区App负责业务逻辑。升级方式可以是通过串口、CAN、无线模块接收固件包。关键点是Bootloader要足够稳定升级过程中断电不能变砖App区要有版本号和校验防止写入错误固件。我做过一个项目用串口升级Bootloader只占8KBApp区从0x08002000开始升级时先擦除App区再按页写入最后校验CRC。实测下来升级一个60KB的固件大约需要十几秒。6.4 结合RTOS提升多任务处理能力当项目功能变多比如同时采集传感器、刷新屏幕、处理串口命令、记录数据裸机的前后台架构就会力不从心。这时候可以上FreeRTOS。STM32F1的SRAM有限C8T6只有20KB跑FreeRTOS要精打细算。我的配置是任务栈不要开太大空闲任务和定时器任务用默认用户任务栈根据实际使用调整一般128到256字足够。任务间通信用队列和信号量避免全局变量竞争。DHT11读取放在一个低优先级任务里串口命令解析放在高优先级任务屏幕刷新放在中优先级。这样即使DHT11读取时关中断也不会影响串口响应因为串口中断优先级更高。7. 我个人在实际操作中的几点体会踩过几次坑之后我越来越觉得STM32F1的难点不在代码本身而在对硬件时序和电气特性的理解。DHT11这个传感器便宜、简单但它的单总线协议对延时和中断非常敏感正好可以用来训练你对时序的敏感度。我建议每个刚接触STM32F1的人都拿DHT11练手不是因为它有用而是因为它能让你把GPIO、延时、中断、串口这几个基础外设串起来。另外不要迷信库函数该看手册的时候一定要看手册尤其是时钟树、GPIO模式、中断向量表这几块手册里的图比任何教程都清楚。最后分享一个小技巧调试时序问题时与其反复改代码猜不如花几十块钱买个逻辑分析仪波形一看就明白省下来的时间远超这点成本。这个项目后续还可以扩展成带屏幕显示的桌面气象站或者加上ESP8266模块上传到物联网平台都是很自然的演进方向。