基于STM32的DHT11功能实现(操作时序)

news2024/11/16 17:54:00

1.引脚定义

Pin名称注释
1VDD供电 3-5.5V
2GND接地,电源负极
3DATA串行数据,单总线
4NC空脚,请悬空

 2.数据格式

  • DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。
  • 一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。
  • 数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。
  • 由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。

3.操作时序

通过研究时序来实现对DHT11读取数据和通信的功能。下面将时序拆解,有助于更好地理解和封装功能。

3.1 配置DHT11为输入模式(即32单片机配置为输出)

void DHT11_Init_Out(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
}

这里通信数据线选择为PA5

3.2 配置DHT11为输出模式(即32单片机配置为输入)

void DHT11_Init_In(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
}

3.3 实现通信开始时序

DHT11时序图

 分析该时序可知,总线空闲状态为高电平,通信开始信号为主机将总线拉低至少18ms,之后再拉高20-40us;之后的总线控制权交由DHT11控制。功能实现代码如下:

void DHT11_Start(void)
{
	DHT11_Init_Out();//主机输出模式
	DHT11_BitValue(0);//主机拉低总线
	Delay_ms(20);//延时20ms
	DHT11_BitValue(1);//主机再次拉高总线
	Delay_us(22);//延时20ms
	DHT11_Init_In();//开始时序结束,总线交于DHT11控制
}

为了方便更改电位,封装函数

void DHT11_BitValue(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_5, (BitAction)BitValue);//强转为BitAction类型
}

3.4 读取一个字节

时序图

首先关于数字0和数字1信号的时序区分

数字0信号

数字1信号

由时序图可以得知,数字1信号时序高电平持续时间为70us,远大于数字0信号的26us-38us,因此我们可以采用在30us时读取该电平信号,若为高电平,则为数字信号1。另外其余的7个bit操作完全相同,所以我们可以用for把他们套起来。

功能实现代码如下:

uint8_t DHT11_ReadByte(void)
{
	uint8_t data = 0x00;//给数据付一个初始值
	uint8_t i = 0;
	for(i=0;i<8;i++)//循环8次,接受一个字节
	{
		while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==RESET);//等待电平跳变为高电平
		Delay_us(40);
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==SET)//此时检测电平高低,若为高电平
	    {
			data |= (0x80>>i);//将该位置一
			while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)==SET);//等待高电平结束,进入下一位
	    }	
	}
	return data;//返回一个字节的数据
}

3.5 接收完整数据

此时我们实现了通信开始,传输一个字节的数据,这样的字节我们一共有5个,每个的操作与上一步相同,所以我们依然可以用for循环五次,来接收数据

 注意在接收数据之前,我们要等待DHT响应 ,所以要判断是否响应

为了方便编写和阅读代码我们用宏定义替换GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)

#define  dht11_get  GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)

void DHT11_Data(uint8_t *temp,uint8_t *shidu)
{
	int8_t i = 0;
	int8_t arr[5];
	
	DHT11_Start();//通信开始时序
	DHT11_BitValue(1);//拉高电平,方便检测DHT进入低电平
	//检测响应信号
	if(dht11_get == 0)
	{
		
	    while(dht11_get == 0);//低电平等待跳变高电平
		while(dht11_get == 1);//高电平等待跳变低电平
		
		for(i=0;i<5;i++)
		{
			arr[i] = DHT11_ReadByte();
		}
		DHT11_BitValue(0);//通信结束,DHT11拉低总线
		Delay_us(50);
        DHT11_Init_Out();//总线交给主机
		DHT11_BitValue(1);//拉高总线进入空闲
		if(arr[0]+arr[1]+arr[2]+arr[3]==arr[4])//校验位检测
		{
			*temp = arr[2];
			*shidu = arr[0];
			
		}
	}
}

3.6 DHT11.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h" 


#define  dht11_get  GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5)

//电平高低翻转
void DHT11_BitValue(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_5, (BitAction)BitValue);
}



//stm32对dht11输出
void DHT11_Init_Out(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
}
//DHT11对stm32输入
void DHT11_Init_In(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
}

//通信开始时序
void DHT11_Start(void)
{
	DHT11_Init_Out();
	//DHT11_BitValue(1);
	DHT11_BitValue(0);
	Delay_ms(20);
	DHT11_BitValue(1);
	Delay_us(22);
	DHT11_Init_In();
}
//读取一个字节
uint8_t DHT11_ReadByte(void)
{
	uint8_t data = 0x00;
	int i = 0;
	
	for(i=0;i<8;i++)
	{
		while(dht11_get==RESET);
		Delay_us(40);
		if(dht11_get  == SET)
	    {
			data |= (0x80>>i);
			while(dht11_get==SET);
	    }	
	}
	return data;
}



