当前位置: 首页 > news >正文

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

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

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

一、DHT11模块介绍

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

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

型号测量范围测湿精度测温精度
DHT1120-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();  //复位DHT11return DHT11_Check();//等待DHT11的回应
} 

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

        步骤1

//复位DHT11
void DHT11_Rst(void)	   
{                 DHT11_IO_OUT(); 	//SET OUTPUTDHT11_DQ_OUT=0; 	//拉低DQdelay_ms(20);    	//拉低至少18msDHT11_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);//等待40usif(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 */}
}

http://www.lryc.cn/news/597002.html

相关文章:

  • 利用aruco标定板标定相机
  • EDoF-ToF: extended depth of field time-of-flight imaging解读, OE 2021
  • C Primer Plus 第6版 编程练习——第10章(上)
  • 2025暑期—05神经网络-BP网络
  • 深入解析预训练语言模型在文本生成中的革命性应用:技术全景与未来挑战
  • 工业微控制器的启动过程以及安全设计所面临的挑战
  • TODAY()-WEEKDAY(TODAY(),2)+1
  • 数据结构系列之二叉搜索树
  • 关于针对 DT_REG 出现红色波浪线的问题(编译错误/IDE警告),以下是 精准解决方案,保持你的代码功能完全不变:
  • LeetCode11~20题解
  • 动态递归之正则表达式
  • 西安电子科技大学金融学431考研经历分享
  • 分布式任务调度实战:XXL-JOB与Elastic-Job深度解析
  • 一次Oracle集群脑裂问题分析处理
  • PetaLinux 使用技巧与缓存配置
  • Oracle迁移到高斯,查询字段默认小写,解决办法
  • Zookeeper学习专栏(七):集群监控与管理
  • MySQL binlog解析
  • IDEA maven加载依赖失败不展示Dependencies项
  • 华为云数据库 GaussDB的 nvarchar2隐式类型转换的坑
  • Tomcat与JDK版本对照全解析:避坑指南与生产环境选型最佳实践
  • 【矩阵专题】Leetcode73.矩阵置零
  • 华为云开发者空间 × DeepSeek-R1 智能融合测评:云端开发与AI客服的协同进化
  • (46)elasticsearch-华为云CCE无状态负载部署
  • 基于Dapr Sidecar的微服务通信框架设计与性能优化实践
  • python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件
  • 华为云中,列表中的镜像无法删除可能由多种原因导致
  • MybatisPlus操作方法详细总结
  • CNN实战案例:从图像识别到医疗诊断
  • 19-动态路由