项目1:STM32+DHT11+FreeRTOS+emwin+LCD

2024-05-08 21:28

本文主要是介绍项目1:STM32+DHT11+FreeRTOS+emwin+LCD,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【屏幕显示DHT11数据】

     面向对象的思想编写硬件驱动程序,DHT11采集环境中的温湿度数据。使用FreeRTOS提供的任务间通信、同步、互斥,将DHT11的数据传递给显示任务。显示任务中,使用emWin中间件,制作屏幕的各种界面,并将DHT11的数据显示到屏幕上。

项目开发流程:

第一个项目内容:屏幕显示DHT11数据

       面向对象的思想编写硬件驱动程序,DHT11采集环境中的温湿度数据。使用FreeRTOS提供的任务间通信、同步、互斥,将DHT11的数据传递给显示任务。显示任务中,使用emWin中间件,制作屏幕的各种界面,并将DHT11的数据显示到屏幕上。

项目框架:

TFT彩屏

TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal Display。TFT-LCD与无源 TN-LCD、STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关。     

TFT-LCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。应用于电视、手机、电脑、平板等各种电子产品。

分辨率:240*320 驱动IC:ILI9341 自带触摸屏(电阻触摸屏) 16位80并口驱动 16位真彩显示(65536色)

TFT彩屏引脚定义

TFT写数据

TFT读数据

TFT控制框图

DHT11模块简介

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。

 传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。

DHT11电路图

DHT11参数

测量分辨率分别为 8bit(温度)、8bit(湿度)。

DHT11传输数据:串行接口(单线双向)

DHT11采用单总线协议与stm32通信。DHT11只有在接收到开始信号后才会触发温湿度采集。数据采集完毕且无开始信号后,DHT11自动切换到低速模式(复位信号触发DHT11从低速到高速模式)

(1)一次完整的数据传输为40bit,高位先出。

(2)数据格式:
8bit湿度整数数据 + 8bit湿度小数数据 + 8bi温度整数数据 + 8bit温度小数数据 + 8bit校验和

(3)数据传送正确时,校验和数据等于
“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

注:DHT11传输时,时序非常严格,不允许各种意外情况打断。

DHT11时序 (通信过程)

如下图所示,用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据。

从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集。采集数据后转换到低速模式。

如下图所示,总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号。主机发送开始信号结束后,延时等待20-40us后,读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高。

如上图所示,总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。

DHT11数据位格式 

如下图所示每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
     数字0信号表示方法如下图所示:                             数字1信号表示方法如下图所示:

数字0信号与数字1信号的不同之处在于高电平的时间不同,利用这点,我么们可以通过设置电平时间阈值来判断信号的种类。

DHT11驱动程序

DHT11.c

 配置输入输出GPIO:

复位DHT11

复位DHT11就是发送DHT11起始信号,告诉传感器通讯开始。

 

检查DHT11是否正常

检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平,然后发送80us高电平。即证明DHT11工作正常,该函数工作正常返回0,否则返回1,该函数中利用了while循环检测在一定时间内的电平变化,此类用法在后面也会经常用到。

读取一位数据(返回值0/1)

该函数采用两个while循环是等待每个周期的电平变化,先等待低电平到来,后等待高电平到来,延时40us后判断引脚电平,来判断该位数据为1或0。之所以是40微秒是因为传感器数字0的信号持续时间为26-28us,数字1的信号持续时间为70us,选择一个中值来区分两种信号,当然也可以选择其他值,但最好在40us附近,在while循环中选择循环100次也就是100us,是因为防止当单片机由于某些原因迟迟收不到传感器电平信号,造成死机。

读取一个字节(返回值:读到的数据)

循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。

读取DHT11数据(读取成功返回0,失败返回1)

读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

