项目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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/833621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于零一万物多模态大模型通过外接数据方案优化图像文字抽取系统

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径&#xff1a;AI代理工作流大模型应用开发实用开源项目汇总大模…

vue3+ts+vant选择器选中文字效果

所需要的样式: 选中某个选项后文字有放大和改变颜色的效果 主要就是在van-picker上加class, 给对应的style样式即可 <van-pickerclass"custom-picker":title"pickerData.titleText"v-if"pickerData.ispicker"show-toolbar:columns"col…

今日刷三题(day11):不同路径的数目(一)+短距离最小路径和+把数字翻译成字符串

题目一&#xff1a;不同路径的数目&#xff08;一&#xff09; 题目描述&#xff1a; 一个机器人在mn大小的地图的左上角&#xff08;起点&#xff09;。机器人每次可以向下或向右移动。机器人要到达地图的右下角&#xff08;终点&#xff09;。可以有多少种不同的路径从起点…

ICME2024 | 基于半监督对比学习的表现力语音合成

人类的语音极富表现力&#xff0c;不仅包括语调和重读&#xff0c;还包括风格和情感等多种元素。表现力语音合成的目标是要精准捕捉并再现这些元素。先前表现力语音合成方面的研究通常将表现力视为单一维度&#xff0c;如风格或情感。但实际上&#xff0c;风格可以随着文本和场…

Matlab实现分段函数拟合(分段点未知)| 源码分享 | 视频教程 | 三种分段函数拟合方法

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…

Davinci工程CANTP模块讲解

配置CAN的TP模式&#xff0c;涉及BSW\CanTp\CanTp.c和CanTp.h CanTpChannels 他有两组收发&#xff0c;功能诊断和物理诊断。 功能诊断有自己的参数要求 物理诊断的接收要求相对多一些 由于发送只有一个&#xff0c;所以我们把它放在物理诊断接收那组里面。 CanTpGeneral 也…

DDD架构学习

文章目录 领域建模事件风暴四色建模法 DDD名称解析领域子域核心域通用域支撑域限界上下文战术设计实体值对象聚合和聚合根工厂资源库领域服务领域事件 DDD代码的分层名词解析实体值对象聚合根领域服务领域事件 VO&DTO&DO&PO博客 领域建模 领域驱动设计的核心在于领…

厚德提问大佬答3:让AI绘画更有效率

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你解…

iOS xib布局

1.多次启动发现启动图和截屏的图片不一致,设置launch storyboard 不能到顶部 https://blog.csdn.net/u011960171/article/details/104053696/ 2.multipiler是比例&#xff0c;需要控制顺序1.视图&#xff0c;2父视图&#xff0c;选择宽度比例&#xff0c;默认是1 3.Aspect R…

【Java基础】Java异常处理机制超简单的!!

程序在运行时出现的不正常情况 java把程序运行时出现的各种不正常情况提取属性和行为进行描述&#xff0c;从而出现了各种异常类&#xff0c;也就是异常被面向对象了。 异常名称、异常信息、异常发生的位置 Exception in thread "main" java.lang.ArrayIndexOutOf…

海淘美国礼品卡测评:AE/TT/香草卡与国内卡商、亚马逊测评工作室如何变现?(下)

上回分析的四种变现模式&#xff0c;相信大家已经了解清楚。 塔吉特礼品卡&#xff0c;香草礼品卡&#xff0c;AE礼品卡&#xff0c;百思买礼品卡&#xff0c;亚马逊礼品卡&#xff0c;沃尔玛礼品卡&#xff0c;丝芙兰礼品卡&#xff0c;雷蛇礼品卡&#xff0c;谷歌礼品卡&…

处理推送被拒绝的问题

处理推送被拒绝的问题 问题描述 error: failed to push some refs to hint: Updates were rejected because the remote contains work that you do not have locally. This is usually caused by another repository pushing to the same ref. You may want to first integ…

大模型入门(二)—— PEFT

PEFT&#xff08;Parameter-Efficient Fine-Tuning&#xff09;是hugging face开源的一个参数高效微调大模型的工具&#xff0c;里面集成了4中微调大模型的方法&#xff0c;可以通过微调少量参数就达到接近微调全量参数的效果&#xff0c;使得在GPU资源不足的情况下也可以微调大…

《ElementUI 基础知识》el-tree 之“我的电脑”目录结构效果

前言 项目需求&#xff0c;Web 端获取服务器文件夹目录结构。目录数据是调接口获取&#xff0c;本篇略过&#xff0c;直接展现数据&#xff01; 效果 实现 html 代码 8 - 15 行&#xff0c;自定义节点信息&#xff1b;代码 9 - 14 行&#xff0c;判断 icon 显示&#xff1b…

[沫忘录]MySQL储存对象

[沫忘录]MySQL储存对象 视图 视图本质是对原表(基表)显示上的裁剪&#xff0c;可以当作表进行操作&#xff0c;其操作的结果会直接反馈到原表上&#xff0c;即对视图的操作实质上是对原表的操作。 MySQL不仅支持为基表创建视图&#xff0c;同时也支持为视图创建视图。 基本语…

如何备份firewalld的配置信息?

要备份Firewalld的配置信息&#xff0c;您可以通过以下步骤进行&#xff1a; 备份配置文件&#xff1a; Firewalld的配置文件位于/etc/firewalld/目录下。您可以使用cp命令来备份这些文件到其他位置&#xff0c;例如&#xff1a; cp -r /etc/firewalld/zones /path/to/backup…

Bumblebee X系列用于高精度机器人应用的新型立体视觉产品

Bumblebee X是最新的GigE驱动立体成像解决方案&#xff0c;为机器人引导和拾取应用带来高精度和低延迟。 近日&#xff0c;51camera的合作伙伴Teledyne FLIR IIS推出一款用于高精度机器人应用的新型立体视觉产品Bumblebee X系列。 Bumblebee X产品图 BumblebeeX系列&#xff…

在JavaScript中获取当前页面路径的方法

在Web开发中&#xff0c;我们经常需要获取当前页面的URL路径&#xff0c;以便进行导航、数据加载或其他与页面相关的操作。JavaScript提供了几种方法来帮助我们实现这一功能。在本文中&#xff0c;我们将探讨几种常用的方法。 方法一&#xff1a;使用 window.location 对象 wi…

百度云内容审核快速配置 (java)

为什么要选择百度云 &#xff1f; 因为他免费用一年 首先要先开通百度云内容安全服务 按照操作指引走完整套 ContentCensor Java SDK目录结构** com.baidu.aip├── auth //签名相关类├── http //Http通…

IDEA 好用的插件

图标插件&#xff1a;Atom Material Icons 此插件的作用就是更好的显示各种文件的类别&#xff0c;使之一目了然 汉化包 Chinese ​(Simplified)​ Language Pack / 中文语言包 作用就是 汉化 AI编码助手 GitHub Copilot AI编码助手&#xff1a;提示代码很好用 缺点&#xff1a…