1.硬件介绍
本期将为大家介绍智能家居中常用的温湿度模块 ——DHT11。
DHT11 是一款集成了已校准数字信号输出的温湿度复合传感器,它采用专门的数字模块采集技术和温湿度传感技术,不仅稳定性可靠,还具备响应速度快、抗干扰能力强的特点。
传感器内部集成了高分子电阻式感湿元件和 NTC 测温元件,更关键的是,它仅需一根数据线即可与单片机连接,通过单总线协议实现与微处理器的通讯。
根据硬件说明书和产品参数,DHT11 的 VCC 引脚支持 3.3V 或 5V 供电(适配多数单片机,无需额外电平转换),DATA 引脚连接单片机用于单总线通讯,GND 引脚接地形成供电回路。”
1.1.测量范围
2.软件设计
2.1.STM32Cubemx配置
①配置我们用到的引脚PC3。
②配置引脚GPIO模式为推挽输出。
③配置为引脚为非上拉和下拉,浮空模式。
2.1.1.输入输出模式配置
void DH11_GPIO_Init_IN()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO 端口时钟使能
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置 GPIO 引脚模式为输入
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
void DH11_GPIO_Init_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO 端口时钟使能
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置 GPIO 引脚模式为输出
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
2.2.单总线控制(单线双向)
单总线的意思是,STM32作为主机、DTH11温湿度模块作为从机,主机发送控制命令,以及获取传感器传输回来的数据,都只需要使用一根信号线。
2.2.1.通讯过程
DATA 线用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右。数据包含了小数部分和整数部分,不过根据产品说明书表示,当前小数部分用于以后扩展,现读出为零。
操作流程如下:
一次完整的数据传输为40bit,高位先出。数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和。
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
用单片机发送一次开始信号后,DHT11将从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据。
从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
//获取数据
void DHT11_REC_Data(void)
{
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,CHECK;
DHT11_Start(); //主机发送信号
dht11_high; //拉高电平
if( Read_Data == 0 ) //判断DHT11是否响应
{
while( Read_Data == 0); //低电平变高电平,等待低电平结束
while( Read_Data == 1); //高电平变低电平,等待高电平结束
R_H = DHT11_Rec_Byte();
R_L = DHT11_Rec_Byte();
T_H = DHT11_Rec_Byte();
T_L = DHT11_Rec_Byte();
CHECK = DHT11_Rec_Byte(); //接收5个数据
dht11_low; //当最后一bit数据传送完毕后,DHT11拉低总线 50us
delay_us(55); //这里延时55us
dht11_high; //随后总线由上拉电阻拉高进入空闲状态。
if(R_H + R_L + T_H + T_L == CHECK) //和检验位对比,判断校验接收到的数据是否正确
{
RH = R_H;
RL = R_L;
TH = T_H;
TL = T_L;
}
}
rec_data[0] = RH;
rec_data[1] = RL;
rec_data[2] = TH;
rec_data[3] = TL;
}
2.2.2.起始信号表示
总线空闲状态下为高电平,主机把总线拉低等待DHT11响应,这个过程必须大于18毫秒,保证DHT11能检测到起始信号。
DHT11接收到主机的开始信号后, 等待主机开始信号结束,然后发送80us低电平响应信号,主机发送开始信号结束后,延时等待20-40us后,读取DHT11的响应信号。
主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高。
总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。
//主机发送开始信号
void DHT11_Start(void)
{
DH11_GPIO_Init_OUT(); //输出模式
dht11_high; //先拉高
delay_us(30);
dht11_low; //拉低电平至少18us
delay_ms(20);
dht11_high; //拉高电平20~40us
delay_us(30);
DH11_GPIO_Init_IN(); //输入模式
}
每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。
2.2.3.数据0信号表示
格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常。
当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
2.2.4.数据1信号表示
//获取一个字节
char DHT11_Rec_Byte(void)
{
unsigned char i = 0;
unsigned char data;
for(i=0;i<8;i++) //1个数据就是1个字节byte,1个字节byte有8位bit
{
while(Read_Data == 0); //从1bit开始,低电平变高电平,等待低电平结束
delay_us(30); //延迟30us是为了区别数据0和数据1,0只有26~28us
data <<= 1; //左移
if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
{
data |= 1; //数据+1
}
while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
}
return data;
}
2.3.完整代码实现
2.3.1.dth11.c
#include "dth11.h"
#include "delay.h"
unsigned int rec_data[4];//数据
void DH11_GPIO_Init_IN()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO 端口时钟使能
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置 GPIO 引脚模式为输入
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
void DH11_GPIO_Init_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// GPIO 端口时钟使能
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置 GPIO 引脚模式为输出
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//主机发送开始信号
void DHT11_Start(void)
{
DH11_GPIO_Init_OUT(); //输出模式
dht11_high; //先拉高
delay_us(30);
dht11_low; //拉低电平至少18us
delay_ms(20);
dht11_high; //拉高电平20~40us
delay_us(30);
DH11_GPIO_Init_IN(); //输入模式
}
//获取一个字节
char DHT11_Rec_Byte(void)
{
unsigned char i = 0;
unsigned char data;
for(i=0;i<8;i++) //1个数据就是1个字节byte,1个字节byte有8位bit
{
while( Read_Data == 0); //从1bit开始,低电平变高电平,等待低电平结束
delay_us(30); //延迟30us是为了区别数据0和数据1,0只有26~28us
data <<= 1; //左移
if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
{
data |= 1; //数据+1
}
while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
}
return data;
}
//获取数据
void DHT11_REC_Data(void)
{
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,CHECK;
DHT11_Start(); //主机发送信号
dht11_high; //拉高电平
if( Read_Data == 0 ) //判断DHT11是否响应
{
while( Read_Data == 0); //低电平变高电平,等待低电平结束
while( Read_Data == 1); //高电平变低电平,等待高电平结束
R_H = DHT11_Rec_Byte();
R_L = DHT11_Rec_Byte();
T_H = DHT11_Rec_Byte();
T_L = DHT11_Rec_Byte();
CHECK = DHT11_Rec_Byte(); //接收5个数据
dht11_low; //当最后一bit数据传送完毕后,DHT11拉低总线 50us
delay_us(55); //这里延时55us
dht11_high; //随后总线由上拉电阻拉高进入空闲状态。
if(R_H + R_L + T_H + T_L == CHECK) //和检验位对比,判断校验接收到的数据是否正确
{
RH = R_H;
RL = R_L;
TH = T_H;
TL = T_L;
}
}
rec_data[0] = RH;
rec_data[1] = RL;
rec_data[2] = TH;
rec_data[3] = TL;
}
2.3.2.dth11.h
#ifndef _DTH11_H_
#define _DTH11_H_
#include "main.h"
#define dht11_high HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET)
#define dht11_low HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET)
#define Read_Data HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_3)
void DHT11_GPIO_Init_OUT(void);
void DHT11_GPIO_Init_IN(void);
void DHT11_Start(void);
unsigned char DHT11_REC_Byte(void);
void DHT11_REC_Data(void);
#endif
2.3.3.main.c
int main()
{
DHT11_REC_Data();
delay_ms(1000);
}