STM32驱动DHT11温湿度传感器详解
摘要:本文介绍了DHT11数字温湿度传感器在STM32平台上的应用实现。内容涵盖传感器引脚电路、单总线通信协议、数据格式解析以及完整的代码封装方案。重点讲解了主机通信时序控制、数据读取校验方法,并提供了GPIO初始化、数据采集和校验的完整代码实现。通过逻辑分析仪验证了通信波形,最终实现了温度湿度数据的准确采集和OLED屏显功能。该项目代码模块化程度高,包含完整的.h和.c文件,可直接应用于暖通空调
目录
前言
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数 字模块采集技术和温湿度传感技术,确保产品具有枀高的可靠性与卓越的长期稳定性。传感器包括一 个电容式感湿元件和一个NTC 测温元件,并与一个高性能8位单片机相连接。广泛应用于暖通空调、除湿器、农业、冷链仓储、测试及检测设备、消费品、汽车、自动控制、数据记录器、气 象站、家电、湿度调节器、医疗、其他相关湿度检测控制。

一、使用模块储备知识
1.引脚电路

根据数据手册我们可以看到引脚2为信号线,需要与VCC相连将数据线拉高,这里我们使用下图模块已经自动帮我们进行拉高了。所以最后只引出了3个引脚。

| Pin | 功能 | 注解 |
|---|---|---|
| VCC | 3.3V-5.5V供电 | 无 |
| GND | 地线 | 与单片机接地 |
| DAT | 数据线 | 模块已经自动拉高无需处理 |
2.单总线传送数据位定义
DATA用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式,一次传送40位数据,高位先 出。
8bit 湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据 + 8bit校验位。

二、通讯协议及数据格式


通过上图我们可以发现,通讯开始是由主机(stm32)发起的通讯,在发起通讯后改引脚的控制权将交给从机,因此我们在进行初始化的时候需要对GPIO进行封装。
三、代码封装
现在DHT11.h文件中定义GPIO端口,时钟,及PIN,方便后期的移植。
#define DHT11_GPIO_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define DHT11_GPIO GPIOB
#define DHT11_PIN GPIO_PIN_11
然后我们使用宏定义进行GPIO的初始化。
void DHT11_OUTPUT(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_GPIO_CLK(); /* IO口时钟使能 */
gpio_initstruct.Pin = DHT11_PIN; /* DHT11引脚 */
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_initstruct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(DHT11_GPIO, &gpio_initstruct); /* 初始化DHT11引脚 */
}
void DHT11_INPUT(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_GPIO_CLK(); /* IO口时钟使能 */
gpio_initstruct.Pin = DHT11_PIN; /* DHT11引脚 */
gpio_initstruct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_initstruct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(DHT11_GPIO, &gpio_initstruct); /* 初始化DHT11引脚 */
}

翻看数据手册,步骤一可以忽略,我们直接从步骤二开始,这是由主机(stm32)发起的,将数据线拉低至少18ms。
void DHT11_Start(void)
{
DHT11_OUTPUT(); /* 设置为输出模式 */
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_RESET); /* 拉低DHT11引脚 */
delay_ms(20); /* 延时20ms */
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_SET); /* 拉高DHT11引脚 */
delay_us(30); /* 延时30us */
DHT11_INPUT(); /* 设置为输入模式 */
delay_us(160); /* 等待DHT11响应 */
}
需要特别注意在发送完开始信息以后,我们需要等待DHT11返回应答数据(如下图),否则将会导致读取信息混乱及验证错误,通过下图我们可以感到从机会由170us的一个响应信号才开始发送数据,这也是为什么我们DHT11_Start()函数最后需要延时160us的原因。

在写完DHT11_Start()以后我们就可以通过调用函数,使用逻辑分析仪查看返回波形数据了。

可以看到我们Start拉低了20ms,与我们的代码一致,后面就是DHT11返回的温湿度及校验位的数据了。


接着就是我们的数据读取函数。在开始之前我们先定义一个数组存储数据:
uint8_t DHT11_buff[5]; /* DHT11数据缓冲区 */
uint8_t DHT11_Read(void)
{
uint8_t i, j;
for (j = 0; j < 5; j++)
{
DHT11_buff[j] = 0;
for (i = 0; i < 8; i++)
{
uint32_t timeout = 1000;
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_RESET && timeout--);
if (timeout == 0) return 0;
delay_us(40);
if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET)
{
DHT11_buff[j] |= (1 << (7 - i));
}
timeout = 1000;
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET && timeout--);
if (timeout == 0) return 0;
}
}
return 1;
}
验证数据校验位:

