FEATURED · 精选文章

STM32 HAL库驱动DHT11温湿度传感器:单总线时序与微秒延时实战解析

发布时间 / 2026/9/9 12:08:13
来源 / 创域科博编辑部
栏目 / 资讯中心
STM32 HAL库驱动DHT11温湿度传感器:单总线时序与微秒延时实战解析 简介适用于STM32F103C8T6的HAL库DHT11温湿度传感器驱动代码面向嵌入式开发者和STM32入门用户帮助解决在STM32平台上稳定读取DHT11温湿度数据的问题。资源包为RAR压缩格式共2个文件包含1个头文件与1个源文件整体仅2KB两个文件职责分明头文件负责接口声明、源文件负责底层读取结构简洁适合快速集成与二次开发。代码基于STM32CubeMX生成的HAL库IO口操作完成底层读写尤为注重运行鲁棒性读取时序时未使用while死等即使传感器未连接或响应异常程序也能继续运行不会出现卡死故障驱动未绑定特定芯片资源可方便移植到其他支持HAL库的单片机平台。目前已有两千八百三十六人浏览学习适合需要快速添加温湿度采集功能或参考HAL库外设驱动写法的开发者直接下载使用。 做嵌入式这几年DHT11大概是上手门槛最低的温湿度传感器之一但它也经常让新手在HAL库下翻车。最近我在STM32F103C8T6最小系统板上重新整理了一套完整的HAL库驱动代码过程中把单总线时序、微秒延时、GPIO方向切换这些容易踩坑的点都过了一遍。这篇文章就把这套方案完整拆开讲为什么选这颗芯片、DHT11通信协议怎么啃、CubeMX怎么配、代码怎么写以及我实测中遇到的各种问题和排查思路适合刚接触HAL库、或者想搞明白“为什么照抄标准库例程跑不通”的朋友。1. 项目思路与选型分析为什么是F103C8T6加HAL库1.1 F103C8T6这颗芯片为什么这么能打STM32F103C8T6属于F1系列里的“入门但够用”选手72MHz主频、64KB Flash、20KB RAMLQFP48封装。做温湿度采集这种轻量任务性能和资源绰绰有余。更关键的是最小系统板价格非常低几块钱到十几块钱一块板载8MHz晶振、USB转串口、复位电路插上USB线就能开始调试最适合当学习样板。这颗芯片的资料实在太多了遇到问题基本都能搜到。哪怕以后项目换用更高端的F4、H7外设寄存器和HAL库接口思路也是相通的。所以说新手的第一块STM32板子选F103C8T6是性价比拉满的选择。1.2 HAL库驱动单总线器件难在哪很多人用HAL库驱动DHT11最大的困惑是明明照标准库例程写的为什么读不到数据核心原因有两个。第一个原因是HAL_Delay只能精确到毫秒级它基于SysTick实现而DHT11的数据位识别需要几十微秒级别的延时。第二个原因是HAL库的GPIO操作比标准库“重”一点HAL_GPIO_ReadPin、HAL_GPIO_WritePin里面有一堆参数判断和位运算虽然只多花几十个时钟周期但在微秒级时序里可能累积出偏差。但HAL库的好处同样明显用CubeMX图形化配置时钟树和GPIO代码结构清晰后续扩展ADC、串口、I2C等外设非常方便。所以问题的关键不是“HAL库能不能驱动DHT11”而是“要不要理解DHT11的原始时序”。理解之后HAL库反而能帮我们快速搭出更完整的项目。1.3 DHT11本身的脾气和限制DHT11是单总线数字温湿度传感器供电范围3.3-5.5V湿度测量范围20%-90%RH温度0-50℃精度分别为±5%RH和±2℃。这个精度放今天看确实一般但胜在便宜、稳定、协议简单做室内环境监测、智能家居演示绰绰有余。它的通信协议不需要时钟线只有一根DATA线主机发起一次通信传感器回传40位数据。采样周期要求至少间隔1秒连续读取间隔太短会拿不到数据。这就是DHT11的脾气不追求速度但稳定可靠适合展示型或者低成本项目。2. 硬件准备与接线注意事项2.1 需要准备的器件清单我这次用的全套东西STM32F103C8T6最小系统板一块DHT11模块一个也可以买裸传感器USB转TTL模块一个如果板子不集成串口芯片杜邦线若干可选0.96寸OLED、LED指示灯DHT11模块和裸传感器的区别在于模块上已经把上拉电阻、去耦电容都焊好了引脚也拉出来插线就能用。裸传感器只有4个引脚VCC、DATA、NC、GND需要自己接上拉电阻。建议新手直接买模块省去排查硬件毛病的麻烦。2.2 接线图与引脚说明以PA0作为数据引脚为例接线非常简单DHT11引脚STM32F103C8T6VCC3.3V或5VDATAPA0NC悬空GNDGND这里有一个需要考虑的点DHT11模块如果VCC接5VDATA线的上拉电平也是5V。STM32F103的数据手册上大多数IO是5V容忍的PA0也不例外所以直接用问题不大。但稳妥起见我建议VCC也接3.3V让整个系统电平一致减少潜在风险。2.3 上拉电阻与电平匹配避坑DHT11单总线协议要求数据线在空闲状态保持高电平所以需要外部上拉电阻。模块上一般已经焊了4.7kΩ或10kΩ电阻直接使用即可。如果你用的是裸传感器记得在DATA线和VCC之间接一个4.7kΩ-10kΩ的上拉电阻。没有上拉传感器下拉总线时没问题但释放总线时MCU读到的电平不确定数据必然是错的。这个坑我见过太多次排错的时候务必先确认。另外杜邦线尽量短。时序通信对边沿敏感线太长或者飞线乱糟糟的寄生电容和串扰会把微秒级的波形搞烂出现间歇性数据错误。3. 核心难点拆解DHT11单总线时序协议3.1 一次完整通信的时序流程DHT11的时序是整个驱动的灵魂分几个阶段空闲状态数据线为高电平。主机发起起始信号主机把数据线拉低保持至少18ms我习惯拉低20ms然后释放并拉高20-40us再切换为输入模式等待传感器响应。传感器响应DHT11检测到起始信号后先拉低80us再拉高80us表示“我准备好了”。数据发送随后DHT11连续发送40位数据每一位以50us的低电平开始之后的高电平时间长短决定该位是0还是1。结束最后一组数据位发送完后DHT11再拉低50us释放总线总线恢复高电平。判定0和1的关键点在于数据位高电平时长26-28us表示0高电平时长70us表示1。主机在读每一位时先等待50us低电平结束然后延时40us左右再去读引脚电平。如果此时是高电平就是1如果已经变回低电平就是0。这个40us的采样点需要实测微调但DHT11时序有容差40us用起来没问题。3.2 40位数据结构与校验算法DHT11一次返回40位数据排列顺序是湿度整数部分8位湿度小数部分8位温度整数部分8位温度小数部分8位校验和8位DHT11的小数位一般固定返回0所以实际有效数据就是湿度和温度的整数部分。校验算法很朴素前四个字节相加取低8位如果等于第五个字节说明数据可靠。uint8_t check buf[0] buf[1] buf[2] buf[3]; if (check buf[4]) { // 数据有效 }这个校验很简单但能过滤掉大部分偶然的误码。实际项目中不能省。3.3 为什么HAL库自带延时救不了场微秒延时怎么写前面提过HAL_Delay最小单位是1msDHT11需要的延时是几十微秒所以必须自己实现微秒级延时。最土的办法是用for循环空转比如for (uint16_t i 0; i n; i);。但这种写法极度依赖编译优化等级和主频换个优化选项或者换颗芯片延时时间就变了不建议在生产逻辑里用。我推荐用DWTData Watchpoint and Trace内核调试单元实现微秒延时。DWT有一个32位的CYCCNT周期计数器在72MHz主频下每1us计数72次精度足够而且不占用定时器资源。#include stm32f1xx.h void DWT_Delay_Init(void) { CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; } void DWT_Delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000U); while ((DWT-CYCCNT - start) ticks); }注意使用前先调用DWT_Delay_Init()。SystemCoreClock在HAL库启动时已经根据CubeMX配置自动更新延时函数拿到的是77核心频率非常可靠。4. CubeMX初始化与HAL库工程搭建4.1 CubeMX关键配置项打开STM32CubeMX新建工程并选择STM32F103C8Tx然后按下面配置RCC-HSE选择Crystal/Ceramic Resonator。最小系统板一般带8MHz晶振如果没有可以改用HSI内部时钟后面时钟树配置会不同。SYS-Debug选择Serial Wire否则板载调试器可能连不上。GPIO将PA0配置为GPIO_Output初始电平选择High因为DHT11空闲时数据线需要为高。USART1开启异步模式波特率115200用于串口打印调试数据。Clock Configuration如果HSE是8MHzPLL倍频9倍得到72MHz主频这是F103最经典的工作频率。生成工程时IDE选择MDK-ARM或者STM32CubeIDE按自己习惯来。4.2 代码结构设计引脚切换宏与延时函数DHT11驱动最核心的trick是引脚方向的动态切换主机发送起始信号时PA0是推挽输出读取传感器响应和数据时PA0必须切回输入模式。很多人栽在这一步——用了输出模式去读输入电平结果永远读到高电平。我在工程里定义了两个方向切换函数#define DHT11_PORT GPIOA #define DHT11_PIN GPIO_PIN_0 static void DHT11_Set_Output(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); } static void DHT11_Set_Input(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); }第一次配置成输出后先把引脚写高保证总线空闲。切换成输入时把内部上拉也打开即使板子上没有外部上拉电阻也能让总线保持高电平。4.3 DHT11驱动完整实现与数据解析下面是完整的DHT11驱动代码包含发送起始信号、读取响应、读取一个字节、读取全部数据并校验。代码里加了超时保护避免传感器异常时程序卡死在while循环里。uint8_t DHT11_ReadByte(void) { uint8_t data 0; uint16_t timeout; for (uint8_t i 0; i 8; i) { timeout 10000; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { if (--timeout 0) return 0xFF; } DWT_Delay_us(40); if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { data (data 1) | 0x01; timeout 10000; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { if (--timeout 0) return 0xFF; } } else { data (data 1) | 0x00; } } return data; } uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature) { uint8_t buf[5] {0}; // 发送起始信号拉低至少18ms DHT11_Set_Output(); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); DWT_Delay_us(20000); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); DWT_Delay_us(30); // 切换输入等待传感器响应 DHT11_Set_Input(); // 检查响应先低80us再高80us if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { uint16_t timeout 10000; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET) { if (--timeout 0) return 0; } timeout 10000; while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { if (--timeout 0) return 0; } for (uint8_t i 0; i 5; i) { buf[i] DHT11_ReadByte(); } // 恢复输出释放总线 DHT11_Set_Output(); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); // 校验 uint8_t check buf[0] buf[1] buf[2] buf[3]; if (check buf[4]) { *humidity buf[0]; *temperature buf[2]; return 1; } } else { DHT11_Set_Output(); HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); } return 0; }主函数里每2秒读一次通过串口打印结果简单直观。#include stdio.h #include string.h int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); DWT_Delay_Init(); DHT11_Init(); uint8_t humidity, temperature; char msg[64]; while (1) { if (DHT11_ReadData(humidity, temperature)) { sprintf(msg, Hum: %d%%, Temp: %d.%dC\r\n, humidity, temperature, 0); HAL_UART_Transmit(huart1, (uint8_t *)msg, strlen(msg), 100); } else { HAL_UART_Transmit(huart1, (uint8_t *)DHT11 read fail\r\n, 18, 100); } HAL_Delay(2000); } }这里DHT11_Init()就调用一次内部把引脚配置为输出并写高电平。注意主循环里的读取间隔要至少1秒我直接用了2秒稳妥。5. 实际运行中的常见问题与排查实录5.1 一直读回0xFF或者超时这个现象排在所有问题第一位。遇到先检查三件事一是接线。VCC和GND有没有接反DATA线是不是真的接到了PA0杜邦线有没有接触不良别觉得低级八成问题就出在这。二是上拉电阻。用的是裸传感器的话DATA线到VCC之间必须接上拉没有上拉一定是读不到数据的。三是引脚方向。程序里有没有在发送起始信号后把引脚切换为输入模式我见过不少例程把输入模式用宏改成GPIO_MODE_OUTPUT_OD这样做也不是不行但要保证引脚是开漏输出且没有内部上拉否则读数据时会出奇怪的结果。5.2 数据偶发错误、校验不过这类问题通常是中断干扰和硬件环境导致的。SysTick中断、串口中断在微秒级读时序中插入会把采样点打偏。如果系统里中断比较多可以在DHT11_ReadData()读数据前临时关中断读完立刻恢复__disable_irq(); uint8_t ret DHT11_ReadData(humidity, temperature); __enable_irq();但注意关闭中断的时间要短我这套代码一次完整DHT11读取大约几毫秒关一下可以接受。如果项目里有关键的实时任务不建议长时间关中断更合理的做法是把读取密度放低比如每2秒读一次。杜邦线过长也是数据错误的高发原因。我之前用20cm以上的杜邦线飞线读取失败率明显上升换成10cm以内的短线之后基本稳定。5.3 打印乱码与采样频率的坑串口打印乱码先确认波特率。CubeMX里配置了115200但终端软件却用了9600那肯定乱码。还有一个容易忽略的地方如果板子上没有外部晶振CubeMX时钟树里却配置了HSE系统实际跑的不是72MHz波特率也会跟着偏掉。采样频率方面DHT11要求两次读取间隔至少1秒。如果主循环用HAL_Delay(200)延时你会看到第一次能读到数据后面全失败。因为传感器在连续唤醒下会进入保护状态。调成HAL_Delay(2000)就正常了。5.4 常见问题速查表现象可能原因解决方法一直读回0xFF接线错误、上拉缺失、引脚方向未切换检查接线、加上拉、确认切换输入模式数据偶发错误中断干扰、杜邦线太长临时关中断、缩短线缆读取几次后失败采样间隔太短主循环延时至少1秒串口打印乱码波特率不匹配、时钟配置错误核对波特率、确认HSE晶振程序卡死读取超时未处理在while等待循环中增加超时计数6. 个人经验总结与项目扩展建议实际调通这套驱动之后我最大的感受是单片机外设驱动凡是涉及自定义时序的都值得先用逻辑分析仪或者示波器看一眼波形。我自己排查DHT11问题时用逻辑分析仪抓过一次起始信号和响应波形瞬间就明白为什么之前代码不行了。没有仪器的话就通过串口打印中间状态也能定位个大概。这套方案跑稳定之后可以往几个方向扩展。比如接一个0.96寸OLED显示温湿度做成桌面小仪表或者接ESP8266模块把数据通过MQTT上报到云平台就是一个小型环境监测节点。如果对温度精度有更高要求可以换DHT12或者AHT20后者是I2C接口代码模型完全不同但采集和展示逻辑可以复用。最后再分享一个细节DHT11的数据线空闲时必须保持高电平所以DHT11_Init()最后要写一次高。很多人在程序初始化后、首次读取前忘记这个操作导致第一个起始信号没发出去。函数内部虽然设置了初始电平但如果你在工程里中途修改过引脚状态记得在读取前再确保总线空闲。先跑通再优化这项目就算落地了。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