//接受完整数据
void DHT11_Data(uint8_t *temp,uint8_t *shidu)
{
	int8_t i = 0;
	int8_t arr[5];
	
	DHT11_Start();
	DHT11_BitValue(1);
	//检测响应信号
	if(dht11_get==0)
	{
		
	    while(dht11_get==0);
		while(dht11_get==1);
		
		for(i=0;i<5;i++)
		{
			arr[i] = DHT11_ReadByte();
		}
		DHT11_BitValue(0);
		Delay_us(50);
		DHT11_Init_Out();
		DHT11_BitValue(1);
		if(arr[0]+arr[1]+arr[2]+arr[3]==arr[4])
		{
			*temp = arr[2];
			*shidu = arr[0];

		}
	}
}

3.7 DHT11.h

#ifndef __DHT11_H
#define __DHT11_H

uint8_t DHT11_Data(uint8_t *temp,uint8_t *shidu);

#endif

 3.8 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "DHT11.h"

uint8_t temp,shidu;

int main(void)
{

	
	
	OLED_Init();
	
	Serial_Init();

	while (1)
	{
	
	    Delay_s(1);
		DHT11_Data(&temp,&shidu);
	    Serial_Printf("\r\ntemp=%d", temp);
	    Serial_Printf("\r\nshidu=%d", shidu);
	    Serial_Printf("\r\n");		
	
	}
}

3.9 serial.c(串口输出)

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)//发送一个字节
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)//发送多字节
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)//发送字符串
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

3.10 serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_Printf(char *format, ...);

#endif

4.功能实现

oled显示屏实现

5.不足

 该程序目前还有更进一步的可能,比如在不同的不同时序单元内加入状态返回值,方便判断和调试,欢迎各位与我交流。

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

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

相关文章

新品:新一代全双工音频对讲模块SA618F22-C1

SA618F22-C1是我司一款升级版的无线数字和音频二合一全双工传输模块&#xff0c;支持8路并发高音质通话。用户不仅可以通过串口实现数据的无线传输&#xff0c;还可以通过I2S数字音频或模拟音频接口来传输语音信号。该模块内置高速微控制器、回声消除电路、ESD静电防护、高性能…

全国自闭症学校全寄宿制:为特殊儿童提供专业教育护理

在全国范围内&#xff0c;随着对自闭症儿童教育需求的日益增长&#xff0c;全寄宿制自闭症学校逐渐成为了一种重要的教育模式。这些学校以其专业的教育团队、全面的生活护理以及个性化的教学方案&#xff0c;为自闭症儿童提供了一个安全、稳定、充满爱的成长环境。在广州&#…

走进灯塔工厂,腾讯云携手业界专家共筑AI智造未来

现在&#xff0c;我国工业正处于从数字化向智能化转型的关键阶段&#xff0c;而人工智能、云计算和大数据等前沿技术正成为推动这进程的核心力量。以大模型为例&#xff0c;大模型通过高效处理和分析海量数据&#xff0c;帮助企业挖掘出有价值的规律和趋势&#xff0c;有效拓展…

使用双向链表和哈希表实现LRU缓存

在日常开发中&#xff0c;缓存 是一个非常常见且重要的技术手段&#xff0c;能够显著提升系统性能。为了保证缓存的有效性&#xff0c;需要实现一种机制&#xff0c;在缓存空间不足时&#xff0c;能够自动淘汰最久未被使用的数据。这种机制就是**LRU&#xff08;Least Recently…

CSS文本格式化

通过 CSS 中的文本属性您可以像操作 Word 文档那样定义网页中文本的字符间距、对齐方式、缩进等等&#xff0c;CSS 中常用的文本属性如下所示&#xff1a; text-align&#xff1a;设置文本的水平对齐方式&#xff1b;text-decoration&#xff1a;设置文本的装饰&#xff1b;te…

面试题-部分

目录 1. 从输入url到渲染页面&#xff0c;中间经历了什么&#xff1f; 2. vue中的v-if和v-show有什么区别 3. 什么是Css中的回流&#xff08;重排&#xff09;与重绘 4. 介绍一下let、const、var的区别 5. 箭头函数和普通函数有什么区别 6. Css中常用的水平垂直居中解决方…

传输大咖49 | 科学研究院跨网文件交换高效、安全解决方案

在科学研究领域&#xff0c;数据的价值堪比黄金。科学研究所的日常运作依赖于大量的数据交换&#xff0c;高效的文件交换系统离不开内部合作和与外部合作伙伴的交流。然而&#xff0c;随着数据量的激增和网络环境的复杂性&#xff0c;传统的文件交换方法很难满足需求。本文将讨…

RK3568 android11 适配鼎桥MT5710-CN 5G模块

