当前位置: 首页 > 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.dtcms.com/a/293570.html

相关文章:

  • 嵌入式学习-(李宏毅)机器学习(1)-day28
  • 本地电脑映射端口到外网访问的开启方法和注意事项,内网服务提供跨网使用简单操作实现
  • 神经网络和机器学习的一些基本概念
  • 某种物联网SIM卡流量查询方法
  • BQ4050RSMR DIOTEC高精度锂电池保护与电量监测芯片 集成保护+计量+通信
  • 2025年Zigbee技术白皮书:全球物联网无线通信的关键创新
  • 【Linux | 网络】应用层(HTTPS)
  • 如何在 Ubuntu 20.04 Linux 上安装 TeamSpeak 客户端
  • SparkSQL 聚合函数 MAX 对 NULL 值的处理
  • AWS Lambda IoT数据处理异常深度分析:从告警到根因的完整排查之路
  • Notepad++工具操作技巧
  • BitDistiller:通过自蒸馏释放 Sub-4-Bit 大语言模型的潜力
  • React Native + Expo 入坑指南:从核心概念到实战演练
  • Android 测试全指南:单元测试与UI测试框架详解
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——3. QML入门:像搭积木一样构建UI
  • 论文笔记:Tuning Language Models by Proxy
  • 图机器学习(16)——图数据与自然语言处理
  • qiankun 和 Element UI 影响 el-cascader 宽度问题
  • 计算机毕设分享-基于SpringBoot的房屋租赁系统(开题报告+源码+Lun文+开发文档+数据库设计文档)
  • win11安装erlang和rabbitmq
  • 基于 XGBoost 与 SHAP 的医疗自动化办公与可视化系统(上)
  • Kafka——Kafka中的位移提交
  • ITIL 4:云计算与微服务对组织架构的影响
  • Elasticsearch(ES)介绍和安装
  • 20250704-基于强化学习在云计算环境中的虚拟机资源调度研究
  • ElasticSearch基础数据管理详解
  • CSS3文本阴影特效全攻略
  • Paimon的部分更新以及DeleteVector实现
  • 把xml的格式从utf-8-bom转为utf-8
  • HarmonyOS Flutter Boost完全接入手册:爬完所有坑的实战指南