ESP8266和DHT11通讯


DHT11介绍

参考文献《DHT11温湿度模块学习手册》
DHT11传感器采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。设备(主机或从机)通过一个漏极开路或三态端口(单片机10口)连至该数据线,以允许设备在不发送数据时能够释放总线(由上拉电阻控制,把总线上拉成高电本) ,而让其它设备使用总线;单总线通常要求,外接一个约4.7k2的上拉电阻,这样,当总线闲置时,其状态为高电平(模块中已经集成4.7k上拉电阻)。由于它们是主从结极,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。

1.通讯与数据说明

DHT11采用单总线通讯协议,与单片机通讯过程:

主机发送起始信号
DHT11检测到信号并发送响应
DHT11发送40位数据高位先出
DHT11发送结束信号

在这里插入图片描述
接受到的40位数据为:湿度高8位,湿度低8位,温度高8位,温度低8位, 8位校验位。湿度的高8位是测量湿度的整数,湿度的低8位是测量湿度的小数(湿度的小数部分为0) ;温度的高8位是测量温度的整数,温度的低8位是测量温度的小数;8位校验和-湿度高8位+湿度低8位+温度高8位+温度低8位,可参考下图:

在这里插入图片描述
注意:当温度低于0℃是,温度数据低8位最高位位置位1.
温度高8位|温度低8位|湿度高8位|湿度低8位=校验位

2. DHT11通讯实现

主要介绍dht11.c 与dht11.h 文件。

  • DHT11上电后需要等待1S,并且数据总线要保持高电平
  • 主机从DHT11读取的温湿度数据总是前一次的测量值,如果测量间隔时间较长(超过45) ,请连续读取两次值,然后以第二次读取的值为实时温湿度值
  • 当主机要对数据总线操作操作(拉高/拉低)时,应配置为输出模式; DHT11对数据总线操作(拉高/拉低)时,主机应配置为输入模式
  • 单片机用延迟函数时,误差不能太大,否则无法准确读取温湿度的值
  • 通讯协议的实现函数多次用到超时检测,这是为了避免程序运行时DHT11出现错误而卡在while中
    通讯可按照以下的步骤进行(函数实现在dhtl1.c文件中) :
    1. 主机发送起始信号并检测DHT11的响应信号。主机会发送一个时间T(18ms<T<30ms)的低电平作为起始信号(拉低后要释放总线) , DHT11检测到起始信号后会把信号总线拉低83us作为响应信号,然后拉高87us通知主机准备接收数据。
      -在这里插入图片描述
  • 2)主机接收DHT11发送的数据(高位先出) 。在DHT11发送87us高电平的通知信号后紧接着发送40Bit数据。数据0由54us的低电平与23-27us的高电平构成;数据1由54us的低电平与68-74us构成,时序图如下:

在这里插入图片描述
在这里插入图片描述

    1. 主机读取DHT11发送的40Bit数据并检测DHT11的结束信号。DHT11发送完40Bit数据后会继续输出54us的低电平作为结束信号,然后转为输入状态。这时主机在检测到DHT11的结束电平结束后应该配置为输出模式并输出高电。时序图如下:
  • 在这里插入图片描述
  • 4)数据时序图
    在这里插入图片描述

3.官方程序

1:输出启动信号>>接受响应信号

在这里插入图片描述

2.读取一个位的数据

在这里插入图片描述

3.数据用结构体保存

读取的DHT11 数据是用一个结构体保存,这样可以把40Bit 数据分为湿度的高八位、湿度的低八位、温度
的高八位、温度的低八位、八位校验数据保存,结构体定义在dht11.h 文件中
在这里插入图片描述
在这里插入图片描述

4.ESP8266 SDK函数库

#include "driver/dht11.h"		// DHT11头文件


// 全局变量
//==================================================================================
// DHT11_Data_Array[0] == 湿度_整数_部分
// DHT11_Data_Array[1] == 湿度_小数_部分
// DHT11_Data_Array[2] == 温度_整数_部分
// DHT11_Data_Array[3] == 温度_小数_部分
// DHT11_Data_Array[4] == 校验字节
// DHT11_Data_Array[5] == 【1:温度>=0℃】【0:温度<0℃】
//-----------------------------------------------------
u8 DHT11_Data_Array[6] = {0};	// DHT11数据数组

u8 DHT11_Data_Char[2][10]={0};	// DHT11数据字符串【行:湿/温度】【列:数据字符串】
// DHT11_Data_Char[0] == 【湿度字符串】
// DHT11_Data_Char[1] == 【温度字符串】
//==================================================================================


// 毫秒延时函数
//=================================================
void ICACHE_FLASH_ATTR Dht11_delay_ms(u32 C_time)
{	for(;C_time>0;C_time--)
		os_delay_us(1000);
}
//=================================================


// GPIO_5(DHT11信号线)设为输出模式,并输出参数对应的电平
//===========================================================================
void ICACHE_FLASH_ATTR DHT11_Signal_Output(u8 Value_Vol)
{
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U,	FUNC_GPIO5);	// GPIO5设为IO口
	GPIO_OUTPUT_SET(GPIO_ID_PIN(5),Value_Vol);				// IO5设为输出=X
}
//===========================================================================