一,概述 鼎桥MT571X设备和Android系统主要通过USB接口进行数据通信,Android系统上的Linux内核需要根据鼎桥模块设备上报的USB设备接口加载USB驱动,USB驱动正确加载后,鼎桥模块才能正常工作。 Android系统中支持鼎桥模块设备相关的Linux内核驱动架构,如下图所示: 在Lin…

js删除emoji表情问题

emoji标签占位两个 &#xff0c;直接删除后一位会出现乱码符&#xff1b; 判断是否是emoji function isEmoji(char) {let code char.charCodeAt(0);return code>55296&&code<57343 } // 使用方法&#xff0c;传入单字符 console.log(isEmoji(1)); // false con…

Kubernetes 配置管理

一、什么是 ConfigMap&#xff1f; 在传统架构中&#xff0c;配置文件往往被保存在宿主机上&#xff0c;程序启动是可以指定某个配置文件&#xff0c;但是使用容器部署时&#xff0c;容器所在的节点并不固定&#xff0c;所以不能使用这种方式&#xff0c;此处在构建镜像时&…

【Redis】主从复制(下)--主从复制原理和流程

文章目录 主从复制原理主从节点建立复制流程图数据同步 psyncpsync的语法格式 psync运行流程全量复制全量复制的流程全量复制的缺陷有磁盘复制 vs 无磁盘复制 部分复制部分复制的流程复制积压缓冲区 实时复制 主从复制原理 主从节点建立复制流程图 保存主节点的信息从节点(sla…

感悟:糟糠之妻不下堂和现在女性觉醒的关系

古人说“糟糠之妻不下堂”真是害惨了中国女性&#xff0c;古代之所以有这一说法&#xff0c;大概是因为男子可以三妻四妾&#xff0c;妻子永远是正妻&#xff0c;也不需要讲究什么从一而终&#xff0c;更不会讲什么男德&#xff0c;只会要求女性学习女德、女训之类&#xff0c;…

性能测试:性能测试报告

性能测试报告是性能测试的产出物之一&#xff0c;它是对系统性能测试结果和数据的总结和分析&#xff0c;记录了系统在不同负载和场景下的性能表现和性能问题。性能测试报告提供了有关系统性能的详细信息&#xff0c;供项目团队、开发人员和其他相关利益相关者参考。 性能测试…

原生app云打包,更换图标,和名称。PDA的安装正式包

原生app云打包 复制下载即可&#xff0c;是正式版

Android下MVP和MVVM模式的实践

转载注明出处&#xff1a;https://blog.csdn.net/skysukai 1、前言 MVP和MVVM诞生已经好些年头了&#xff0c;记得刚毕业才参加工作的时候&#xff0c;第一次见到了有上万行的Activity&#xff0c;这种巨无霸的Activity维护起来简直就是噩梦。这时候&#xff0c;就需要进行代…

商标价值如何评估与增值?

商标是企业的标志&#xff0c;代表着企业的产品或服务质量、信誉和形象。一个具有高知名度和美誉度的商标&#xff0c;能够为企业带来巨大的商业价值。它不仅可以帮助企业在市场中脱颖而出&#xff0c;吸引消费者的关注和购买&#xff0c;还可以作为企业的重要资产进行融资、并…

无人便利店无人超市云值守收银系统源码

随着人力成本越来越高&#xff0c;很多门店越来想做无人值守模式&#xff0c;尤其是晚上休息时间等想让云值守客服来看店。自然要求收银系统需要可以在【有收银员值守】和【无收银员值守】两种模式灵活切换。 1. 有收银员值守模式 白天有收银员在店&#xff0c;收银员可以协助…

Tomcat服务与运用

案例准备 1.规划节点 IP 主机名 节点 192.168.20.20 tomcat Tomcat 2.基础准备 使用VMWare Workstation软件安装CentOS 7.2操作系统&#xff0c;镜像使用提供的CentOS-7-x86_64-DVD-1804.iso&#xff0c;最小化安装CentOS 7.2系统 案例实施 1.基础环境配置 1.1修改…

微信小程序的 button 标签的边框如何去除?

目录 问题描述&#xff1a; 问题原因&#xff1a; 解决办法&#xff1a; 方案一 方案二 问题描述&#xff1a; 实际开发中会发现这个 button 自带有样式&#xff0c;当背景颜色设置为白色的时候还有一个黑色的边框&#xff0c;刚开始那个边框怎么都去不掉 无法去除的边框…

IRR 之 24期免息等于免费?钱买保险还是余额宝?

一、IRR 是什么&#xff1f; 英文全称是 Internal Rate of Return 具体公式可以参考公众号 随园经济56&#xff5c;内部收益率 简单理解可以为&#xff1a; IRR是投资项目在整个生命周期中所能获得的平均年化收益率 更简单理解为&#xff0c;余额宝展现给你的年化收益 二、…