DHT11温湿度模块协议编写

系列文章目录

本文旨在介绍DHT11在stm32中的使用和如何根据厂家所给参考手册书写代码

本次芯片选择stm32F103ZET6

本次硬件选择DHT11

本次编写软件选择cubeIDE(基于hal库的编写)


文章目录


提示:以下是本篇文章正文内容,下面案例可供参考

一、DHT11模块基础介绍

1.简介

DHT11模块是一款常用的数字温湿度传感器,广泛应用于各种嵌入式系统、智能家居和气象站等领域,相比于 DS18B20 只能测量温度,DHT11 既能检测温度又能检测湿度,不过 DHT11 的精度和测量范围都要低于 DS18B20,其温度测量范围为 0~50℃,误差在±2℃;湿度的测量范围为 20%~90%RH(Relative Humidity 相对湿度—指空气中水汽压与饱和水汽压的百分比),误差在±5%RH

2.与MCU的通信方式

DHT11采用单总线通信

单总线通信的基本原理

单总线通信的基本原理是通过调整高低电平的时长来发送'0'和'1',并利用起始位和终止位来开始和结束通信。主机和从机之间的通信通常包括以下几个步骤:

  1. 初始化:主机通过拉低单总线480 ~ 960 us产生复位脉冲,然后释放总线,进入接收模式。
  2. 识别从机:主机发送ROM命令以识别从机,并等待从机应答。
  3. 交换数据:主机与从机之间进行数据传输,数据传输是半双工的,即接收和发送不能同时进行

我个人对单总线通信的理解就是一种不依赖于单片机内部通信外设(usart iic can)的通信方式,它只通过MCU发送高电平和低电平来进行主机与从机之间的通信,换句通俗易懂的话来说就是我们用手法来1写出协议来代替MCU外设进行通信

二、代码介绍

1.结合参考手册书写

1.电路讲解

在DHT11的电路中我们外接了一个上拉电阻,之所以要外接这个上拉电阻的原因是,我们在前面说过DHT11是单总线通信,在未进行通信时我们需要将DHT11的电平拉高,为了拉高电平使总线一直保持高电平,我们外接了一个上拉电阻。

Pin名称解释
1VDD供电3.3—5v
2GND接地,电源负极
3DATA随便初始化一个GPIO口PA5
4NC悬空不接线

2.DHT11通信协议及数据格式

DATA 用于微处理器与 DHT 11 之间的通讯 和 同步 , 采用单总线数据格式 , 一次
通讯时间 4ms 左右 , 数据分小数部分和整数部分 , 具体格式在下面说明 , 当前小数
部分用于以后扩展 , 现读出为零 . 操作流程如下 :
一次完整的数据传输为 40bit, 高位先出
数据格式 :8bit 湿度整数数据 +8bit 湿度小数数据
+8bi 温度整数数据 +8bit 温度小数数据
+8bit 校验和
数据传送正确时校验和数据等于 “ 8bit 湿度整数数据 +8bit 湿度小数数据
+8bi 温度整数数据 +8bit 温度小数数据 ” 所得结果的末 8 位。

从上面手册中所给的说明,我们可以知道的是一次完整的数据传输位40bit,这40bit的数据分为五个部分  1.温度整数  2.温度小数数据 3.湿度数据 4.湿度小数数据 5. 校验位  数据发送的顺序是高位在前,低位在后的顺序传输。

3.操作时序(整个流程的框架)

上图是我们读取DHT11的整个流程图,我们接下来将其细分为三个阶段 1.MCU唤醒DHT11  2.DHT11接收信号做出回应 3.DHT11发送信号 

1.MCU唤醒DHT11 

图中的黑线代表我们的主机信号,而黄线代表从机(DHT11)信号,首先我们主机将电平拉低也就是输出一个低电平信号,这个低电平信号持续18ms,之后再将信号线拉高20—40us也就是输出高电平

我们首先配置GPIO模式为推挽输出模式,选择引脚为PA5,选择高速输出。

void DHT_GPIO_SET_OUTPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=GPIO_PIN_5;
	GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

然后配置PA5先输出18ms的低电平信号将信号线拉低,再控制PA5输出高电平 ,这样一个过程就完成了MCU唤醒DHT11的全过程

     DHT_GPIO_SET_OUTPUT();
	 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
	 HAL_Delay(18);
	 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
	 Delay_us(20);

 然后就是从机DHT11做出反应,DHT11收到主机的唤醒后,先将高电平拉低80us,然后再将低电平信号拉高80us,在这之后DHT11开始发送信号。(对应下图)

 

从机响应之后,就会开始发送信号,所以这个时候我们要手动判断DHT11是否唤醒,我们需要手动编写代码来读取GPIO口的信号变化。

我们首先将GPIO口设置为输入模式来保证我们能读取DHT11的信号(如下图)

void DHT_GPIO_SET_INPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=GPIO_PIN_5;
	GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

 我们先读取PA5是否为0,也就对应上图中的DHT11是否将信号拉低,然后就是while循环,这个循环的判断是在判断PA5是否为0 retry是否小于100,如果满足这个条件就进入函数,进入while后先延迟1us,然后对retry进行累加,直到我们将信号拉高或者retry大于100才会跳出循环,这个过程就是我们在等待DHT11将信号拉低后再将信号拉高的这个瞬间,一但DHT11将信号拉高的这个瞬间,PA5就不满足等于0的条件,就会跳出第一个循环,而之所以让retry<100 对应的就是80us的低电平时间,然后第二个while循环就是在判断电平是否又被DHT11拉低(这个过程比较绕多看几遍代码就懂了)那这里为什么又要将电平拉低呢? 原因就是在信号开始发送前,电平又会被拉为低电平50us(如下图)。

