公司动态
STM32驱动DHT11温湿度传感器实战指南
1. DHT11温湿度传感器概述DHT11是一款广泛使用的数字温湿度复合传感器采用单总线通信协议具有成本低、体积小、功耗低等特点。它内部集成了湿敏电阻和NTC热敏电阻通过专用ASIC芯片进行信号采集和数字转换。传感器测量范围为湿度20-90%RH±5%精度和温度0-50℃±2℃精度采样周期不小于1秒。典型应用场景包括室内环境监测农业大棚监控仓储环境记录智能家居控制实际使用中发现DHT11在高温高湿环境下如浴室测量值会有明显偏差建议在要求不高的场合使用。对精度要求高的项目可考虑SHT3x或AHT20等传感器。2. 硬件连接与工作原理2.1 引脚定义与接线方式DHT11采用4针单排直插封装注意也有3针模块化封装版本VDD3.3-5.5V电源输入DATA单总线数据线NC空引脚GND电源地典型STM32连接方案VDD → 3.3VDATA → PA0需4.7K上拉电阻GND → GND调试时常见问题忘记加上拉电阻会导致通信失败表现为一直读取不到数据。我曾在一个项目中因此浪费了两小时排查时间。2.2 单总线通信协议详解DHT11采用严格的时序通信完整数据包为40bit5字节湿度整数8bit湿度小数8bitDHT11固定为0温度整数8bit温度小数8bitDHT11固定为0校验和8bit通信流程分三个阶段主机启动信号拉低DATA线18ms后拉高传感器响应拉低80μs→拉高80μs数据传输每位以50μs低电平开始高电平26-28μs表示070μs表示13. STM32硬件环境搭建3.1 CubeMX基础配置创建STM32F103C8T6工程以Keil MDK为工具链配置时钟树HSE 8MHzSYSCLK 72MHzAPB1 36MHzAPB2 72MHz启用USART1用于调试输出115200bps配置TIM2为32位定时器预分频值7172MHz→1MHz自动重装载值0xFFFFFFFF3.2 GPIO配置要点DATA线以PA0为例需要动态切换输入输出模式输出模式推挽输出高速输入模式上拉输入在CubeMX中初始化为GPIO_Output实际使用时通过代码切换void DHT11_IO_OUT(void){ GPIO_InitTypeDef gpio; gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_OUTPUT_PP; gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, gpio); } void DHT11_IO_IN(void){ GPIO_InitTypeDef gpio; gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_INPUT; gpio.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, gpio); }4. 核心驱动实现4.1 微秒级延时函数基于TIM2实现精确延时void delay_us(uint32_t us){ __HAL_TIM_SET_COUNTER(htim2, 0); HAL_TIM_Base_Start(htim2); while(__HAL_TIM_GET_COUNTER(htim2) us); HAL_TIM_Base_Stop(htim2); }4.2 数据读取函数完整实现代码uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi){ uint8_t buf[5] {0}; uint8_t i, j; // 启动信号 DHT11_IO_OUT(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); delay_us(18000); // 18ms HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); delay_us(30); DHT11_IO_IN(); // 等待响应 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 0){ uint32_t timeout 10000; while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 0 timeout--); timeout 10000; while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 1 timeout--); // 读取40位数据 for(i0; i5; i){ for(j0; j8; j){ while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 0); delay_us(40); if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 1){ buf[i] | (1(7-j)); while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) 1); } } } // 校验数据 if(buf[4] (buf[0]buf[1]buf[2]buf[3])){ *humi buf[0]; *temp buf[2]; return 0; // 成功 } } return 1; // 失败 }5. 实际应用与优化5.1 数据滤波处理由于DHT11测量存在波动建议采用滑动平均滤波#define FILTER_LEN 5 uint8_t temp_buf[FILTER_LEN], humi_buf[FILTER_LEN]; uint8_t filter_index 0; void DHT11_Filter(uint8_t temp, uint8_t humi){ temp_buf[filter_index] temp; humi_buf[filter_index] humi; filter_index (filter_index 1) % FILTER_LEN; } uint8_t Get_Average(uint8_t *buf){ uint16_t sum 0; for(uint8_t i0; iFILTER_LEN; i){ sum buf[i]; } return sum / FILTER_LEN; }5.2 低功耗优化对于电池供电设备采样间隔延长至10-30秒两次采样间将DATA引脚设为模拟输入使用STOP模式定时器唤醒void Enter_LowPower(void){ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); GPIO_InitTypeDef gpio; gpio.Pin GPIO_PIN_0; gpio.Mode GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, gpio); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); }6. 常见问题排查6.1 通信失败分析现象可能原因解决方案一直返回错误接线错误检查VCC/GND/DATA连接偶尔读取失败时序不精确优化延时函数精度数据明显错误电源干扰VCC与GND间加100nF电容无响应上拉电阻缺失DATA线增加4.7K上拉6.2 精度提升技巧避免阳光直射传感器保持传感器周围空气流通定期校准对比标准温湿度计在代码中补偿固定偏差// 温度补偿示例 temp_actual temp_read - 0.5;7. 进阶应用示例7.1 结合FreeRTOS实现创建独立传感器任务void DHT11_Task(void const *arg){ uint8_t temp, humi; while(1){ if(DHT11_Read_Data(temp, humi) 0){ xQueueSend(temp_queue, temp, 0); xQueueSend(humi_queue, humi, 0); } vTaskDelay(pdMS_TO_TICKS(2000)); } }7.2 通过WiFi上传数据配合ESP8266模块void Upload_Data(uint8_t temp, uint8_t humi){ char cmd[128]; sprintf(cmd, ATCIPSTART\TCP\,\api.thingspeak.com\,80\r\n); ESP8266_Send(cmd); sprintf(cmd, GET /update?api_keyYOUR_KEYfield1%dfield2%d\r\n, temp, humi); ESP8266_Send(cmd); }实际项目中建议将传感器放置在远离MCU的位置时使用屏蔽线连接DATA线并在两端添加TVS二极管防止静电损坏。我在一个工业项目中曾因忽视这点导致传感器寿命大幅缩短。