// GPIO_5(DHT11信号线)设为输入模式
//===========================================================================
void ICACHE_FLASH_ATTR DHT11_Signal_Input(void)
{
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U,	FUNC_GPIO5);	// GPIO5设为IO口
	GPIO_DIS_OUTPUT(GPIO_ID_PIN(5));		// GPIO5失能输出(输入)
}
//===========================================================================


// DHT11:输出起始信号->接收响应信号
//-----------------------------------------------------
// 返回值:		0		成功
//				1		失败:规定时间内未接收到响应信号
//				2		失败:响应信号的低电平时长超时
//===========================================================================
u8 ICACHE_FLASH_ATTR DHT11_Start_Signal_JX(void)
{
	u8 C_delay_time = 0;	// 延时计时

	// IO5抬高
	//--------------------------------------------------------
	DHT11_Signal_Output(1);	// DHT11信号线(IO5) == 输出高
	Dht11_delay_ms(1);

	// IO5拉低(25ms):起始信号
	//---------------------------------------------
	GPIO_OUTPUT_SET(GPIO_ID_PIN(5),0);	// IO5 = 0
	Dht11_delay_ms(25);

	// IO5抬高【注:起始信号结束后的约13us,DHT11开始输出信号】
	//---------------------------------------------------------
	GPIO_OUTPUT_SET(GPIO_ID_PIN(5),1);	// IO5 = 1
	os_delay_us(5);		// 延时5us


	// 接收响应信号
	//……………………………………………………………………………………
	// IO5设为输入:接收DHT11数据
	//-------------------------------------------------
	DHT11_Signal_Input();	// DHT11信号线(IO5) = 输入

	// 等待响应信号的低电平【最迟等50us】
	//-------------------------------------------------------------
	while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<50 )
	{
		os_delay_us(1);		// 1us计时
		C_delay_time++;
	}

	// 响应信号超时未收到
	//--------------------------------------------------
	if(C_delay_time >= 50)
		return 1;	// 失败:规定时间内未接收到响应信号


	C_delay_time = 0 ;		// 低电平计时开始

	// 响应信息的低电平时长计时【最多170us】
	//-------------------------------------------------------------
	while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<170 )
	{
		os_delay_us(1);
		C_delay_time++;	// 低电平时长
	}

	// 响应信号的低电平时长超时
	//------------------------------------------------
	if(C_delay_time >= 170)
		return 2;	// 失败:响应信号的低电平时长超时

	// 响应信号的低电平成功接收
	//--------------------------
	else
		return 0;	// 成功
}
//===========================================================================


// 读取DHT11一位数据
//--------------------------------
// 返回值:		0		数据=="0"
//				1		数据=="1"
//======================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Bit(void)
{
	u8 C_delay_time = 0;	// 延时计时

	// 等待响应信息的低电平【最迟等150us】
	//-------------------------------------------------------------
	while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<150 )
	{
		os_delay_us(1);		// 1us计时
		C_delay_time++;
	}

	C_delay_time = 0 ;		// 低电平计时开始

	// 数据位的低电平时长计时【最多200us】
	//-------------------------------------------------------------
	while( GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<120 )
	{
		os_delay_us(1);
		C_delay_time++;	// 低电平时长
	}

	// 数据位的低电平结束后,是数据位的高电平
	// 数据"0"的高电平时长 == [23~27us]
	// 数据"1"的高电平时长 == [68~74us]
	//------------------------------------------------
	os_delay_us(45);	// 跳过数据"0"的高电平部分

	// 延时45us后,检测信号线电平
	// 如果此时信号线电平==1 => 数据=="1"
	// 如果此时信号线电平==0 => 数据=="0"
	//-------------------------------------
	return GPIO_INPUT_GET(GPIO_ID_PIN(5));
}
//======================================================================

// 读取DHT11一个字节
//======================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Byte(void)
{
	u8 C_Bit = 0;	// 位计数

	u8 T_DHT11_Byte_Data = 0;	// DHT11字节数据

	for(; C_Bit<8; C_Bit++)		// 读取DHT11一个字节
	{
		T_DHT11_Byte_Data <<= 1;

		T_DHT11_Byte_Data |= DHT11_Read_Bit();	// 一位一位的读取
	}

	return T_DHT11_Byte_Data;	// 返回读取字节
}
//======================================================================



