一、前言
在物联网和嵌入式系统开发中,温湿度监测是常见的基础功能需求。本文将详细介绍如何使用STM32F103c8t6单片机驱动DHT11数字温湿度传感器,并提供完整的代码解析,源码每条都进行了注释。资源链接包含了整个代码工程和DHT11的数据手册,需要自行下载
二、DHT11传感器简介
2.1 基本特性
-
单总线数字信号输出
-
湿度测量范围:20-90%RH(±5%RH)
-
温度测量范围:0-50℃(±2℃)
-
供电电压:3.3-5.5V
-
典型功耗:0.5mA(测量时)
2.2 数据格式
DHT11每次发送40位数据(5字节),格式为:
湿度整数(8bit) 湿度小数(8bit) 温度整数(8bit) 温度小数(8bit) 校验和(8bit)
注:DHT11小数部分固定为0
2.3 驱动原理
2.3.1. 初始化准备
-
硬件连接:
DHT11的DATA引脚通过4.7K上拉电阻接MCU的GPIO(如PA0),确保总线空闲时为高电平。 -
上电等待:
DHT11上电后需等待至少2秒完成初始化,期间MCU需保持总线空闲(高电平)。
2.3.2. 发送起始信号
-
拉低总线:
将GPIO配置为推挽输出模式,输出低电平持续 18~30ms(代码中为20ms),通知DHT11开始通信。 -
释放总线:
切换GPIO为浮空输入模式(开漏模式更安全),释放总线。此时总线由外部上拉电阻拉高。 -
等待应答:
DHT11会在总线释放后 80μs内拉低总线作为应答信号,随后再拉高 80μs 表示准备发送数据。
代码关键点:if (!GPIO_ReadInputDataBit(...)) { while (!GPIO_ReadInputDataBit(...)); // 等待低电平应答结束 while (GPIO_ReadInputDataBit(...)); // 等待高电平结束 return 1; // 应答成功 }
2.3.3. 数据位读取
-
数据格式:
DHT11发送40位数据(5字节),每位数据以 50~58μs低电平起始,后接不同时长的高电平:-
逻辑0:高电平持续 23~27μs
-
逻辑1:高电平持续 68~74μs
-
-
读取策略:
-
检测到低电平后,等待 28μs(代码中
delay_us(28)
),此时若总线仍为高电平则为1,否则为0。 -
循环读取8次组成一个字节。
代码关键点:
delay_us(28); // 关键采样点 if (GPIO_ReadInputDataBit(...)) temp |= 0x01; // 判断逻辑1
复制
delay_us(28); // 关键采样点 if (GPIO_ReadInputDataBit(...)) temp |= 0x01; // 判断逻辑1
-
2.3.4. 数据校验与解析
-
数据组成:
40位数据分为:-
湿度整数(8bit)、湿度小数(8bit,DHT11固定为0)
-
温度整数(8bit)、温度小数(8bit,DHT11固定为0)
-
校验和(8bit,前四字节和的最低8位)
-
-
校验逻辑:
校验和需满足:复制
(buffer[0] + buffer[1] + buffer[2] + buffer[3]) == buffer[4]
三、硬件连接
3.1 接线方式
头文件直接宏定义了引脚,方便用户修改引脚
DHT11引脚 | STM32引脚 |
---|---|
VCC | 3.3V/5V |
DATA | PA6 |
GND | GND |
这个模块已经画了上拉电阻
库的DHT11.c源码
#include "DHT11.h"
#include "delay.h"
/**
* @函数 DHT11_GPIO初始化函数
* @参数 Mode:指定输入或输出模式
* @返回值 None
*/
void DHT_GPIO_Init(GPIOMode_TypeDef Mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DHT_RCC_PORT,ENABLE); //打开对应引脚时钟
GPIO_SetBits(DHT_GPIO_PORT,DHT_GPIO_PIN);
GPIO_InitStructure.GPIO_Mode = Mode; //通过参数形式来控制GPIO的模式
GPIO_InitStructure.GPIO_Pin = DHT_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT_GPIO_PORT,&GPIO_InitStructure);
}
/**
* @函数 DHT11模块起始信号函数
* @参数 None
* @返回值 1或0,标志起动信号成功与否
*/
uint8_t DHT_Start(void)
{
DHT_GPIO_Init(GPIO_Mode_Out_PP); //推挽输出模式
GPIO_ResetBits(DHT_GPIO_PORT,DHT_GPIO_PIN); //主机控制单总线输出20ms低电平
delay_ms(20);
GPIO_SetBits(DHT_GPIO_PORT,DHT_GPIO_PIN); //主机输出高电平后主机释放总线
DHT_GPIO_Init(GPIO_Mode_IN_FLOATING); //输出高电平后主机释放总线,配置位浮空输入模式
delay_us(20); //从机控制总线
if(!GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN)) //判断从机是否进入应答模式
{
while(!GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN)); //while循环等待从机低电平应答 大概低电平时间持续80us(具体看数据手册)
while(GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN)); //while循环等待从机高电平应答 大概高电平时间87us(具体看手册)
return 1; //返回1表示应答成功
}
return 0; //返回0表示应答失败
}
/**
* @函数 接收DHT11发送来8位的数据
* @参数 None
* @返回值 返回接收到的8位数据
数据手册:数据位的传输:数据位为“0”时,DHT11先输出50~58μs的低电平随后输出23~27μs的高电平;数据位
为“1”时,DHT11首先输出50~58μs的低电平随后输出68~74μs的高电平。
判断数据位为1或0的方法:先whlie等待高电平时间结束,然后延时28us,即28us后再读取读取总线引脚,若还为高电平,则数据为位1,否则为0
*/
uint8_t DHT_Get_Byte_Data(void)
{
int i = 0;
uint8_t temp; //创建个变量用于结束从机发送的数据位
for(i = 0; i < 8; i++) //循环8次,每次接收一个数据为,8次为1个字节
{
temp <<= 1; //变量先左移1位腾出最低位用于判断单总线的数据位的电平信号
while(!GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN)); //这里while等待数据位的等待高电平时间结束
delay_us(28); //延时28us 数据位为“0”时,DHT11先输出50~58μs的低电平随后输出23~27μs的高电平。等待数据为0的高电平时间结束
GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN) ? (temp |= 0x01) : (temp &= ~0x01); //读取总线引脚的电平信号,若为高电平,则变量最低位为1,否则为0
while(GPIO_ReadInputDataBit(DHT_GPIO_PORT,DHT_GPIO_PIN)); //while循环 表示一次数据位的读取结束
}
return temp; //返回1个字节,即8个数据位
}
/**
* @函数 获取DHT11的温度湿度数据
* @参数 buffer[]:需要传入一个存储数据的数组
* @返回值 返回数据校验是否正确 1:正确 0:失败
*/
uint8_t DHT_Get_Temp_Humi_Data(uint8_t buffer[])
{
if(DHT_Start()) //if判断DHT11从机是否应答完毕
{
buffer[0] = DHT_Get_Byte_Data(); //读取温度的整数放入数组第一个元素
buffer[1] = DHT_Get_Byte_Data(); //读取温度的小数放入数组第二个元素
buffer[2] = DHT_Get_Byte_Data(); //读取湿度的整数放入数组第三个元素
buffer[3] = DHT_Get_Byte_Data(); //读取温度的小数放入数组第四个元素
buffer[4] = DHT_Get_Byte_Data(); //读取的校验数据放入数组的第五个元素,用来检测数据是否读取正确
}
return (buffer[0]+buffer[1]+buffer[2]+buffer[3] == buffer[4]) ? 1 : 0; //校验数据是否传输正确
}
头文件DHT11.h源码
#ifndef __DHT11_H
#define __DHT11_H
#include <stm32f10x.h>
//温湿度引脚在这里改
//这里通过宏定义,以方便用户修改引脚
#define DHT_GPIO_PORT GPIOA
#define DHT_GPIO_PIN GPIO_Pin_6
#define DHT_RCC_PORT RCC_APB2Periph_GPIOA
uint8_t DHT_Get_Temp_Humi_Data(uint8_t buffer[]);
#endif
主函数main
/*
关注csdn单片机阿伟
*/
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "delay.h"
#include "DHT11.h"
#include "bsp_usart.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
char str[20];
uint8_t DHT_Buffer[5];
int main(void)
{
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
printf("this is a tese based on stm32f103c8t6 drive DHT11\r\n");
while (1)
{
if(DHT_Get_Temp_Humi_Data(DHT_Buffer)) //DHT_Get_Temp_Humi_Data获取温湿度
{
printf("\r\nTemperature=%d.%d\r\n", DHT_Buffer[2],DHT_Buffer[3]); //打印温度
printf("humidity=%d.%d\r\n", DHT_Buffer[0],DHT_Buffer[1]); //打印湿度
}
}
}
/*********************************************END OF FILE**********************/
运行看效果