STM32F103C8T6 DHT11 单总线通信:从 18ms 起始信号到 40bit 数据解析全流程

发布时间:2026/7/10 7:54:01
STM32F103C8T6 DHT11 单总线通信:从 18ms 起始信号到 40bit 数据解析全流程 STM32F103C8T6与DHT11温湿度传感器的单总线通信深度解析1. 单总线通信协议的本质在嵌入式系统开发中单总线通信协议因其简洁的硬件连接和高效的通信方式而广受欢迎。DHT11作为典型的单总线温湿度传感器其通信机制体现了这种协议的独特优势。单总线协议的核心在于时间片划分和电平持续时间编码。与I2C、SPI等多线协议不同单总线仅用一根数据线完成双向通信这要求协议必须精确控制以下要素严格的时序要求每个操作阶段都有明确的时间窗口电平持续时间编码通过高电平持续时间区分逻辑0和1主从协同主机发起通信从机响应并传输数据DHT11的通信过程可分为四个关键阶段主机发送起始信号至少18ms低电平从机响应信号80us低电平80us高电平数据传输阶段40位数据高位在前结束信号50us低电平后释放总线2. 硬件设计与电气特性2.1 典型电路连接DHT11与STM32的典型连接方式如下表所示DHT11引脚STM32连接说明VDD3.3V供电电压(3-5.5V)DATAGPIOx单总线数据线NC-悬空不接GNDGND电源地关键设计要点上拉电阻选择线长20米用5KΩ20米适当减小阻值电源去耦VDD与GND间建议并联100nF电容布线要求尽量缩短传感器与MCU距离减少干扰2.2 电气参数详解DHT11的电气特性参数对稳定通信至关重要参数最小值典型值最大值单位供电电压3.05.05.5V工作电流-0.51.0mA采样周期-2-秒高电平阈值0.7VDD--V低电平阈值--0.3VDDV3. 精确时序实现方案3.1 微秒级延时实现在STM32标准外设库中实现精确微秒延时的典型方法void Delay_Init(void) { // 使用SysTick定时器配置 SysTick-LOAD SystemCoreClock / 1000000 - 1; SysTick-VAL 0; SysTick-CTRL SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; } void Delay_us(uint32_t us) { uint32_t ticks us * (SystemCoreClock / 1000000); uint32_t start SysTick-VAL; while ((start - SysTick-VAL) ticks); }延时精度优化技巧使用硬件定时器如TIM2可获得更高精度关闭中断避免延时被干扰校准延时函数消除指令周期误差3.2 起始信号实现起始信号的精确控制是通信成功的第一步void DHT11_Start(void) { GPIO_InitTypeDef GPIO_InitStruct; // 配置为推挽输出 GPIO_InitStruct.Pin DHT11_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(DHT11_PORT, GPIO_InitStruct); // 拉低至少18ms HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); Delay_ms(20); // 实际18ms增加余量 // 释放总线准备接收响应 HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET); Delay_us(30); // 20-40us等待 }4. 数据解析与校验机制4.1 数据位判定算法DHT11的数据位通过高电平持续时间区分uint8_t DHT11_ReadBit(void) { uint32_t timeout 1000; // 超时保护 // 等待50us低电平结束 while(!HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)) { if(--timeout 0) return 0xFF; // 超时错误 Delay_us(1); } // 测量高电平持续时间 uint32_t start SysTick-VAL; while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)) { if(--timeout 0) return 0xFF; Delay_us(1); } uint32_t duration start - SysTick-VAL; // 判断数据位 return (duration 40) ? 1 : 0; // 阈值取40us }4.2 完整数据帧结构DHT11传输的40位数据格式如下数据段位数说明湿度整数8范围20-90%RH湿度小数8固定为0温度整数8范围0-50℃温度小数8固定为0校验和8前4字节和的最低8位数据校验实现uint8_t DHT11_CheckData(uint8_t *data) { return (data[0] data[1] data[2] data[3]) data[4]; }5. 健壮性驱动设计5.1 超时处理机制可靠的驱动必须包含完善的超时保护#define DHT11_TIMEOUT 1000 // 超时阈值(us) uint8_t DHT11_WaitForLevel(GPIO_PinState level) { uint32_t timeout DHT11_TIMEOUT; while(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) ! level) { if(--timeout 0) return 0; // 超时 Delay_us(1); } return 1; }5.2 完整驱动代码示例结合所有关键技术的完整驱动实现typedef struct { uint8_t humidity; uint8_t temperature; uint8_t checksum; } DHT11_Data; uint8_t DHT11_Read(DHT11_Data *data) { uint8_t buffer[5] {0}; // 1. 发送起始信号 DHT11_Start(); // 2. 等待响应信号 if(!DHT11_WaitForLevel(GPIO_PIN_RESET)) return 0; // 80us低 if(!DHT11_WaitForLevel(GPIO_PIN_SET)) return 0; // 80us高 // 3. 接收40位数据 for(int i0; i40; i) { if(!DHT11_WaitForLevel(GPIO_PIN_RESET)) return 0; // 50us低 uint32_t start SysTick-VAL; if(!DHT11_WaitForLevel(GPIO_PIN_SET)) return 0; uint32_t duration start - SysTick-VAL; buffer[i/8] 1; if(duration 40) buffer[i/8] | 1; // 判定为1 } // 4. 校验数据 if(!DHT11_CheckData(buffer)) return 0; // 5. 填充数据结构 >#define MAX_RETRY 3 uint8_t DHT11_Read_Stable(DHT11_Data *data) { uint8_t retry 0; while(retry MAX_RETRY) { if(DHT11_Read(data)) { // 验证数据合理性 if(data-humidity 20 >