DHT11时序详解 (基于stm32)

前情提要:

DHT11是一款单总线温湿度传感器,外部接线非常简单,只需要三根线(Vcc,Gnd,Data)。

要驱动这个传感器,首先要了解传输时序,和数据格式。

  • DHT11数据格式

一次完整的数据传输为40bit,高位先出。数据分小数部分和整数部分,数据格式:

  • 8bit湿度整数数据
  • 8bit湿度小数数据
  • 8bit温度整数数据
  • 8bit温度小数数据
  • 8bit校验和

若数据传送正确,则校验和数据 = “8bit 湿度整数数据 +8bit 湿度小数数据+8bit温度整数数据 +8bit 温度小数数据”所得结果的末8位。

以下是几个函数的详细讲解,每个函数名下面都有此函数的代码,和代码实现出的效果(逻辑分析仪截图),完整代码在文末。

主要由以下几个函数组成:

  • 主机发送开始信号    DHT11_Start();
//*************************************************
//主机发送开始信号,开始采集
void DHT11_Start(void)
{
	DH11_GPIO_Init_OUT(); //输出模式
	
	dht11_low; //拉低总线20ms
	delay_ms(20);
	
	dht11_high; //释放总线20~40us
	delay_us(30);
	
	DH11_GPIO_Init_IN(); //切输入模式
}

逻辑分析仪画面: 

  •  DHT11响应信号 是在采集过程中的一部分,体现在DHT11_Read_Data();函数里面

DHT11_Read_Data();内部主要是是读取5个字节,组成一个完整的数据

*读取一个字节,后面有讲(讲解的顺序是根据dht11时许来讲的)

//*************************************************
//获取数据(获取5个字节)
void DHT11_Read_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_Read_Byte();
		R_L = DHT11_Read_Byte();
		T_H = DHT11_Read_Byte();
		T_L = DHT11_Read_Byte();
		CHECK = DHT11_Read_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;
}

 if( Read_Data == 0 ) //判断DHT11是否响应
            {
                while( Read_Data == 0); //低电平变高电平,等待低电平结束
                while( Read_Data == 1); //高电平变低电平,等待高电平结束

这几行是判断DHT11响应信号

响应信号对应的是“ while( Read_Data == 0); //低电平变高电平,等待低电平结束” 

释放总线对应的是“ while( Read_Data == 1); //高电平变低电平,等待高电平结束”

*这里的讲解可能不是很明白,欢迎提出意见和一起讨论

  • 读取一个字节 DHT11_Read_Byte();
//*************************************************
//获取一个字节
//函数返回值:data
unsigned char DHT11_Read_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; //左移1位
		
		if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
		{
			data |= 1; //数据+1
		}
		
		while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
	}
	
	return data;

逻辑分析仪画面: 

 此段借鉴csdn博主“安赫'”的内容:

还是像检测响应时间那样计算高电平持续时间那就太麻烦了!!!

数据“0”的高电平持续 26~28us,数据“1”的高电平持续70us,每一位数据前都有 50us 的起始时隙。如果我们取一个中间值 40us 来区分数据“0”和数据“1”的时隙。

当数据位之前的 50us 低电平时隙过后,总线肯定会拉高,此时延时 40us 后检测总线状态,如果为高,说明此时处于 70us 的时隙,则数据为“1”;如果为低,说明此时处于下一位数据 50us 的开始时隙,那么上一位数据肯定是“0”。

为什么延时 40us?
由于误差的原因,数据“0”时隙并不是准确 26~28us,可能比这短,也可能比这长。
当数据“0”时隙大于 26~28us 时,
如果延时太短,无法判断当前处于数据“0”的时隙还是数据“1”的时隙;
如果延时太长,则会错过下一位数据前的开始时隙,导致检测不到后面的数据
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/m0_55849362/article/details/126426768

 

while( Read_Data == 0); //从1bit开始,低电平变高电平,等待低电平结束
 delay_us(30); //延迟30us是为了区别数据0和数据1,0只有26~28us 

这两条就实现了判断1和0

在54us左右的低电平时隙结束后,延时30us

此时,如果数据是0,那么此时读到的是低电平(由于0的高电平时间为20us左右,延时30us采样正好超过了20us的高电平)看图中我画的1那里。

如果数据是1,那么此时读到的是高电平(由于1的高电平时间为70us左右,延时30us采样正好在高电平里面)看图中我画的2那里。

以此类推,for循环采样8次就是一个字节了。

//******************************************************************************************************

源码部分

dht11.c

#include "stm32f10x.h" 
#include "DHT11.h"
#include  "delay.h"
//*************************************************
//数据变量数组
unsigned int rec_data[4];// 占用2个字节


//*************************************************
//一个io口分别收和发,所以要对应输入/输出两个状态
//输出
void DH11_GPIO_Init_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}

//输入
void DH11_GPIO_Init_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}


//*************************************************
//主机发送开始信号,开始采集
void DHT11_Start(void)
{
	DH11_GPIO_Init_OUT(); //输出模式
	
	dht11_low; //拉低总线20ms
	delay_ms(20);
	
	dht11_high; //释放总线20~40us
	delay_us(30);
	
	DH11_GPIO_Init_IN(); //切输入模式
}

//*************************************************
//获取一个字节
//函数返回值:data
unsigned char DHT11_Read_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; //左移1位
		
		if( Read_Data == 1 ) //如果过了30us还是高电平的话就是数据1
		{
			data |= 1; //数据+1
		}
		
		while( Read_Data == 1 ); //高电平变低电平,等待高电平结束
	}
	
	return data;
}
//*************************************************
//获取数据(获取5个字节)
void DHT11_Read_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_Read_Byte();
		R_L = DHT11_Read_Byte();
		T_H = DHT11_Read_Byte();
		T_L = DHT11_Read_Byte();
		CHECK = DHT11_Read_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;
}
//*************************************************

dht11.h

#ifndef __DHT11_H
#define __DHT11_H	 
#include "stm32f10x.h"  // Device header   

//void DHT11_init(void);//初始化
//****************************************************************
//define
//****************************************************************
#define dht11_high GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define dht11_low GPIO_ResetBits(GPIOB, GPIO_Pin_12)
#define Read_Data GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)


//****************************************************************
//以下是内部调用部分
//****************************************************************
void DHT11_GPIO_Init_OUT(void);
void DHT11_GPIO_Init_IN(void);
void DHT11_Start(void);
unsigned char DHT11_Read_Byte(void);//获取一个字节


//****************************************************************
//以下是用户调用部分
//****************************************************************
void DHT11_Read_Data(void);//获取数据



#endif




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值