uint8_t retry=0;
	 uint8_t i;
	DHT_GPIO_SET_INPUT();
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
	{

		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0 && retry<100)
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1 && retry<100)
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;

	}

 完成了上面的唤醒过程,这个时候我们就可以开始接收信号,而在DHT11的信号发送过程中26—28us时长的高电平代表0,70us时长的高电平代表1.

 有了上面的补充我们知道在DHT11中0和1的表示,接下来我们来编写数据读写函数,如下图首先定义了四个变量 ,然后是一个for循环,循环八次,(之所以要循环八次的原因是我们的信号发送是以bit为单位的,我们数据的格式是每次八个bit位)进入for循环之后我们先判断电平信号是否为0 retry是否小于100,如果符合这两个条件就进入while循环,而这个循环存在的实际意义则是为了判断我们是否从50us的低电平信号跳转到数据所代表的高电平信号,往下走先让retry为0,再延时40us,在延迟40us后如果信号仍为高电平,那么就代表我们接受的信号数字为1,如果不是我们接受的信号数字就是0,具体代码呈现在temp的值,然后接下来就又是一个while判断,这个判断就是在判断下一个bit的发送(即将电平拉低的信号,上图中有显示),然后最后我们将我们得到的数据通过移位操作存入ReadData中(之所以要移位,是因为我们DHT11是高位先行,而一个完整的数据是由8个bit位组成的二进制数字)

uint8_t DHT_Read_Byte(void)
{
	 uint8_t ReadData=0;
	 uint8_t temp;
	 uint8_t retry=0;
	 uint8_t i;
	 for(i=0; i<8; i++)
	 {
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0 && retry<100)

			{
					Delay_us(1);
				  retry++;
			}
			retry=0;
			Delay_us(40);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1)   temp=1;
			 else   temp=0;

			 while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1 && retry<100)
			 {
				 Delay_us(1);
				 retry++;
			 }
			 retry=0;

			 ReadData<<=1;
			 ReadData |= temp;
	 }

		return ReadData;
}

 最后就是for循环四次,之所以循环四次的原因是我们传输了4个数据1.温度整数  2.温度小数数据 3.湿度数据 4.湿度小数数据 ,而这每个数据又是由八个bit位组成的二进制数字(也对应上面那个for循环八次的原因)

for(i=0; i<5; i++)
		{
			 Data[i] = DHT_Read_Byte();
		}

 完整代码如下,唯一没有讲的是,就是最后我们要进行一次数据校验,也就是我们的第五位校验数据。

uint8_t Data[5]={0x00,0x00,0x00,0x00,0x00};

void Delay_us(uint16_t us){
	uint16_t differ = 0xffff-us;
	__HAL_TIM_SET_COUNTER(&htim1,differ);
	HAL_TIM_Base_Start(&htim1);

	while(differ < 0xffff){
		differ = __HAL_TIM_GET_COUNTER(&htim1);
	}
	HAL_TIM_Base_Stop(&htim1);
}



void DHT_GPIO_SET_OUTPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=GPIO_PIN_5;
	GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
//	GPIO_InitStructure.Pull=;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

void DHT_GPIO_SET_INPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=GPIO_PIN_5;
	GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}

uint8_t DHT_Read_Byte(void)
{
	 uint8_t ReadData=0;
	 uint8_t temp;
	 uint8_t retry=0;
	 uint8_t i;
	 for(i=0; i<8; i++)
	 {
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0 && retry<100)

			{
					Delay_us(1);
				  retry++;
			}
			retry=0;
			Delay_us(40);
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1)   temp=1;
			 else   temp=0;

			 while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1 && retry<100)
			 {
				 Delay_us(1);
				 retry++;
			 }
			 retry=0;

			 ReadData<<=1;
			 ReadData |= temp;
	 }

		return ReadData;
}

uint8_t DHT_Read(void)
{
	 uint8_t retry=0;
	 uint8_t i;

	 DHT_GPIO_SET_OUTPUT();
	 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
	 HAL_Delay(18);
	 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
	 Delay_us(20);


	DHT_GPIO_SET_INPUT();
	Delay_us(20);
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0)
	{

		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==0 && retry<100)
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==1 && retry<100)
		{
		   Delay_us(1);
			 retry++;
		}
		retry=0;


		for(i=0; i<5; i++)
		{
			 Data[i] = DHT_Read_Byte();
		}
		Delay_us(50);

	}

	 uint32_t sum=Data[0]+Data[1]+Data[2]+Data[3];
	 if((sum)==Data[4])    return 1;
	   else   return 0;

}

总结

DHT11的协议是比较繁琐的,需要大家仔细观看,重点在于要结合DHT11参考手册进行详细阅读,还要懂一定的编程技巧和原理,希望大家能仔细琢磨和观看,每看一次都是对自己编程能力的提升。

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值