第四章 Freertos物联网实战DHT11温湿度模块

发布于:2025-07-24 ⋅ 阅读:(19) ⋅ 点赞:(0)

        在正式开篇叙述前,这里建议大家可以回顾之前的内容,避免对本文的部分内容理解不清问题,查看整个教程:【Freertos实战】零基础制作基于stm32的物联网温湿度检测(教程非常简易)

        本章开始讲解温湿度模块,并利用上章讲的ESP8266模块,将采集得到的温湿度信息上传到云端,并在云端上显示信息。

一、DHT11模块介绍

        DHT11的供电电压为3~5.5 V。传感器上电后,要等待 1s 以越过不稳定状态,在此期间无需发送任何指令。我自己的话是用的3.3v电压,与stm32主控电压一致。DATA引脚接IO口,大家可以与我一致采用PA11

        模块的工作温度可在0°到50°之间,我们DIY作品一般都在室温下,工作环境是搓搓有余的了。

型号 测量范围 测湿精度 测温精度
DHT11 20-90%RH  0-50摄氏度°C 士5%RH   士2℃

 二、通信过程

1.数据帧

        DHT11通过单根数据线实现双向通信,所以整个通信的状态属于你发我收,你收我发的状态。当我需要获取温湿度的信息时,DHT11会发送5字节的数据,这5字节就组成了一个数据帧,内容为:

         我们依次按位进行接收,每8位数据存入一个字节中,为了避免信号干扰的情况,导致数据出现错误,在结尾处多了一段校验码。在接收完所有的字节后,我们需要将前四个字节相加,若前四个字节全等于校验码,则这段温湿度的数据是正确的。我们后续就可以上传至云端,反之,则可以丢弃了这个数据。

2.时序图

        下面的通信均以PA11口为IO口与DATA进行通信。先贴出硬件初始化部分。

#ifndef __DHT11_H
#define __DHT11_H 
#include "sys.h"   
 
//IO方向设置
#define DHT11_IO_IN()  {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=8<<12;}
#define DHT11_IO_OUT() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=3<<12;}

#define	DHT11_DQ_OUT PAout(11) //数据端口	PA0 
#define	DHT11_DQ_IN  PAin(11)  //数据端口	PA0 

u8 DHT11_Init(void);//初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度
u8 DHT11_Read_Byte(void);//读出一个字节
u8 DHT11_Read_Bit(void);//读出一个位
u8 DHT11_Check(void);//检测是否存在DHT11
void DHT11_Rst(void);//复位DHT11    
#endif
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PG端口时钟
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;				 //PG11端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);				 //初始化IO口
 	GPIO_SetBits(GPIOA,GPIO_Pin_11);						 //PG11 输出高
			    
	DHT11_Rst();  //复位DHT11
	return DHT11_Check();//等待DHT11的回应
} 

        数据帧是站在字节的角度上进行考虑,同时还要知道每个字节中的0和1在时序中的状态是怎么样的,什么条件下DHT11知道它该发数据帧给我,什么时候它处于接收主机控制的状态。这里对照数据手册,罗列出数据时序图        整体过程分为3个步骤:
        ①主机先发送开始信号,主机信号线拉高准备接收数据。
        ②从机会返回一个响应信号进行应答,并将信号拉高准备输出
        ③开始接收数据(一次接收40位)

        步骤1

//复位DHT11
void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//拉低DQ
    delay_ms(20);    	//拉低至少18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	delay_us(30);     	//主机拉高20~40us
}

        步骤2

//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}

 DHT11的0、1时序

        在开始步骤三前,我们先讲一下对于DHT11模块,它的0和1分别是什么样的形式。
        与我们常接触的vcc和gnd代表0和1不同,DHT11发出的0、1时序是以延时的保持时间长短来代表的。我们可以把这一段的时序理解为,DHT11会先把数据线拉低50us,然后延时等待40us,然后再去读取信号线的电平,如果为低电平,则为位“0”;如果为高电平,则为位“1”。

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

        步骤3

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//读取40位数据
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humi=buf[0];
			*temp=buf[2];
		}
	}else return 1;
	return 0;	    
}

