目录
一.前言
在物联网和嵌入式系统开发中,温湿度监测是常见的基础功能需求。无论是智能家居、智能农业,还是工业环境监控,准确地获取环境温湿度数据对于实现智能化控制和优化系统运行至关重要。DHT11作为一种低成本、高性能的数字温湿度传感器,因其简单易用的特性,成为了许多开发者的首选。本文将详细介绍如何使用STM32驱动DHT11数字温湿度传感器,从硬件连接到软件编程,提供完整的代码解析和操作指南,帮助读者快速掌握这一实用技术。此外,源码中的每一条指令都进行了详细注释,以便读者更好地理解代码逻辑。
二. DHT11介绍
DHT11是一款集成温湿度测量功能的数字传感器,广泛应用于各种环境监测场景。它通过单总线(one wire)接口输出温湿度数据,具有以下特点:
- 测量范围:温度为0~50℃,精度可达±2℃;湿度为20%~90% RH,精度为±5% RH。
- 输出方式:单总线数字信号输出,仅需一根数据线即可完成通信。
- 低功耗:适合电池供电的便携式设备。
- 简单易用:无需复杂的外围电路,直接与微控制器连接即可工作。
DHT11内部集成了一个电阻式湿度传感器和一个NTC热敏电阻,用于分别测量环境的湿度和温度。数据通过内部的微控制器处理后,以数字信号的形式输出,极大地简化了硬件设计和软件开发的复杂度。
三.硬件电路接线说明
在实际应用中,将DHT11与STM32单片机连接非常简单。以下是详细的接线方法:
-
DHT11 VCC:连接到STM32的3.3V或5V电源。
-
DHT11 GND:连接到STM32的地(GND)。
-
DHT11 DATA:连接到STM32的GPIO引脚(如PA5或PC13等)。
为了确保信号的稳定性,建议在DATA引脚与VCC之间添加一个4.7kΩ的上拉电阻。这种上拉电阻的配置➡️可以有效避免信号干扰,提高通信的可靠性。
四.DHT11数据格式
DHT11传感器每次发送40位数据(5字节),这些数据包含了环境的温度和湿度信息。具体的数据格式如下:
4.1数据结构
-
湿度整数部分(8位):表示当前环境湿度的整数部分,取值范围为0~100。
-
湿度小数部分(8位):表示当前环境湿度的小数部分。对于DHT11传感器,这一部分始终为0,因此在实际应用中
可以忽略。 -
温度整数部分(8位):表示当前环境温度的整数部分,取值范围为0~50。
-
温度小数部分(8位):表示当前环境温度的小数部分。DHT11的温度小数部分也固定为0,因此在实际应用中同样
可以忽略。 -
校验和(8位):用于验证数据的正确性。校验和的计算方式为:校验和 = 湿度整数 + 湿度小数 + 温度整数 + 温度小数。通过校验和可以确保数据在传输过程中没有发生错误。
4.2数据格式示例
假设DHT11返回的数据为:
-
湿度整数:50(0x32)
-
湿度小数:0(0x00)
-
温度整数:25(0x19)
-
温度小数:0(0x00)
-
校验和:50 + 0 + 25 + 0 = 75(0x4B)
因此,DHT11返回的5字节数据为:0x32 0x00 0x19 0x00 0x4B
。
4.3数据解析
在接收到DHT11传输的数据后,微控制器需要按照以下步骤解析数据:
-
读取数据:从DHT11读取40位数据,分为5个字节。
-
提取信息:
-
湿度整数部分:第一个字节(buffer[0])
-
湿度小数部分:第二个字节(buffer[1]),通常为0
-
温度整数部分:第三个字节(buffer[2])
-
温度小数部分:第四个字节(buffer[3]),通常为0
-
校验和:第五个字节(buffer[4])
-
-
校验数据:通过计算校验和验证数据的完整性。如果计算的校验和与接收到的校验和相等👌则数据有效;否则,需重新读取数据。
五.DHT11通信时序
DHT11采用单总线通信协议,通过单一的数据线完成所有数据传输。其通信时序严格规定了主机(如STM32)与DHT11之间的交互过程,包括启动信号、响应信号和数据传输。以下是详细的通信时序说明:
5.1 启动信号
通信的开始由主机(如STM32)发起,具体步骤如下:
-
拉低信号:主机将数据线拉低至少18ms。
-
拉高信号:主机将数据线拉高20~40us。
-
切换为输入模式:主机将数据线切换为输入模式,等待DHT11的响应。
void DHT11_Start(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 配置为推挽输出模式
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
// 拉低至少18ms
GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
DHT11_Delay(18); // 延时18ms
// 拉高20~40us
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
DHT11_DelayUs(30); // 延时30us
}
5.2 DHT11响应信号
当DHT11检测到主机发送的启动信号后,会通过以下方式响应:
-
拉低80us:DHT11将数据线拉低80us。
-
拉高80us:随后将数据线拉高80us。
-
准备传输数据:响应信号结束后,DHT11准备开始传输40位数据。
注:主机将数据线切换为输入模式,接受从机的响应
uint8_t DHT11_CheckResponse(void)
{
uint8_t response = 0;
// 切换为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
// 等待DHT11拉低信号
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
// 等待DHT11拉高信号
while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由低变高
// 检测拉高时间是否为80us
DHT11_DelayUs(80); // 延时80us
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) {
response = 1; // 响应成功
}
return response;
}
5.3 数据传输
DHT11通过数据线传输40位数据,数据格式为:
-
8位湿度整数
-
8位湿度小数(固定为0)
-
8位温度整数
-
8位温度小数(固定为0)
-
8位校验和
数据位的表示:
每个数据位由DHT11通过高电平的持续时间来区分:
-
数据“0”:低电平持续50us,高电平持续26~28us。
-
数据“1”:低电平持续50us,高电平持续70us。
当 最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。
//读取一位数据位
uint8_t DHT11_ReadBit(void)
{
uint8_t bit = 0;
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
DHT11_DelayUs(40); // 延时40us,确保信号稳定,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) {
bit = 1; // 如果信号线拉高超过40us,则为1
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
}
return bit;
}
//读取一个字节数据
uint8_t DHT11_ReadByte(void)
{
uint8_t byte = 0;
for(uint8_t i = 0; i < 8; i++) {
byte <<= 1; // 左移一位
byte |= DHT11_ReadBit(); // 读取一位数据
}
return byte;
}
5.4 数据校验获取温湿度
接收完40位数据后,主机需要验证数据的正确性:
-
校验和计算:将前四个字节(湿度整数、湿度小数、温度整数、温度小数)相加,结果的低8位应与第五个字节(校验和)相等。
-
校验通过:如果校验和匹配,则数据有效;否则,需要重新发起通信。
//获取外界温湿度
uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature)
{
uint8_t data[5]; // 40位数据,5个字节
uint8_t checksum;
// 读取40位数据
for (uint8_t i = 0; i < 5; i++)
{
data[i] = DHT11_ReadByte();
}
// 校验数据
checksum = data[0] + data[1] + data[2] + data[3];
if (checksum != data[4])
{
return 1; // 校验失败
}
// 提取温湿度数据
*humidity = data[0]; // 湿度整数
*temperature = data[2]; // 温度整数
return 0; // 成功
}
5.5 注意事项
-
时序严格性:DHT11的通信时序非常严格,主机在操作时序时需关闭中断,以避免中断干扰。
-
数据线模式切换:主机在发送启动信号时需将数据线设置为输出模式,接收数据时需切换为输入模式。(发送输出-接收输入)
-
数据稳定性:为了确保信号的稳定性,建议在数据线与电源之间添加一个4.7kΩ的上拉电阻。
六.STM32与DHT11通信程序设计
使用STM32F103C8T6读取DHT11温湿度传感器采集的数据,将读取得到的温湿度数据通过串口发送至电脑
dht11.h文件
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h" // Device header
#include "delay.h"
//向DHT11发送起始信号
void DHT11_Start(void);
//接收从机的响应信号
uint8_t DHT11_CheckResponse(void);
//读取一位数据位
uint8_t DHT11_ReadBit(void);
//读取一个字节数据
uint8_t DHT11_ReadByte(void);
//获取外界温湿度
uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature);
#endif
dht11.c文件
#include "dht11.h"
void DHT11_Start(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 配置为推挽输出模式
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
// 拉低至少18ms
GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
DHT11_Delay(18); // 延时18ms
// 拉高20~40us
GPIO_SetBits(DHT11_PORT, DHT11_PIN);
DHT11_DelayUs(30); // 延时30us
}
uint8_t DHT11_CheckResponse(void)
{
uint8_t response = 0;
// 切换为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
// 等待DHT11拉低信号
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
// 等待DHT11拉高信号
while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由低变高
// 检测拉高时间是否为80us
DHT11_DelayUs(80); // 延时80us
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) {
response = 1; // 响应成功
}
return response;
}
//读取一位数据位
uint8_t DHT11_ReadBit(void)
{
uint8_t bit = 0;
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
DHT11_DelayUs(40); // 延时40us,确保信号稳定,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长
if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) {
bit = 1; // 如果信号线拉高超过40us,则为1
while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); // 等待信号线由高变低
}
return bit;
}
//读取一个字节数据
uint8_t DHT11_ReadByte(void)
{
uint8_t byte = 0;
for(uint8_t i = 0; i < 8; i++) {
byte <<= 1; // 左移一位
byte |= DHT11_ReadBit(); // 读取一位数据
}
return byte;
}
//获取外界温湿度
uint8_t DHT11_ReadData(uint8_t *humidity, uint8_t *temperature)
{
uint8_t data[5]; // 40位数据,5个字节
uint8_t checksum;
// 读取40位数据
for (uint8_t i = 0; i < 5; i++)
{
data[i] = DHT11_ReadByte();
}
// 校验数据
checksum = data[0] + data[1] + data[2] + data[3];
if (checksum != data[4])
{
return 1; // 校验失败
}
// 提取温湿度数据
*humidity = data[0]; // 湿度整数
*temperature = data[2]; // 温度整数
return 0; // 成功
}
main.c文件
// 单片机头文件
#include "stm32f10x.h" // 包含STM32标准外设库的头文件
// 硬件驱动
#include "usart.h" // 包含串口通信的驱动代码
#include "dht11.h" // 包含DHT11温湿度传感器的驱动代码
uint8_t temp, humi; // 定义变量用于存储温度和湿度值
int main(void)
{
// 初始化系统时钟
SystemInit(); // 初始化系统时钟,设置系统运行频率
// 初始化串口1,用于打印调试信息
Usart1_Init(115200); // 初始化串口1,波特率设置为115200
// 等待DHT11温湿度稳定
delay_ms(1000); // 延时1秒,确保DHT11传感器稳定并准备好接收指令
// 发送启动信号
DHT11_Start(); // 向DHT11发送启动信号,唤醒传感器
// 检测DHT11的响应
if (!DHT11_CheckResponse()) // 检查DHT11是否正确响应
{
UsartPrintf(Usart1, "DHT11 Error \r\n"); // 如果未响应,通过串口打印错误信息
delay_ms(1000); // 延时1秒
return 1; // 返回错误代码1,程序退出
}
UsartPrintf(Usart1, "DHT11 Start \r\n"); // 如果响应成功,通过串口打印启动信息
// 主循环
while (1)
{
// 读取温湿度数据
DHT11_ReadData(&temp, &humi); // 调用DHT11驱动函数,读取温度和湿度值
// 打印温湿度
UsartPrintf(Usart1, "temp %d ,humi %d\r\n", temp, humi); // 通过串口打印温度和湿度值
// 延时1秒
delay_ms(1000); // 每次读取后延时1秒,避免频繁读取
}
}
先赞后看的✨感谢支持