ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32F1入门实战:从Cortex-M3到DHT11温湿度驱动

STM32F1入门实战:从Cortex-M3到DHT11温湿度驱动 1. STM32F1到底是个什么定位1.1 一颗芯片撑起整个入门生态先说结论STM32F1是基于ARM Cortex-M3内核的32位微控制器主频最高72MHzFlash从16KB到512KB不等RAM从4KB到64KB。这个参数放在今天看并不惊艳但它几乎是整个中文互联网上教程最多、资料最全、开发板最便宜的单片机没有之一。你在淘宝搜STM32F1能看到几块钱一片的裸芯片、十几块的迷你开发板、几十块带屏幕带传感器的套餐板价格低到你甚至不好意思说它是32位平台。很多人的嵌入式生涯就是从这一颗Cortex-M3开始的。为什么F1能火这么多年硬件上它确实没什么黑科技但生态带来的价值远超过芯片本身。无论是刚学会C语言的大学生还是从51单片机转过来的电工第一句看到的教程几乎都是STM32F1是什么。配套的库函数、标准外设库、HAL库、寄存器版本的例程、各种传感器的驱动代码在GitHub和CSDN上多到看不完。STM32F1这颗芯片的出现把原来只属于工程师的32位单片机门槛拉低到了普通电子爱好者的桌面这让它成了事实上的行业入门教材。我自己最早的STM32F1项目就是用一个蓝板子就是那种十几块钱的STM32F103C8T6最小系统板驱动DHT11温湿度传感器把温湿度数据打到串口助手和一块0.96寸OLED上。很多人以为做这种项目只是为了好玩但实际上它把GPIO、延时、串口、I2C或SPI、传感器时序这些东西全串起来了。做完这一个后面再玩任何外设都不会再有无从下手的感觉。1.2 F1家族成员怎么分清楚STM32F1不是一个具体型号而是一个庞大的家族。最常用的子系列有F101、F102、F103还有一些带后缀的如F105、F107。其中F103系列是绝对的销量冠军也是最合适拿来学习的。F101基础型主频36MHz没有USB、没有CAN功能少但便宜。F102USB基本型带USB设备控制器适合做USB转串口这种应用。F103增强型主频72MHzFlash、RAM、外设数量都最均衡是学习首选。F105/F107互联型带USB OTG和以太网MAC适合做网络类产品。芯片命名里还有容量标识需要看懂。比如STM32F103C8T6中间的C8表示64KB FlashC代表48引脚封装8代表64KB。同理R6是64引脚32KBZ是144引脚。后面T6指示封装是LQFP48温度范围是-40℃到85℃。这些信息在选型时很重要因为同一个F103引脚数不同、Flash不同外设复用关系也不一样代码虽然通用但接线和启动文件却有区别。我见过不少人买了STM32F103ZET6144脚大板把例程烧到STM32F103C8T648脚小板上结果下载报错或者程序跑飞。原因就在于Flash大小不一样启动文件可能选错。所以确认型号再选启动文件是第一课。这颗芯片能做的事可以从一个很直观的维度来理解它相当于把一台主频72MHz的小电脑、一组丰富的I/O口和通信接口集成到一个芯片里Cortex-M3内核支持硬件乘除法指令跑一些轻量级的算法、状态机、Modbus协议栈、PID控制完全没问题。虽然它没有MMU跑不了Linux但针对采集传感器数据、控制电机、与人机界面交互这类典型的物联网终端任务STM32F1是性能和成本的平衡点。2. 开发环境与工程搭建完整指南2.1 工具链怎么选开发STM32F1的主流方案大致有三套标准外设库Standard Peripheral Library简称SPL、HAL库Hardware Abstraction Layer、寄存器直接操作。三套方案各有侧重没有绝对的优劣。标准库是ST官方在F1时代主推的封装库把寄存器操作封装成一个个函数和结构体比直接写寄存器省心比HAL更轻量。ST在2019年前后停止了对标准库的维护但它依然是F1最常用、历史资料最多的库。大量老教程、CSDN博客都是基于标准库写的。HAL库是ST现在主推的库配合STM32CubeMX图形化配置工具使用可以自动生成初始化代码跨系列移植性好。代价是函数调用层级深、代码量大、效率略低但在F1上跑HAL库绰绰有余。寄存器操作就是直接读写寄存器地址效率最高、对芯片理解最深但开发速度慢只适合研究原理或做极限性能优化。我给新人的建议是如果只是学F1用标准库加一句注释ST公司已停止更新但很经典如果你打算以后往F4、F7、H7系列走直接学HAL库更划算因为CubeMX生成的代码风格在各系列间是统一的。我自己在开发产品的时候更多是用HAL库配合CubeMX做工程骨架再自己写业务逻辑。开发IDE方面最主流的组合是Keil MDK。虽然界面老旧但启动快、调试方便、破解教程遍地都是。CLion搭配STM32CubeMX插件和OpenOCD也是不错的选择对代码补全和Git支持比Keil好太多适合喜欢现代IDE的开发者。如果你装了VS Code也可以用PlatformIO它内置了F1的支持写代码体验也不错。工具链的差异不会影响最终程序选自己用着舒服的就好。2.2 最小系统和工程创建的坑STM32F1最小系统非常简单核心就是四块3.3V电源、复位电路、8MHz晶振外部晶振可以省略但建议保留、BOOT0引脚处理。这块内容网上资料很多我只讲实际板子上最容易出问题的三件事。第一是电源。F103的IO引脚对外输出能力有限制典型值是灌电流25mA、拉电流20mA左右。直接用IO驱动继电器、电机这类大功率负载基本不现实必须通过三极管、MOS管或专门的驱动芯片来扩展。很多新手做DHT11这类传感器项目没问题但一驱动的器件多了3.3V稳压器就发热甚至重启这时候要检查是不是某路负载电流超标了。第二是外部晶振。CubeMX生成的工程里如果选择了外部晶振HSE作为系统时钟源但板子上没焊接晶振程序就会卡在初始化等待HSE就绪的死循环里表现就是程序下载后不运行、调试停在HAL_RCC_ClockConfig处。开发板通常默认有8MHz晶振但自己用最小板做东西时一定要确认。第三是启动模式。STM32F1有两个引脚BOOT0和BOOT1控制启动方式。BOOT0拉低从主Flash启动这是正常模式BOOT0拉高且BOOT1拉低会从系统存储器启动即进入串口ISP下载模式BOOT0和BOOT1都拉高则从内置SRAM启动主要用来调试。开发板一般都设置好了但如果自己做板子BOOT0最好加一个10k下拉电阻避免悬空导致启动状态不稳定。工程创建方面如果你用CubeMX流程是选芯片型号→配置时钟树外部晶振8MHz→倍频到72MHz→配置外设串口、GPIO等→生成工程代码。这里有个关键参数别搞错CubeMX中HCLK要改成72MHz如果只改倍频不选总线分频可能导致外设时钟频率不符合预期。我自己见过好多人在这一步选择了72MHz但又同时默认打开了某个PLL分频结果系统时钟跑在36MHz串口波特率怎么调都不对。2.3 烧录和调试方式区别STM32F1支持三种常见烧录方式JTAG、SWD、串口ISP。JTAG需要20针接口速度快、功能多但占用的引脚也最多PA13-PA15、PB3-PB4等。SWD只需要两根线SWDIO和SWCLK加上地线共三根非常节省引脚是所有开发板的标配调试接口。我日常调试都是用ST-Link V2通过SWD连接便宜、稳定、速度快推荐新手直接买。串口ISP则是在没有调试器的时候用串口下载程序。把BOOT0拉高、按一下复位、通过串口工具选择hex文件烧写。这种方式的缺点是只能下载程序不能在线调试没有断点、没有变量观察效率和体验都差不少。但作为一种备用方案特别在调试器丢失或者目标板不适合接SWD时还是很管用的。调试器驱动也是新人容易卡住的地方。装完ST-Link驱动后设备管理器里如果看不到ST-Link设备通常是驱动没装好或线序接错了。SWD接线其实就三个信号SWDIO、SWCLK、GND目标板要上电调试器也要识别到目标芯片。有一种特例如果用SWD连接不上先按住复位键连接前一瞬间松开有时候能绕过低电平锁定状态。3. 核心外设的技术要点与原理剖析3.1 GPIO不是简单的高低电平STM32F1的GPIO比51单片机复杂得多每个引脚都可以配置成输入、输出、复用、模拟四种模式每种模式又有细分。最常用的输出模式是推挽输出Push-Pull和开漏输出Open-Drain。推挽输出能主动输出高电平和低电平带负载能力强日常驱动LED、控制逻辑电平都用这个。开漏输出只能主动拉低或释放高阻态高电平需要外接上拉电阻提供常用于I2C这类需要线与的总线或者需要实现电平转换的场景。输入模式中浮空输入、上拉输入、下拉输入要分清。如果外接的传感器在无信号时处于高阻态就要用上拉或下拉输入来固定电平否则引脚会悬空检测到的电平随机跳变。DHT11正好就是这种情况它空闲时靠外部上拉电阻维持高电平主机要读数据时引脚要先处于输出模式发送起始信号再切回输入模式读取响应和数据。GPIO模式的切换是DHT11驱动里最核心的一环很多人第一次接触一个引脚既要输出又要输入会懵搞懂这一点就等于入门了。GPIO还有一个重要知识点是复用功能AFIO。USART的TX/RX、定时器的PWM输出、SPI和I2C的信号线都是通过复用功能映射到具体引脚的。F1的引脚复用不像F4那么自由一个外设引脚基本上固定在某一组引脚上所以查数据手册的Alternate function mapping表格比死记硬背更高效。我自己做硬件接线之前一定会把CubeMX引脚配置页面打开先把外设引脚分配好再画原理图这样可以避免后面软件里发现引脚冲突又要改板子的尴尬。3.2 定时器F1最值钱的外设STM32F1内部有多个定时器按功能分成三类基本定时器TIM6/TIM7、通用定时器TIM2/3/4/5、高级定时器TIM1/TIM8。基本定时器只能做定时通用定时器在定时基础上还能输出PWM、做输入捕获、编码器接口高级定时器则多了互补输出和刹车功能专门为电机控制设计。定时器定时的本质是一个计数器在一个时钟源驱动下不断累加从0数到自动重载值ARR数到顶就产生更新事件并将计数器清零。只要算好预分频值PSC和自动重载值就能得到任意想要的定时时长。公式很简单定时时间 (PSC1) × (ARR1) / 定时器时钟频率。比如定时器时钟72MHz想让定时器1ms溢出一次可以设PSC71即72分频那么计数频率变成1MHz即每1us计数一次ARR999表示从0数到999需要1000us正好1ms。这个计算方式几乎所有F1开发者都躲不开做延时、做周期采样、做按键消抖、做PWM输出都要用到。PWM输出就是让定时器的输出引脚在计数器小于某个比较值CCR时输出高电平大于时输出低电平通过改变CCR就能改变占空比通过改变ARR能改变频率。用这种方式驱动LED渐亮渐暗、控制舵机角度、控制直流电机转速都是最基础的项目。定时器输入捕获则用于测量外部信号的频率或脉宽。原理是当引脚出现上升沿或下降沿时硬件自动把当前计数器的值保存到捕获寄存器里软件再算出前后两次捕获的差值乘以计数周期就是时间。超声波测距模块HC-SR04就是靠这个功能测量回波时间来计算距离的。可以说用好定时器你的STM32F1水平已经超过一半的新手了。3.3 USART串口通信的几个细节USART通用同步/异步收发器是STM32F1里最常用、也最能反映一个工程师基本功扎实与否的外设。它的核心任务是把并行数据变成串行数据按帧发送以及从串行数据恢复出并行数据。说到串口很多人第一反应是能打印日志就行但实际调试中串口往往给你埋了很多坑。第一个坑是波特率误差。串口通信要求收发双方的波特率误差不能太大一般容差在2%到3%以内。STM32F1的波特率由串口时钟、USARTDIV分频值共同决定。如果你的系统时钟不是精确的72MHz比如用了内部HSI 8MHz经过PLL倍频到64MHz而不是72MHz那么用57600、115200这类波特率时误差可能超标导致乱码。排查串口乱码首先要确认系统时钟而不是怀疑代码逻辑。第二个坑是引脚复用和重映射。F103的USART1固定在PA9TX和PA10RX但也可以重映射到PB6和PB7USART2可以重映射到PD5和PD6。很多开发板的丝印上标了A9/A10 串口1但有些板子为了布线方便把串口重映射到了别的引脚代码里如果不开启AFIO重映射数据从错误引脚出来自然收不到。在CubeMX里配置串口时它会自动检查引脚冲突并给出提示这也是我推荐新手先用CubeMX的原因之一。第三个坑是接收中断的使用方式。很多例程都用接收一个字节进一次中断的方式但实际项目里数据往往是一帧一帧到达的比如一个协议帧8个字节。正确做法是在中断里把每个字节存进环形缓冲区主循环再按帧解析。直接在主循环里用阻塞方式等待接收数据会严重拖慢系统而且在高速通信时几乎必定丢字节。串口还有一个容易被忽略的功能半双工模式下的单线通信。DHT11实际上是单总线协议跟串口半双工模式有点类似但DHT11的时序是微秒级别的USART不一定能直接适配它所以更多人选择GPIO模拟时序而不是硬接串口。这个我在第四部分详细说。3.4 I2C和SPI在F1上的注意事项I2C和SPI是另外两个必须掌握的总线协议。I2C用两根线SCL时钟线、SDA数据线就能挂多个设备每个设备有独立地址适合连接温湿度传感器、EEPROM、OLED这类低速设备。SPI用四根线SCLK、MOSI、MISO、CS通信速率高适合Flash存储器、SD卡、显示屏这类需要高速传输的设备。STM32F1的硬件I2C模块历史上口碑不佳主要是它的状态机设计复杂程序容易进BUG早期网上甚至流传F1的I2C有问题最好用软件模拟。实际情况是硬件I2C能用但注意中断处理和超时判断的细节很多。很多成熟的工程师在F1上宁可GPIO模拟I2C也不愿意花时间去调硬件I2C的坑。SPI则稳定得多直接使用硬件SPI配合DMA可以做到高速无CPU干预传输。这里给一个选型思路如果你在F1上连接的是一个地址固定的传感器比如DHT11、DS18B20、OLED单总线或I2C软件模拟最顺手如果连接的是大容量数据如W25Q64 Flash、SD卡用硬件SPI加DMA。做项目优先考虑稳定性和自己熟悉的方式而不是一味追求用硬件外设的好看名头。4. 实战基于STM32F1驱动DHT11温湿度传感器4.1 DHT11传感器的工作机制DHT11是一个典型的单总线温湿度传感器内部包含一个电阻式测湿元件、一个NTC测温元件和一个8位单片机。它通过一根数据线DATA与主机通信在同一根线上既发送数据又接收指令数据的每一位都是靠高低电平的宽度来区分的。它一次通信需要传输40位数据8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、8位校验和。校验和的计算方式是前四个字节相加低八位如果等于第五个字节就认为传输无误。DHT11的精度并不高湿度精度±5%RH温度精度±2℃测量范围湿度20%-90%RH、温度0℃-50℃。所以它更适合做环境监测、智慧农业的简单采集、室内舒适度判断这类对精度不敏感的场景而不是实验室级别的精密测量。如果项目需要高精度应该选SHT30或HTU21D这类数字传感器。DHT11的供电范围是3.3V到5.5V所以5V也可以驱动。但在STM32F1这样的3.3V系统里要注意如果传感器模块板上带有稳压和上拉电路接到3.3V最保险如果板子是直接用5V供电的DATA引脚输出的高电平也可能接近5V直接连到STM32的3.3V引脚上可能超压。针对这一点最稳妥的电路设计是模块用3.3V供电数据线上接一个4.7kΩ到10kΩ的上拉电阻到3.3V。多数市售模块自带板载上拉电阻但自己做板子时务必自己加。4.2 通信时序详解读懂这张图就成功了DHT11单总线通信的时序非常固定可以分成四个阶段。第一次接触的人先去网上找一张时序图照着看就会发现所谓的驱动其实就是在正确的时间点把GPIO拉高或拉低。第一个阶段是主机发送起始信号。主机先把数据线拉低保持至少18ms实际编程中一般用20ms然后释放总线拉高让上拉电阻把电平拉回高电平。这个低电平脉冲的作用是让DHT11从低功耗模式唤醒并准备应答。第二个阶段是DHT11响应。主机释放总线后DHT11会在20us到40us之后把总线拉低持续约80us表示我准备好了随后再拉高约80us告诉主机准备开始传数据。如果这个响应信号迟迟不来多半是接线错误、上拉电阻缺失、或者传感器供电异常。第三个阶段是数据位传输。DHT11每发送一个数据位都先把总线拉低约50us作为位同步信号然后释放总线。释放之后总线维持高电平的时间决定了这位是0还是1如果是0高电平持续约26us到28us如果是1高电平持续约70us。简单说低电平时间固定高电平时间区分0和1。第四个阶段是通信结束。DHT11发完40位数据后把总线拉低约50us表示结束然后释放总线进入空闲状态等待下一次主机起始信号。这四十个比特的读取方式是在检测到每个数据位开头的下降沿之后延时30us到40us再读取引脚电平。如果引脚为高说明这一位是高电平约70us的1如果引脚为低说明这一位是0。这个延时窗口非常关键如果延时太短可能读在高电平的起始处如果太长又可能错过。实践中常见的做法是延时40us后读一次因为在40us这个点上0的26us高电平已经结束了引脚回到低而1的70us高电平还在持续区分效果最好。4.3 从零写一版DHT11驱动代码先讲硬件连接。以STM32F103C8T6最小系统板为例DHT11模块的VCC接3.3VGND接GNDDATA接PA0。如果你的模块没有板上上拉在PA0和3.3V之间加一个10kΩ电阻。接线完成后的初始化十分简单把PA0配置为推挽输出先输出高电平让总线处于空闲状态。然后是微秒级延时函数的问题。HAL库的HAL_Delay()只能保证毫秒级精度DHT11的时序需要几十微秒的延时所以必须自己写一个us级延时。最简单可靠的方案是用SysTick定时器做一个基准。SysTick是一个24位递减计数器给它配置好重载值后每过1us产生一次中断用一个全局变量累加delay_us()函数读这个变量的差值来实现延时。在72MHz主频下重载值设72-1就是1us一次中断。但这个方案有一个缺点中断频繁会影响CPU效率而且要求你已经正确配置了SysTick。另一个常见方案是for循环空转粗略延时void delay_us(uint32_t nus) { // 72MHz下约循环8次接近1us实际需用示波器或逻辑分析仪校正 for (uint32_t i 0; i nus * 8; i) { __NOP(); } }这种方式的精度和编译器优化等级直接相关必须在-O0或当前优化等级下实测校正。项目正式场合更推荐用定时器方式做us延时。我个人的习惯是统一用一个TIM定时器做微秒延时服务不管是OLED驱动、DS18B20还是DHT11都用它这样时序精度可控又不互相干扰。DHT11主机起始信号发送和读取代码的核心结构如下// 发送起始信号 void DHT11_Start(void) { GPIO_SetPinOutput(DHT11_PORT, DHT11_PIN); // 改为输出模式 GPIO_WriteLow(DHT11_PORT, DHT11_PIN); // 拉低总线 delay_us(20000); // 至少18ms用20ms稳妥 GPIO_WriteHigh(DHT11_PORT, DHT11_PIN); // 释放总线 delay_us(30); // 释放后等30us再切输入 GPIO_SetPinInput(DHT11_PORT, DHT11_PIN); // 改为输入模式 }注意这里GPIO模式切换的代码是驱动里最容易出错的地方。如果默认把GPIO配置成了推挽输出那么输入模式下必须把引脚脚配置为上拉输入或浮空输入同时外部又有上拉电阻才能正确读到高电平。如果固件库配置不当读到的一直是低电平DHT11的数据就全是0。读取一个字节和读取一帧数据的代码逻辑uint8_t DHT11_ReadByte(void) { uint8_t value 0; for (int i 0; i 8; i) { // 等待低电平开始位同步信号 while (GPIO_ReadPin(DHT11_PORT, DHT11_PIN) 0); delay_us(40); // 重点延时40us后读电平 value 1; if (GPIO_ReadPin(DHT11_PORT, DHT11_PIN) 1) { value | 1; } // 等待高电平结束进入下一位 while (GPIO_ReadPin(DHT11_PORT, DHT11_PIN) 1); } return value; }完整读取一帧的过程是先启动通信拉低20ms→释放→等应答然后读取5个字节。读完后先做校验如果byte[0]byte[1]byte[2]byte[3]的低八位等于byte[4]这帧数据有效湿度是byte[0]温度是byte[2]。如果校验失败建议丢弃这一帧过2秒再重新采样。DHT11推荐的采样周期是1秒以上你采集再频繁它也不会给出新数据反而可能让时序错乱。另外每次读取前主机起始信号至少18ms但也不能太长。有人图省事把20ms改成50ms甚至100ms这在多数模块上也能工作但不推荐。DHT11在唤醒后需要准备时间响应主机如果起始低电平过长它的内部单片机可能已经把这一电平当成了一次通信开始时序就会乱。严格按照数据手册是20ms实测下来这是最稳的。4.4 实测数据与调试方法把上面的代码下载进STM32F1通过串口打印湿度、温度数据不出意外的话你应该能看到类似Humidity: 56.0% Temperature: 24.0°C这样的输出。如果你看到的湿度或者温度是0或者数据跳动剧烈不要急着改代码先按顺序排查接线是否正确DATA是否真的接在PA0上模块供电电压是否在规格内。数据线上的上拉电阻是否正常。无上拉时空闲高电平无法维持通信必失败。读取时序中40us的延时是否准确。用逻辑分析仪抓取总线波形对比提前提到的时序图看高电平宽度是否在28us和70us附近。是否在上一次通信结束后等够了1秒以上再发下一次起始信号。我在实际调试时最常用的工具是逻辑分析仪几十块钱的那种就行把DHT11的数据线接到分析仪通道上抓一次完整通信过程直接看波形。比盲改代码高效得多。如果你想自己确认延时的误差也可以在GPIO翻转上做一个辅助引脚比如每次读完一个位翻转一次PB0用示波器测PB0的波形周期来反推代码运行时间。DHT11在量产级项目里其实不是首选它的精度和一致性只能说够用。做一批产品不同批次的DHT11读出来的值可能有差别这是器件本身的一致性决定的。对电路可靠性和数据准确性要求高的项目DHT11只能用来做功能演示和原型验证不能直接用在计量级产品上。这是选择传感器时必须清楚的认知。5. 常见问题与排查技巧实录5.1 程序下载失败的三种典型原因下载失败是STM32F1新人遇到最多的故障我总结基本逃不出三类。第一类是调试器连接不上。典型现象是Keil报No target connected或者Connection error。原因通常是SWD线序接反、目标板没供电、或者芯片上一次烧录时禁用了SWD引脚。前两个好解决第三类比较隐蔽如果代码里将PA13或PA14也就是SWDIO和SWCLK配置成了普通GPIO并输出调试接口就会被关闭。解决办法是在下载时按住复位键点击下载后马上松开让芯片在复位瞬间能被调试器连接上。根治办法是把这两个引脚设置为复用、不使能SWD调试引脚的方案换用串口ISP烧录恢复。第二类是能识别芯片但擦除失败。常见原因是芯片读保护被开启。在Keil的Flash Download页面勾选Reset and Run之外还要确认没有开启读保护或者通过调试器的整片擦除命令解除保护。老版本固件库的某些擦写代码也可能触发意外保护这种情况换新版固件库就行。第三类是程序能烧进去但不运行。这里要先查BOOT0引脚的电平再查复位电路。个别开发板SWD下载完会自动复位运行有些板子则需要手动按复位键。另外检查CubeMX配置中System Core SYS里的Debug选项如果选的是No Debug调试器在复位后可能无法接管芯片。解决方法是把这个选项改为Serial Wire这也是很多开发板程序跑不起来的隐藏原因。5.2 时钟配置错误引发的诡异故障时钟配置错误的表现非常多样串口波特率不对、定时器计时不准确、PWM频率偏差、系统运行变慢但功能正常。这些问题的实质性原因是芯片实际运行的时钟频率和你代码里假设的频率不一致。F103外部晶振通常为8MHz经PLL倍频后得到72MHz系统时钟。如果外部晶振换成了12MHz而代码里按照8MHz配置PLL实际系统时钟会变成108MHz超频到极限可能导致程序不稳定反过来如果外部晶振没有焊接CubeMX默认HSE方式初始化时程序直接卡死等待。所以第一步永远是确认硬件上实际使用的晶振频率再去配置时钟树。第二种情况是HSI内部8MHz时钟作为PLL输入默认情况下PLL倍频系数如果设定为9得到72MHz也无问题但HSI精度不如外部晶振误差可达1%串口通信的波特率误差就会偏高。如果项目有串口通信需求建议外部晶振必须存在同时做通信测试时用示波器测TX引脚的实际波形频率才能确认时钟是否精确。调试时想快速判断系统时钟最直接的方式是在主循环里周期性翻转一个GPIO用示波器或逻辑分析仪测它的精确频率。如果翻转周期为100ms如HAL_Delay(100)加翻转实测却是110ms那说明系统时钟跑在了实际频率的90%左右。5.3 DHT11数据异常的专项排查DHT11这类单总线传感器出问题时现象往往就那么几种但背后的原因各不相同。数据全部是0通常是GPIO没有正确切换到输入模式或者切换后引脚被配置成浮空输入但缺少上拉电阻总线空闲时电平不确定读到的一直是0。处理方法是检查配置代码确保空闲态为高。数据全是255通常是GPIO输入配置成了上拉输入但传感器发送的低电平不能把总线拉到足够低的水平多数情况是因为模块上DATA没有正确接地或者接线松动。数据随机跳变一般是时序延时不准确40us的延时偏差过大。建议用定时器延时替代for循环延时并抓波形对照。第一次读取失败、第二次成功因为DHT11上电后的稳定时间不够。在初始化完GPIO后先延时1秒再开始第一次通信能缓解这个问题。还有一个容易忽略的点DHT11模块的DATA引脚如果和STM32之间串了一个过大的电阻比如10kΩ串联而非上拉会让电平转换变慢导致时序边沿不陡峭。用示波器看波形的话可以发现上升沿缓慢上升而不是陡峭跳变这种情况下通信都会变得不稳定。正确的接法是上拉电阻接到3.3V而不是在数据线上串联电阻。5.4 性能优化与资源占用心得STM32F1的性能上限是72MHz在当代MCU里不算强但用好它的资源需要一些思路上的转变。第一是DMA一定要用起来。串口发送、SPI读写、ADC采样这些场景都适合DMA。用DMA搬运数据时CPU可以去做别的事情处理几十个字节的协议帧、刷新OLED、读取按键扫描都不会卡顿。很多人觉得DMA复杂不敢碰其实它就是把源地址、目标地址、长度设置好然后启动传输完成时触发中断你就收到了一个搬运完成了的通知。第二是中断优先级要规划。NVIC中抢占优先级和子优先级各用多少位由SCB-AIRCR寄存器决定。一个实用的配置是用优先级分组2即2位抢占优先级、2位子优先级这样最多可以安排4个抢占优先级等级足够大多数项目使用。SysTick应该设置为最低优先级它只是一个时基基准不应该打断关键通信中断。第三是结构体加状态机的编程方式远比散乱的全局变量好维护。DHT11的采集状态可以定义为一个枚举IDLE、START、WAITING、READING、DONE主循环里根据状态执行对应的步骤。这种写法在一个项目里有多个传感器时尤其有用不会出现一个传感器读数据时另一个传感器被阻塞的情况。6. F1之后往哪儿走我在做完DHT11这个项目之后又陆续用STM32F1做过遥控小车、倒车雷达、电压采集记录仪、简易逻辑分析仪。这颗芯片帮我把C语言、硬件电路、调试方法论串成了一条完整的技能链。如果你已经能用F1驱动温湿度传感器并且掌握了信号时序分析、异常排查这些方法接下来可以考虑往两个方向走。一个是往更高的MCU平台走。STM32F4系列有Cortex-M4内核和FPU主频跑到168MHz甚至更高DSP指令让数字信号处理应用得心应手适合做FOC电机控制、音频处理、更多的传感器融合。H7系列则是双核Cortex-M7加M4性能更强但学习曲线也陡。你从F1转到F4CubeMX生成的工程骨架几乎一样HAL库API风格的差异也不大最大的工作量在熟悉新外设的特性和引脚复用关系。另一个方向是往物联网系统走。STM32F1跑个轻量级RTOS如FreeRTOS加上Wi-Fi模块ESP8266或ESP32就能组成一个温湿度数据上云的终端。这时你会接触到协议栈、MQTT、JSON解析、远程OTA这些东西原来的单片机程序从裸机跑一个大循环变成OS调度的多任务系统复杂度又上一个台阶。但这正是F1在物联网终端里最常见的真实形态MCU负责本地数据采集和控制通信模块负责上云。回到标题本身STM32F1系列真正不可替代的价值是它用极低的成本提供了一个足够复杂的硬件平台迫使每个开发者去理解时钟树、中断、GPIO复用、总线时序这些底层的嵌入式概念。你把F1吃透了后面学任何别的MCU都会很快因为MCU的内核、外设、开发方法大同小异差的只是细节。至少我自己的经历是这样当年花了一个星期让DHT11的数据成功出现在OLED上那种成就感直到今天都还在支撑我继续写代码。
返回列表