uint8_t DHT11_check(void)
{
uint8_t sum = 0;
for (uint8_t i = 0; i < 4; i++) /* 计算前4个字节的和 */
{
sum += DHT11_buff[i];
}
return (sum == DHT11_buff[4]); /* 返回校验结果 */
}
最后就是我们完整的获取一次数据的函数了。
void DHT11_GetData(uint8_t *temperature, uint8_t *humidity)
{
DHT11_Start();
if (DHT11_Read() && DHT11_check())
{
*humidity = DHT11_buff[0];
*temperature = DHT11_buff[2];
}
else
{
*humidity = 0;
*temperature = 0;
}
}
完整的DHT11.c文件
#include "DHT11.h"
uint8_t DHT11_buff[5]; /* DHT11数据缓冲区 */
void DHT11_OUTPUT(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_GPIO_CLK(); /* IO口时钟使能 */
gpio_initstruct.Pin = DHT11_PIN; /* DHT11引脚 */
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_initstruct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(DHT11_GPIO, &gpio_initstruct); /* 初始化DHT11引脚 */
}
void DHT11_INPUT(void)
{
GPIO_InitTypeDef gpio_initstruct;
DHT11_GPIO_CLK(); /* IO口时钟使能 */
gpio_initstruct.Pin = DHT11_PIN; /* DHT11引脚 */
gpio_initstruct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_initstruct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(DHT11_GPIO, &gpio_initstruct); /* 初始化DHT11引脚 */
}
void DHT11_Start(void)
{
DHT11_OUTPUT(); /* 设置为输出模式 */
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_RESET); /* 拉低DHT11引脚 */
delay_ms(20); /* 延时20ms */
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_SET); /* 拉高DHT11引脚 */
delay_us(30); /* 延时30us */
DHT11_INPUT(); /* 设置为输入模式 */
delay_us(160); /* 等待DHT11响应 */
}
uint8_t DHT11_Read(void)
{
uint8_t i, j;
for (j = 0; j < 5; j++)
{
DHT11_buff[j] = 0;
for (i = 0; i < 8; i++)
{
uint32_t timeout = 1000;
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_RESET && timeout--);
if (timeout == 0) return 0;
delay_us(40); // 建议改为40us
if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET)
{
DHT11_buff[j] |= (1 << (7 - i));
}
timeout = 1000;
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET && timeout--);
if (timeout == 0) return 0;
}
}
return 1;
}
uint8_t DHT11_check(void)
{
uint8_t sum = 0;
for (uint8_t i = 0; i < 4; i++) /* 计算前4个字节的和 */
{
sum += DHT11_buff[i];
}
return (sum == DHT11_buff[4]); /* 返回校验结果 */
}
void DHT11_GetData(uint8_t *temperature, uint8_t *humidity)
{
DHT11_Start();
if (DHT11_Read() && DHT11_check())
{
*humidity = DHT11_buff[0];
*temperature = DHT11_buff[2];
}
else
{
*humidity = 0;
*temperature = 0;
}
}
完整的DHT11.h文件
#ifndef _H_DHT11_
#define _H_DHT11_
#include "sys.h"
#include "delay.h"
#include "stdio.h"
#define DHT11_GPIO_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define DHT11_GPIO GPIOB
#define DHT11_PIN GPIO_PIN_11
void DHT11_GetData(uint8_t *temperature, uint8_t *humidity); /* 获取温度和湿度数据 */
uint8_t DHT11_check(void);
#endif
main.c文件
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "oled.h"
#include "font.h"
#include "SR04.h"
#include "DHT11.h"
uint8_t temperature = 0; /* 温度 */
uint8_t humidity = 0; /* 湿度 */
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
uart1_init(115200);
printf("running...\r\n");
SR04_GPIO_Config();
OLED_Init();
float distance = 0;
uint8_t SR04_msg[20] = "";
uint8_t temperature_msg[20] = "";
uint8_t humidity_msg[20] = "";
delay_ms(1000); /* 延时1秒, 等待OLED初始化完成 */
while (1)
{
DHT11_GetData(&temperature, &humidity); /* 获取温度和湿度数据 */
printf("Temperature: %d ℃, Humidity: %d %%\r\n", temperature, humidity);
distance = SR04_distance(); /* 获取SR04测距值 */
printf("SR04 distance: %.2f cm\r\n", distance);
sprintf((char*)SR04_msg,"distance: %.2f cm", distance);
sprintf((char*)temperature_msg,"temperature: %d C", temperature);
sprintf((char*)humidity_msg,"humidity: %d C", humidity);
OLED_NewFrame();
OLED_PrintASCIIString(0, 10, (char*)SR04_msg, &afont8x6, OLED_COLOR_NORMAL);
OLED_PrintASCIIString(0, 20, (char*)temperature_msg, &afont8x6,OLED_COLOR_NORMAL);
OLED_PrintASCIIString(0, 30, (char*)humidity_msg, &afont8x6, OLED_COLOR_NORMAL);
OLED_ShowFrame();
delay_ms(1000); /* 延时1秒 */
}
运行效果
串口结果:

OLED显示结果:

更多推荐




所有评论(0)