// 完整的读取DHT11数据
//-----------------------------------------------
// 返回值:		0		DHT11数据读取成功
//				1		结束信号的低电平时长超时
//				2		启动DHT11传输_失败
//				3		校验错误
//==============================================================================
u8 ICACHE_FLASH_ATTR DHT11_Read_Data_Complete(void)
{
	u8 C_delay_time = 0;	// 延时计时

	// 启动DHT11传输_成功
	//------------------------------------------------------------------------
	if(DHT11_Start_Signal_JX() == 0)	// DHT11:输出起始信号->接收响应信号
	{
		DHT11_Data_Array[0] = DHT11_Read_Byte();	// 湿度_整数_部分
		DHT11_Data_Array[1] = DHT11_Read_Byte();	// 湿度_小数_部分
		DHT11_Data_Array[2] = DHT11_Read_Byte();	// 温度_整数_部分
		DHT11_Data_Array[3] = DHT11_Read_Byte();	// 温度_小数_部分
		DHT11_Data_Array[4] = DHT11_Read_Byte();	// 校验字节


		// 如果此时是最后一位数据的高电平,则等待它结束
		//-----------------------------------------------------------
		while(GPIO_INPUT_GET(GPIO_ID_PIN(5))==1 && C_delay_time<100)
		{
			os_delay_us(1);		// 1us计时
			C_delay_time++;
		}

		C_delay_time = 0 ;		// 低电平计时开始


		// 结束信号的低电平时长计时
		//-----------------------------------------------------------
		while(GPIO_INPUT_GET(GPIO_ID_PIN(5))==0 && C_delay_time<100)
		{
			os_delay_us(1);		// 1us计时
			C_delay_time++;
		}

		//-----------------------------------------------------------
		if(C_delay_time >= 100)
			return 1;		// 返回1,表示:结束信号的低电平时长超时


		// 数据校验
		//-----------------------------------------------
		if(	DHT11_Data_Array[4] ==
			DHT11_Data_Array[0] + DHT11_Data_Array[1] +
			DHT11_Data_Array[2] + DHT11_Data_Array[3] )
		{

			// 读取DHT11数据结束,ESP8266接管DHT11信号线
			//-----------------------------------------------------------
			//os_delay_us(10);
			//DHT11_Signal_Output(1);	// DHT11信号线输出高(ESP8266驱动)


			// 判断温度是否为 0℃以上
			//----------------------------------------------
			if((DHT11_Data_Array[3]&0x80) == 0)
			{
				DHT11_Data_Array[5] = 1;		// >=0℃
			}
			else
			{
				DHT11_Data_Array[5] = 0;		// <0℃

				DHT11_Data_Array[3] &= 0x7F;	// 更正温度小数部分
			}


			return 0;	// 返回0,表示:温湿度读取成功
		}

		else return 3;		// 返回3,表示:校验错误
	}

	//-----------------------------------------------------
	else return 2;		// 返回2,表示:启动DHT11传输,失败
}
//==============================================================================


// DHT11数据值转成字符串
//======================================================================
void ICACHE_FLASH_ATTR DHT11_NUM_Char(void)
{
	u8 C_char = 0;		// 字符计数

	// 湿度数据字符串
	//…………………………………………………………………………………………
	if(DHT11_Data_Array[0]/100)			// 湿度整数的百位
		DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[0]/100 + 48;

	if((DHT11_Data_Array[0]%100)/10)	// 湿度整数的十位
		DHT11_Data_Char[0][C_char++] = (DHT11_Data_Array[0]%100)/10 + 48;

	// 湿度整数的个位
	//---------------------------------------------------------
	DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[0]%10 + 48;

	DHT11_Data_Char[0][C_char++] = '.';		// 小数点

	// 湿度整数的小数
	//---------------------------------------------------------
	DHT11_Data_Char[0][C_char++] = DHT11_Data_Array[1]%10 + 48;

	DHT11_Data_Char[0][C_char++] = ' ';		// ' '
	DHT11_Data_Char[0][C_char++] = '%';		// '%'
	DHT11_Data_Char[0][C_char++] = 'R';		// 'R'
	DHT11_Data_Char[0][C_char++] = 'H';		// 'H'
	DHT11_Data_Char[0][C_char] 	 =  0 ;		// 添0
	//…………………………………………………………………………………………

	C_char = 0;		// 重置

	// 温度数据字符串
	//…………………………………………………………………………………………
	if(DHT11_Data_Array[5]==0)			// 温度 < 0℃
		DHT11_Data_Char[1][C_char++] = '-';

	if(DHT11_Data_Array[2]/100)			// 湿度整数的百位
		DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[2]/100 + 48;

	if((DHT11_Data_Array[2]%100)/10)	// 湿度整数的十位
		DHT11_Data_Char[1][C_char++] = (DHT11_Data_Array[2]%100)/10 + 48;

	// 湿度整数的个位
	//---------------------------------------------------------
	DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[2]%10 + 48;

	DHT11_Data_Char[1][C_char++] = '.';		// 小数点

	// 湿度整数的小数
	//---------------------------------------------------------
	DHT11_Data_Char[1][C_char++] = DHT11_Data_Array[3]%10 + 48;

	DHT11_Data_Char[1][C_char++] = ' ';		// ' '
	DHT11_Data_Char[1][C_char++] = 'C';		// 'C'
	DHT11_Data_Char[1][C_char]   =  0 ;		// 添0
	//…………………………………………………………………………………………
}
//======================================================================

5.示波器通讯波形分析

1:主机启动>>释放

在这里插入图片描述

2. 从机拉低>>抬高总线

在这里插入图片描述

3.数据接收(高位先出)

在这里插入图片描述

4. 结束信号(54ms)

在这里插入图片描述

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值