物联网专题12:ESP8266 DHT11

DHT11是一款应用广泛的温湿度传感器,有操作简单、精度高等优点。

下图为ESP-12F开发板的原理图:

DHT11直接接入到GPIO5引脚上,GPIO5就是DHT11的信号线。

关于DHT11的驱动时序,在网上有非常详细的资料。接下来是 DHT11.c 源程序:

驱动程序步骤:

1 定义了存储温湿度数据的数组和字符串;

// 全局变量
//==================================================================================
// 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] == 【温度字符串】
//==================================================================================

2 配置DHT11信号线 GPIO5 的输入输出模式;

// 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失能输出(输入)
}

3 DHT11输出起始信号,并等待接收应答信号;

时序图:

函数实现如下:

// 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;	// 成功
}

3 读取数据的函数;

// 读取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传输,失败
}

然后看一下 user_init 函数:

void ICACHE_FLASH_ATTR user_init(void)
{
	uart_init(115200,115200);	// 初始化串口波特率
	os_delay_us(10000);			// 等待串口稳定
	os_printf("\r\n=================================================\r\n");
	os_printf("\t Project:\t%s\r\n", ProjectName);
	os_printf("\t SDK version:\t%s", system_get_sdk_version());
	os_printf("\r\n=================================================\r\n");

	// OLED显示初始化
	//--------------------------------------------------------
	OLED_Init();							// OLED初始化
	OLED_ShowString(0,0,"Humidity:");		// 湿度
	OLED_ShowString(0,4,"Temperature:");	// 温度
	//--------------------------------------------------------

	LED_Init_JX();		// LED初始化

	OS_Timer_1_Init_JX(3000,1);		// 3秒定时(重复)
}

在这里,我们设置了软件定时器 3 秒定时,主要的处理是在定时器回调函数中,如下所示:

// 定时的回调函数
//==========================================================================================
void ICACHE_FLASH_ATTR OS_Timer_1_cb(void)
{
	if(DHT11_Read_Data_Complete() == 0)		// 读取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】
		//-------------------------------------------------


		// 温度超过30℃,LED亮
		//----------------------------------------------------
		if(DHT11_Data_Array[5]==1 && DHT11_Data_Array[2]>=30)
			GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0);		// LED亮
		else
			GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1);		// LED灭


		// 串口输出温湿度
		//---------------------------------------------------------------------------------
		if(DHT11_Data_Array[5] == 1)			// 温度 >= 0℃
		{
			os_printf("\r\n湿度 == %d.%d %RH\r\n",DHT11_Data_Array[0],DHT11_Data_Array[1]);
			os_printf("\r\n温度 == %d.%d ℃\r\n", DHT11_Data_Array[2],DHT11_Data_Array[3]);
		}
		else // if(DHT11_Data_Array[5] == 0)	// 温度 < 0℃
		{
			os_printf("\r\n湿度 == %d.%d %RH\r\n",DHT11_Data_Array[0],DHT11_Data_Array[1]);
			os_printf("\r\n温度 == -%d.%d ℃\r\n",DHT11_Data_Array[2],DHT11_Data_Array[3]);
		}

		// OLED显示温湿度
		//---------------------------------------------------------------------------------
		DHT11_NUM_Char();	// DHT11数据值转成字符串

		OLED_ShowString(0,2,DHT11_Data_Char[0]);	// DHT11_Data_Char[0] == 【湿度字符串】
		OLED_ShowString(0,6,DHT11_Data_Char[1]);	// DHT11_Data_Char[1] == 【温度字符串】
	}
}

在定时器回调函数中,做了如下处理:

1 每3秒读取一次温湿度数据;

2 如果温度超过30℃,则LED亮预警;

3 通过串口打印温湿度数据;

4 通过OLED显示温湿度数据;

 

接下来,编译执行程序。结果如下:

开发板OLED显示:

串口调试信息输出:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值