完整版.c文件
#include "dht11.h"
#include "delay.h"#define    DHT11_DelayMs(t)                      Delay_Ms(t)
#define    DHT11_DelayUs(t)                      Delay_Us(t)#define    DHT11_PIN_HIGH                        1
#define    DHT11_PIN_LOW                         0 #if  defined  (STM32F40_41xxx)#include "stm32f4xx.h"
//配置输入输出GPIO://温湿度模块输入函数
//浮空输入+设置IO口速度+选择端口
#define    __DHT11_CONFIG_IO_INPUT(DHT11)           {  GPIO_InitTypeDef GPIO_InitStructure; \ GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_IN; \GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \GPIO_Init(DHT11->port, &GPIO_InitStructure); \}
//温湿度模块输入函数
//推挽输出+设置IO口速度+选择端口
#define    __DHT11_CONFIG_IO_OUTPUT(DHT11)          {  GPIO_InitTypeDef GPIO_InitStructure; \GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_OUT; \GPIO_InitStructure.GPIO_OType    = GPIO_OType_PP; \  GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \GPIO_Init(DHT11->port, &GPIO_InitStructure); \}#define    __DHT11_IO_SET(DHT11, value)             {  if (value == DHT11_PIN_HIGH) \GPIO_SetBits(DHT11->port, DHT11->pin); \else \GPIO_ResetBits(DHT11->port, DHT11->pin); \}#define    DHT11_IO_H(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_HIGH)}
#define    DHT11_IO_L(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_LOW)}
#define    DHT11_IO_IN(DHT11)                       GPIO_ReadInputDataBit(DHT11->port, DHT11->pin)#endif/******************************************************************************* @brief      复位DHT11** @param[in]  dht11  :  dht11结构体指针** @return     0, 表示正常, 其他值表示失败*******************************************************************************/
//根据DHT11时序图
//复位DHT11:发送DHT11起始信号,告诉传感器通讯开始
static int DHT11_Rst(DHT11_t *dht11)     //DHT端口复位,发出起始信号(IO发送)
{__DHT11_CONFIG_IO_OUTPUT(dht11);    //设置引脚为输出模式DHT11_IO_L(dht11);                  //拉低DQDHT11_DelayMs(20);                  //拉低至少18ms  拉低20msDHT11_IO_H(dht11);                  //DQ=1 DHT11_DelayUs(30);                  //主机拉高20~40us__DHT11_CONFIG_IO_INPUT(dht11);     //设置引脚为输入模式//随后主机开始读取return 0;
}//复位DHT11可以理解为开始/******************************************************************************* @brief      等待DHT11的回应** @param[in]  dht11  :  dht11结构体指针** @return     0, 存在, 返回1:未检测到DHT11的存在*******************************************************************************/
//检查DHT11是否正常:检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平,然后发送80us高电平。
static int DHT11_Check(DHT11_t *dht11)  //读取引脚的状态 retry临界值
{int retry = 0; while (DHT11_IO_IN(dht11) && (retry < 100)) {   //DHT11会拉低40-80usretry++;DHT11_DelayUs(1);};if (retry >= 100) {       //超过100ms说明统计没有相应return -2;} else {retry = 0;}while (!DHT11_IO_IN(dht11) && (retry < 100)) {  //DHT11拉低后会再次拉高40-80usretry++;DHT11_DelayUs(1);};if (retry >= 100) {return -3;}return 0;
}/******************************************************************************
以上没有问题就可以传输数据了* @brief      从DHT11读取一个位** @param[in]  dht11  :  dht11结构体指针** @return     0, 1 读取一位数据(返回值0/1)*该函数采用两个while循环是等待每个周期的电平变化,先等待低电平到来,
*等待高电平到来,延时40us后判断引脚电平,来判断该位数据为1或0。******************************************************************************/
static uint8_t DHT11_ReadBit(DHT11_t *dht11)
{int retry = 0;while (DHT11_IO_IN(dht11) && (retry < 100)) {   //等待变为低电平retry++;DHT11_DelayUs(1);}retry = 0;while (!DHT11_IO_IN(dht11) && (retry < 100)) {  //等待变高电平retry++;DHT11_DelayUs(1);}DHT11_DelayUs(40);//等待40usif (DHT11_IO_IN(dht11)) {return 1;} else {return 0;}
}/******************************************************************************* @brief      从DHT11读取一个字节** @param[in]  dht11  :  dht11结构体指针** @return     读到的数据*读取一个字节(返回值:读到的数据)
循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。******************************************************************************/
static uint8_t DHT11_ReadByte(DHT11_t *dht11)
{uint8_t i, dat;dat = 0;for (i = 0; i < 8; i++) {dat <<= 1;dat |= DHT11_ReadBit(dht11);}return dat;
}/******************************************************************************* @brief      从DHT11读取一次数据    把协议全部放进去*                   temp:温度值(范围:0~50°)*                   humi:湿度值(范围:20%~90%)** @param[in]  dht11  :  dht11结构体指针** @return     0, 表示正常, 其他值表示失败*读取DHT11数据(读取成功返回0,失败返回1)
读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据
等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。******************************************************************************/
int DHT11_ReadData(DHT11_t *dht11)
{if (!dht11 || !dht11->init) return -1;  //初始化引脚之后uint8_t buf[5];uint8_t i;DHT11_Rst(dht11);       //复位传感器if (DHT11_Check(dht11) == 0) {for (i = 0; i < 5; i++) {   //读取40位数据   40位 五个字节 连续读五次buf[i] = DHT11_ReadByte(dht11);}if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) {     //校验  相等说明传输数据么有问题dht11->humidity = buf[0];dht11->temperature = buf[2];             //放入温湿度}} else {return -2;}return 0;
}/******************************************************************************* @brief      初始化DHT11的IO口 DQ 同时检测DHT11的存在.*DHT11初始化
在上电后,对IO端口初始化,和检查DHT11状态。* @param[in]  dht11  :  dht11结构体指针** @return     0, 表示正常, 其他值表示失败*******************************************************************************/
int DHT11_Init(DHT11_t *dht11, DHT11_GPIO_Port_t port, uint32_t pin)    //对外提供两个接口 触发温湿度传感器  读取数据
{if(!dht11) return -1;//配置引脚,默认输出
#if defined (STM32F40_41xxx)assert_param(IS_GPIO_ALL_PERIPH(port));if (port  == GPIOA)        { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); }else if (port  == GPIOB)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); }else if (port  == GPIOC)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); }else if (port  == GPIOD)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); }else if (port  == GPIOE)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); }else if (port  == GPIOF)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); }else if (port  == GPIOG)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); }else return -1;__DHT11_CONFIG_IO_OUTPUT(dht11);#endifdht11->port = port;dht11->pin = pin;dht11->temperature = 0.0;dht11->humidity = 0.0;dht11->init = true;DHT11_IO_H(dht11);DHT11_Rst(dht11);return DHT11_Check(dht11);
}

DHT11.h

#ifndef __DHT11_H
#define __DHT11_H #include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>#if defined (STM32F40_41xxx)#include "stm32f4xx.h"typedef    GPIO_TypeDef*  DHT11_GPIO_Port_t;#else#error dht11.h: No processor defined!#endiftypedef struct dht11_t
{DHT11_GPIO_Port_t     port;             //所使用的端口uint32_t              pin;              //所使用的引脚double                temperature;      //温度值double                humidity;         //湿度bool                  init;             //初始化标志
}DHT11_t;int   DHT11_Init(DHT11_t* dht11, DHT11_GPIO_Port_t port, uint32_t pin);         //初始化DHT11  使用哪个引脚
int   DHT11_ReadData(DHT11_t* dht11);                                           //获取DHT11的数据#endif

main.c

#include "main.h"
DHT11_t gDht11;  //定义一个温湿度结构体
char show_buffer[100];
int main(void)
{Debug_Init(115200);Delay_Init();printf("DHT11 start\r\n");LCD_Init();          //初始化屏幕LCD_Clear(RED);      //屏幕显示红色Delay_Ms(1000);LCD_Clear(~RED);Delay_Ms(1000);/* 初始化DHT11 */int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);   //DHT11接到PA6引脚上去printf("ret: %d\r\n", ret);/* 每秒读取一次温湿度传感器,并通过串口发送 */while (1) {DHT11_ReadData(&gDht11);                               //读取温湿度传感器的数值memset(show_buffer, 0, sizeof(show_buffer));sprintf(show_buffer, "T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);printf("%s", show_buffer);                             //显示LCD_ShowString(0, 0, 240, 30, 24, show_buffer);        //通过LCD屏幕显示Delay_Ms(1000);}
}

总结

介绍DHT11温湿度传感器,及STM32版本驱动函数的编写

后续将加入freertos和emwin

这篇关于项目1:STM32+DHT11+FreeRTOS+emwin+LCD的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/971481

相关文章

一文教你如何将maven项目转成web项目

《一文教你如何将maven项目转成web项目》在软件开发过程中,有时我们需要将一个普通的Maven项目转换为Web项目,以便能够部署到Web容器中运行,本文将详细介绍如何通过简单的步骤完成这一转换过程... 目录准备工作步骤一:修改​​pom.XML​​1.1 添加​​packaging​​标签1.2 添加

tomcat多实例部署的项目实践

《tomcat多实例部署的项目实践》Tomcat多实例是指在一台设备上运行多个Tomcat服务,这些Tomcat相互独立,本文主要介绍了tomcat多实例部署的项目实践,具有一定的参考价值,感兴趣的可... 目录1.创建项目目录,测试文China编程件2js.创建实例的安装目录3.准备实例的配置文件4.编辑实例的

springboot集成Deepseek4j的项目实践

《springboot集成Deepseek4j的项目实践》本文主要介绍了springboot集成Deepseek4j的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录Deepseek4j快速开始Maven 依js赖基础配置基础使用示例1. 流式返回示例2. 进阶

SpringBoot项目启动报错"找不到或无法加载主类"的解决方法

《SpringBoot项目启动报错找不到或无法加载主类的解决方法》在使用IntelliJIDEA开发基于SpringBoot框架的Java程序时,可能会出现找不到或无法加载主类com.example.... 目录一、问题描述二、排查过程三、解决方案一、问题描述在使用 IntelliJ IDEA 开发基于

SpringBoot项目使用MDC给日志增加唯一标识的实现步骤

《SpringBoot项目使用MDC给日志增加唯一标识的实现步骤》本文介绍了如何在SpringBoot项目中使用MDC(MappedDiagnosticContext)为日志增加唯一标识,以便于日... 目录【Java】SpringBoot项目使用MDC给日志增加唯一标识,方便日志追踪1.日志效果2.实现步

Ubuntu中Nginx虚拟主机设置的项目实践

《Ubuntu中Nginx虚拟主机设置的项目实践》通过配置虚拟主机,可以在同一台服务器上运行多个独立的网站,本文主要介绍了Ubuntu中Nginx虚拟主机设置的项目实践,具有一定的参考价值,感兴趣的可... 目录简介安装 Nginx创建虚拟主机1. 创建网站目录2. 创建默认索引文件3. 配置 Nginx4

SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法

《SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法》本文主要介绍了SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法,具有一定的参考价值,感兴趣的可以了解一下... 目录方法1:更改IDE配置方法2:在Eclipse中清理项目方法3:使用Maven命令行在开发Sprin

Nginx实现高并发的项目实践

《Nginx实现高并发的项目实践》本文主要介绍了Nginx实现高并发的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用最新稳定版本的Nginx合理配置工作进程(workers)配置工作进程连接数(worker_co

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排