STM32CubeMX驱动DHT11温湿度传感器:从引脚配置到串口打印的完整流程

在嵌入式开发中,温湿度传感器是常见的环境监测组件,而DHT11因其简单易用、成本低廉的特点,成为入门级项目的首选。本文将详细介绍如何利用STM32CubeMX这一强大的图形化配置工具,结合HAL库快速实现DHT11传感器的驱动开发,并通过串口输出温湿度数据。

1. 环境准备与CubeMX基础配置

开始之前,确保已安装STM32CubeMX和对应的IDE(如Keil MDK或IAR)。我们以STM32F103C8T6为例,演示完整的配置流程。

打开CubeMX后,首先进行时钟配置:

  1. 选择正确的芯片型号
  2. 在Clock Configuration选项卡中启用外部高速时钟(HSE)
  3. 配置系统时钟为72MHz(根据具体芯片调整)

接下来配置调试接口:

/* 在SYS选项卡中 */
Debug: Serial Wire

对于串口输出,我们使用USART1:

Mode: Asynchronous
Baud Rate: 115200
Word Length: 8 Bits
Parity: None
Stop Bits: 1

2. GPIO动态配置与DHT11通信实现

DHT11使用单总线协议,同一引脚需要在不同时刻切换输入/输出模式。CubeMX默认不支持动态模式切换,需要手动处理。

2.1 引脚配置技巧

在CubeMX中,我们先将连接DHT11的GPIO(如PB7)配置为输出模式:

GPIO output level: High
GPIO mode: Output Push Pull
Pull-up/Pull-down: No pull-up and no pull-down
Maximum output speed: Low

然后在代码中实现模式切换函数:

void DHT11_GPIO_Mode(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint32_t mode) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_Pin;
    GPIO_InitStruct.Mode = mode;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOx, &GPIO_InitStruct);
}

2.2 精确延时实现

DHT11协议要求微秒级延时精度。HAL库的HAL_Delay()只能实现毫秒级延时,我们需要自定义微秒延时:

void delay_us(uint16_t us) {
    __HAL_TIM_SET_COUNTER(&htimX, 0);
    while(__HAL_TIM_GET_COUNTER(&htimX) < us);
}

注意:需要先配置一个基本定时器(如TIM2),预分频值设为72-1,这样计数器每1MHz计数,即1us计数一次。

3. DHT11协议实现与数据解析

DHT11的通信流程分为三个主要阶段:启动信号、响应检测和数据传输。

3.1 启动信号时序

void DHT11_Start(void) {
    // 设置引脚为输出模式
    DHT11_GPIO_Mode(DHT11_GPIO_Port, DHT11_Pin, GPIO_MODE_OUTPUT_PP);
    
    // 主机拉低至少18ms
    HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET);
    HAL_Delay(20);
    
    // 主机拉高20-40us
    HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_SET);
    delay_us(30);
    
    // 切换为输入模式等待响应
    DHT11_GPIO_Mode(DHT11_GPIO_Port, DHT11_Pin, GPIO_MODE_INPUT);
}

3.2 数据位解析

DHT11每个数据位以50us低电平开始,高电平持续时间决定数据值:

  • 26-28us表示'0'
  • 70us表示'1'
uint8_t DHT11_Read_Bit(void) {
    while(HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_RESET);
    delay_us(40);
    return HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin);
}

uint8_t DHT11_Read_Byte(void) {
    uint8_t data = 0;
    for(int i=0; i<8; i++) {
        data <<= 1;
        if(DHT11_Read_Bit()) {
            data |= 1;
        }
    }
    return data;
}

4. 数据整合与串口输出

完整的温湿度读取函数如下:

uint8_t DHT11_Read_Data(float *temperature, float *humidity) {
    uint8_t data[5] = {0};
    
    DHT11_Start();
    
    // 等待DHT11响应
    if(HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET) return 1;
    while(HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_RESET);
    while(HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET);
    
    // 读取40位数据
    for(int i=0; i<5; i++) {
        data[i] = DHT11_Read_Byte();
    }
    
    // 校验数据
    if(data[4] != (data[0] + data[1] + data[2] + data[3])) return 2;
    
    *humidity = data[0] + data[1] * 0.1;
    *temperature = data[2] + data[3] * 0.1;
    
    return 0;
}

串口输出格式化函数:

void Print_Sensor_Data(float temp, float hum) {
    char buffer[64];
    int len = sprintf(buffer, "Temperature: %.1fC\r\nHumidity: %.1f%%\r\n", temp, hum);
    HAL_UART_Transmit(&huart1, (uint8_t*)buffer, len, HAL_MAX_DELAY);
}

5. 主程序逻辑与错误处理

在主循环中实现定期读取和错误处理:

while (1) {
    float temp, hum;
    uint8_t ret = DHT11_Read_Data(&temp, &hum);
    
    switch(ret) {
        case 0:
            Print_Sensor_Data(temp, hum);
            break;
        case 1:
            printf("DHT11 not responding\r\n");
            break;
        case 2:
            printf("Data checksum error\r\n");
            break;
    }
    
    HAL_Delay(2000); // 2秒读取一次
}

6. 常见问题排查

在实际项目中可能会遇到以下问题:

  1. 无响应或数据全零

    • 检查电源电压(3.3V-5V)
    • 确认上拉电阻(4.7kΩ-10kΩ)
    • 检查接线是否正确
  2. 数据不稳定或校验错误

    • 确保延时函数精度
    • 缩短传感器与MCU的距离
    • 增加读取失败后的重试机制
  3. 串口无输出

    • 检查波特率设置
    • 确认TX/RX接线
    • 验证串口初始化代码

对于需要更高精度的场景,可以考虑升级到DHT22或SHT系列传感器。DHT11的典型精度为±2℃(温度)和±5%RH(湿度),适合对精度要求不高的场合。

更多推荐