三、创建Freertos任务

        在了解完硬件知识和编写完驱动文件后,这里就可以正式创建Freertos任务,将DHT11的温湿度获取放在Freertos上运行,并通过esp8266将数据上传云端。

        引入头文件,创建OLED任务句柄上传温湿度任务句柄OLED任务实现上传温湿度任务实现,设置全局变量,接收DHT11模块的温湿度信息,实现温湿度采集在OLED下位机显示信息上报云端

* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "Task.h"
#include "queue.h"
/* 开发板硬件bsp头文件 */
#include "delay.h"//延时函数
#include "led.h"//LED灯
#include "OLED.h"//显示屏
#include "esp8266.h"//物联网模块
#include "dht11.h"//温湿度模块

/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t Send_Data_Task_Handle = NULL;/* 上传温湿度任务句柄 */
static TaskHandle_t OLED_Task_Handle = NULL;/* OLED任务句柄 */
/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void Send_Data_Task(void* pvParameters);/* 上传温湿度任务实现 */

static void OLED_Task(void* pvParameters);/* OLED任务实现 */

static void BSP_Init(void);/* 用于初始化板载相关资源 */


/*

/*
*************************************************************************
*                             全局变量
*************************************************************************
*/
u8 humidity = 0;//湿度
u8 temp = 0;//温度

/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* OLED 初始化 */
	OLED_Init();
	/* 温湿度 初始化 */
	DHT11_Init();
	/* esp8266 初始化 */
	uart_init(115200);
	
}

/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  taskENTER_CRITICAL();           //进入临界区
	
	/* 创建Send_Data_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Send_Data_Task, /* 任务入口函数 */
                        (const char*    )"Send_Data_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )1,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&Send_Data_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    //printf("创建Send_Data_Task任务成功!\r\n");
	
	/* 创建OLED_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )OLED_Task, /* 任务入口函数 */
                        (const char*    )"OLED_Task",/* 任务名字 */
                        (uint16_t       )512,   /* 任务栈大小 */
                        (void*          )NULL,	/* 任务入口函数参数 */
                        (UBaseType_t    )3,	    /* 任务的优先级 */
                        (TaskHandle_t*  )&OLED_Task_Handle);/* 任务控制块指针 */
  if(pdPASS == xReturn)
    //printf("创建OLED_Task任务成功!\r\n");
	
	
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}


/**********************************************************************
  * @ 函数名  : Send_Data_Task
  * @ 功能说明: Send_Data_Task
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Send_Data_Task(void* parameter)
{	
	esp8266_Init();
	u8 Humi = 0;//湿度
	u8 Temp = 0;//温度
  while (1)
  {
		if(Temp!=temp && Recv_LED_Flag == 0)
		{
			esp8266_mqtt_send_int("Temp", temp);  // 上传温度值
			Temp = temp;
			OLED_Clear();
		}
		if(Humi!=humidity && Recv_LED_Flag == 0)
		{
			esp8266_mqtt_send_int("Humi", humidity);	// 上传湿度值
			Humi = humidity;
			OLED_Clear();
		}
		vTaskDelay(1000);   /* 延时100个tick */
		//esp8266_mqtt_send_bool("LED", false);
  }
}

/**********************************************************************
  * @ 函数名  : OLED_Task
  * @ 功能说明: OLED_Task
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void OLED_Task(void* parameter)
{	
  while (1)
  {
		DHT11_Read_Data(&temp,&humidity);	// 读取湿度值
		OLED_ShowString(1,1,"Temp:");
		OLED_ShowString(2,1,"Humi:");
		OLED_ShowString(3,1,"id:");
		OLED_ShowNum(1,6,temp,2);
		OLED_ShowNum(2,6,humidity,2);
		OLED_ShowNum(3,4,id,2);
		vTaskDelay(300);   /* 延时1000个tick */
  }
}


网站公告

今日签到

点亮在社区的每一天
去签到