从零开始手搓一个STM32与机智云的小项目——GPIO模拟时序控制外设2

news2025/1/10 3:25:50

文章目录

  • 前言
  • 模块简介
    • 硬件介绍
    • 硬件连接
    • 通信时序
    • DHT11的数据帧格式
    • 信号时序
      • 1. 起始信号
      • 2.应答信号(响应信号)
      • 3.接收数据0与1
      • 4.获取数据
      • 5结束信号
    • 输入输出切换
    • 实际效果
  • 总结

前言

在上一篇中介绍了,使用GPIO模拟WS2812B的控制时序来实现对RGB灯的控制,本文继续使用GPIO模拟的方式来MCU实现与DHT11温湿度传感器的通信,获取温湿度信息。

模块简介

DHT11是采集温湿度的常用模块之一,其内部集成了检测湿度和温度的传感器以及一个8位的单片机用来处理温湿度的信息,正是得益于这个8位的内置MCU,让用户省去了温湿度的数据处理步骤,直接根据时序图获取温湿度即可。
在这里插入图片描述

硬件介绍

常见的模块有两种,一类是上图所示的四个管脚没有转接板的,这种模块在使用的时候,原理图绘制过程中一定要给DATA脚加上上拉电阻,否则大概率是无法正常通信获取数据的。
在这里插入图片描述
还有一种模块是3个管脚的,这种模块一般都是在转接板上加了上拉电阻的。为了降低电源对采集数据的影响,尽量选用LDO供电,下图中的C1也是起到一个电源滤波的作用。
在这里插入图片描述
这里笔者选择的是带有转接板的模块。

硬件连接

大致了解模块了之后,就需要对照原理图找到实际通信使用的GPIO,通过原理图,使用的GPIO是PA15。
在这里插入图片描述

通信时序

根据上一篇模拟的步骤,接下来就需要查看其通信时序,使用GPIO模拟时序来实现功能。
DHT11采用的是单总线(1-wire)的通信协议,使用一个数据线完成通信双方的数据交换,既然是单根数据线,那么它铁定是一个半双工的通信方式,同一时间要么MCU发送数据,也么DHT11发送数据。而且,DHT11并不能主动地给MCU传输数据,需要MCU给DHT11发送一个起始信号后,DHT11才会应答,进而进行数据传输。
在这里插入图片描述
在这里插入图片描述

根据这段描述,可以大致总结出DHT11的通信时序:
1.主机(MCU)给DHT11(从机)发送一个起始信号;
2.DHT11(从机)接收到起始信号后,给主机返回一个应答以及准备数据传输的信号;
3.主机(MCU)接收到应答信号和数据传输的信号后,开始接收40bit的数据;
4.DHT11数据发送完成,产生结束信号,MCU停止接收数据。
在这里插入图片描述

DHT11的数据帧格式

DHT11一次会传输40bit的数据,其中每一位的定义如下:
第一个八位数据是湿度的整数位,第二个八位数据是湿度的小数位,第三个八位数据是温度的整数位,第四个八位是温度的小数位,最后一个八位数据是校验位,校验位=前四个八位数据之和,然后取低八位的值。
在这里插入图片描述

手册例子如下:
在这里插入图片描述
实际使用逻辑分析仪抓取的波形如下:
在这里插入图片描述

信号时序

弄清了大致的通信流程后,程序的整体框架有了,但是具体的信号表示方式还需要进一步查看手册。

1. 起始信号

首先来看起始信号,起始信号是MCU给DHT11发送的,此时MCU的GPIO作为输出脚,输出指定时长的高低电平。
起始信号对应的波形是下图红框里面的内容。
在这里插入图片描述
放大后如下图所示:
在这里插入图片描述
通过这个描述以及波形,可以看出起始信号首先需要MCU将数据脚拉低至少18ms,而后又拉高释放总线。
这里的拉高释放总线,怎么理解呢,前面提到了在DATA脚上始终有一个上拉电阻,当MCU与DHT11都没有输出控制DATA脚时,DATA脚的电压始终保持在高电平,也就是说DATA线是拉高空闲,释放状态。这里有个记住,只要MCU和DHT11不对DATA做控制时,DATA的默认状态就是高电平即可。
由此大致写出起始信号的函数:

/*********************************
函数名:DHT11_Start
函数功能:DHT11复位(起始信号开始转换)
形参:void
返回值:void
备注:
主机拉低至少18MS,再拉高20-40us,等待应答
**********************************/
void DHT11_Start(void)
{
	//DATA脚拉低18ms以上30ms以下
	DHT11_OUT_L;
	Systick_Delay_ms(20);
	//再拉高20-40us
	DHT11_OUT_H;
	Systick_Delay_us(30);
}

2.应答信号(响应信号)

DHT11的响应信号如下图红框所示,刚刚主机发送起始信号后,对DATA线进行了拉高释放,也就是说此时DATA线默认是高电平状态,当DHT11接收到起始信号后,会将DATA数据线进行一个拉低,这个拉低就叫做响应信号,响应后为了数据接收的稳定,DHT11也会再次将数据线拉高,释放数据线,告诉主机要准备开始传输数据。
在这里插入图片描述
这部分时序图放大后如下图所示:
DHT11会将DATA脚拉低约80us左右的时间,而后DHT11又会将数据脚拉高告诉主控要开始接收数据。
这里需要注意一点,此时的数据线是由DHT11来控制的,MCU要获取从DHT11上传输来的信号,需要将之前的输出模式切换为输入模式。具体的切换方式有两种方案,这个放到后面细说。知道此时是MCU读取DATA状态就可以。
在这里插入图片描述
这里先写个代码来获取应答信号,而数据准备发送的信号放到读取数据位的时候一起实现。

/*********************************
函数名:DHT11_Check
函数功能:DHT11应答检测
形参:void
返回值:u8 
备注:返回1:未检测到DHT11的存在
返回0:检测到DHT11
**********************************/
u8 DHT11_Check(void)
{
	u8 retry=0;	 	 
	//等待起始信号的高电平时间结束
   while (DHT11_IN && retry<100)
	{
		retry++;
		Systick_Delay_us(1);
	};	 
	if(retry>=100)return 1;//如果DATA脚空闲时间超过100us还没有被DHT11拉低,说明应答失败
	else retry=0;
	//检测DHT11应答拉低的时间是否正常
  while (!DHT11_IN && retry<100)//DHT11会拉低40~80us
	{
		retry++;
		Systick_Delay_us(1);
	};
	if(retry>=100)return 1;	//如果低电平时间超过了100us说明应答失败    
	return 0;//否则就应答成功
}	

3.接收数据0与1

在获取到应答信号后,需要先处理一下预备发送数据的那一段时序;然后就是根据DATA的状态来判断数据位是0还是1了。与上一篇的WS2812B通信一样,DHT11的“1”和“0”也是由特定时长的高低电平组成的。
具体的逻辑“0”与逻辑“1”的表示如下图所示,都是先有一段54us的电平,然后根据高电平的时长来区分逻辑“0”与逻辑“1”。
逻辑“0”的高电平时间持续23-27us,逻辑“1”的高电平时间持续为68=74us。
在这里插入图片描述
那么怎么检测逻辑“0”和逻辑“1”呢,一个简单的方案,既然低电平的时间是一样的,那么读取的时候先等待低电平结束,然后延时超过40us,再去读取DATA的状态,如果此时还是高,则说明是逻辑“1”,如果变成了低则说明是逻辑“0”。
代码如下:

/*********************************
函数名:DHT11_Read_Bit
函数功能:从DHT11获取1位数据
形参:void
返回值:u8  1/0
备注:
数据线低50us表示开始传输数据,数据变为高电平时开始记录高电平持续时间
如果高电平时间持续26-28us表示数据位为0
如果高电平时间持续70us表示数据位为1
**********************************/
u8  DHT11_Read_Bit(void)
{
	u8 retry=0;
	//等待准备接收数据的信号结束
	while(DHT11_IN && retry<100)//等待变为低电平(应答后会拉低拉高,所以在这里要等待80)
	{
		retry++;
		Systick_Delay_us(1);
	}
	retry=0;
	//0和1的低电平时间段不作判定,直接等待
	while(!DHT11_IN &&retry<100)//等待变高电平
	{
		retry++;
		Systick_Delay_us(1);
	}
	Systick_Delay_us(40);//等待40us
	if(DHT11_IN)return 1;//“1”的高电平时间持续68us以上,延时40us后还是高电平
	else return 0;		//“0”的高电平时间持续26-28us,经过上面的延时已经变回低电平了。
}

4.获取数据

然后就是调用上面的bit接收函数来实现40bit的数据接收,高位在前,进而封装出对应的字节接收函数,然后接收5个字节的数据后,根据前面的数据帧格式,判断校验位,读取所需值即可。

/*********************************
函数名:DHT11_Read_Byte
函数功能:从DHT11获取1字节数据
形参:void
返回值:u8 
备注:

**********************************/
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_Read_Data
函数功能:从DHT11获取数据
形参:void
返回值:u8 0,正常;1,读取失败
备注:
8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据
+8bit校验和
**********************************/
u8  DHT11_Read_Data(u8 *temp,u8 *humi)
{
	u8 buf[5];
	u8 i;
	DHT11_Start();
	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;	    
}

5结束信号

至于结束信号,是由DHT11产生的,一般不用做处理,MCU只用管接收完40bit的数据就行了。
在这里插入图片描述

输入输出切换

整个通信流程的代码在就大致是上面的内容了,但是还存在一个问题,GPIOA15在整个通信过程中既要作为输出提供起始信号,又要作为输入获取DHT11的数据,需要怎么实际操作呢,前面提到了有两种方案,其中之一也是笔者比较喜欢的,直接将GPIOA15初始化为开漏模式,此时负责输出高电平的PMOS被屏蔽,当我们对ODR写入1时,GPIOA15不再输出高点电平,也就是说MCU不再占用DATA脚,此时全靠外部上拉电阻与DHT11二者控制DATA线,此时直接调用库函数获取GPIOA15的高低电平就是实际的DATA状态了。

/*********************************
函数名:DHT11_Init
函数功能:DHT11初始化
形参:void
返回值:void
备注:既要输入又要输出
DHT11_DATA-----PA15-------开漏输出//jtag脚需要关闭
**********************************/
u8 DHT11_Init(void)//初始化DHT11
{
	GPIO_InitTypeDef  GPIO_InitStructure;//定义一个结构体的变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//初始化GPIOA端口的时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_OD;//通用开漏输出
	GPIO_Init(GPIOA,&GPIO_InitStructure );
	DHT11_Start();  //复位DHT11
	return DHT11_Check();//等待DHT11的回应
}

还有一种方案是使用推挽输出模式,在获取GPIO状态是做切换,将GPIOA15切换回输入模式。为什么使用推挽模式时要切换模式呢,这是因为,在推挽输出模式下,GPIOA15要么在输出0要么在输出1,不可能出现开漏模式那种解除占用的情况,因此只能在整个过程中不断切换GPIO的输入输出模式才可以。
此法的初始化代码如下:

//PA11
#define DHT11_IO_IN()  {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=8<<15;}//切换为输入模式
#define DHT11_IO_OUT() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=3<<15;} //切换为输出模式
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟
 	GPIO_InitStructure.GPIO_Pin = DT;				 //PA15端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);				 //初始化IO口
 	GPIO_SetBits(GPIOA,DT);						 //PG11 输出高
			    
	DHT11_Rst();  //复位DHT11
	return DHT11_Check();//等待DHT11的回应
} 
//需要注意,使用此法时,上面的通讯流程代码都要稍作修改,代码太多了,这里就不贴出来了。

实际效果

在主函数调用初始化后可以正常获取到温湿度。
在这里插入图片描述
需要注意一点,市面上的DHT11由于厂家不同,其采样速率也不一样,有的模块可能每秒可以采集100次,但是有的模块只能每秒采集2次,单次获取的时间间隔小于这个时间就会出现乱码的情况。

总结

本文对常用的DHT11温湿度采集模块做了一个时序的模拟。文中如有不足欢迎大家批评指正。

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

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

相关文章

【开源与项目实战:开源实战】84 | 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则

在 Java 世界里&#xff0c;Spring 框架已经几乎成为项目开发的必备框架。作为如此优秀和受欢迎的开源项目&#xff0c;它是我们源码阅读的首选材料之一&#xff0c;不管是设计思想&#xff0c;还是代码实现&#xff0c;都有很多值得我们学习的地方。接下来&#xff0c;我们就详…

Nginx服务的主配置文件 nginx.conf

目录 前言 一、Nginx.con位置 二、Nginx.com相关内容 三、Nginx.conf中配置块和指令 1、I/O时间配置 2、HTTP 配置 日志格式设定 总结 前言 Nginx 的主配置文件是 nginx.conf&#xff0c;它通常位于 Nginx 的安装目录下的 conf 文件夹中。主配置文件 nginx.conf 是 Ngin…

Rust语言从入门到入坑——(6)Rust组织管理

文章目录 0 引入1、组织概念1. 1、箱1. 2、包1. 3、模块 2、组织中权限2.1 权限2.2 模块引用2.2 Use 关键词 3、总结 0 引入 任何一门编程语言如果不能组织代码都是难以深入的&#xff0c;几乎没有一个软件产品是由一个源文件编译而成的。本教程到目前为止所有的程序都是在一个…

java——内部类和异常处理

文章目录 内部类成员内部类局部内部类匿名内部类静态内部类 异常处理异常捕获与处理多重异常捕获和处理抛出异常 内部类 Java内部类&#xff08;Inner Class&#xff09;是嵌套在其他类中的类&#xff0c;它可以访问外部类的成员变量和方法&#xff0c;同时也可以被外部类访问…

模拟电路系列文章-ADC驱动电路(下)

目录 概要 整体架构流程 技术名词解释 技术细节 1.低阻输出&#xff0c;以减小误差 2.抗混叠 3.电源级保护 小结 概要 提示&#xff1a;这里可以添加技术概要 一个模拟电压信号&#xff0c;在进入ADC 的输入端之前&#xff0c;一般都需要增加一级驱动电路(Driver]。但是&#…

C语言基础:指针的使用

本文结合工作经验&#xff0c;研究C语言中指针的用法。 文章目录 1 指针的概念2 用法与使用场景2.1 函数的指针参数2.1.1 基本概念2.1.2 使用场景1-函数返回多个值2.1.3 使用场景2-减少函数参数 2.2 void*指针2.2.1 基本概念2.2.2 使用场景 2.3 空指针2.4 const指针2.4.1 基本…

三分钟学习一个python小知识5-----------我的对python中pandas的理解, 我列举了关于pandas常用的4个例子来深入理解pandas

这里写目录标题 1、Pandas是什么2、Pandas的常用功能&#xff1a;2.1. 读取和写入数据2.2. 数据清洗和转换2.3. 数据分析和计算2.4. 数据可视化总结 1、Pandas是什么 Pandas是Python中一个非常流行的数据处理和分析库&#xff0c;可以使用它对数据进行读取、清洗、转换、分析和…

【动态规划算法练习】day4

文章目录 一、213. 打家劫舍 II1.题目简介2.解题思路3.代码4.运行结果 二、740. 删除并获得点数1.题目简介2.解题思路3.代码4.运行结果 三、剑指 Offer II 091. 粉刷房子1.题目简介2.解题思路3.代码4.运行结果 总结 一、213. 打家劫舍 II 1.题目简介 213. 打家劫舍 II 你是一…

数字IC工程师的护城河是什么?

每个人都希望能够增加⾃⼰的核心竞争⼒&#xff0c;然后延展职业⽣涯。 可能IC研发工程师基本上都会有个40岁危机&#xff1f; 时代背景是最⼤的变数&#xff0c;它改变了⼈才供需和技术⾛向&#xff0c;⽐如做处理器 core曾经是屠⻰术&#xff0c;⽽现在是⻩⾦时代 处理器 cor…

堆和优先队列

文章目录 堆维护堆的性质建堆堆排序算法 优先队列详解cpp标准库 priority_queue 参考文献 堆 虽然“堆”这个词源自堆排序&#xff0c;但是目前它已经被引申为“垃圾存储机制”&#xff0c;例如在Java和Lisp语言中所定义的。强调一下&#xff0c;我们使用的堆不是垃圾收集存储…

设计模式之访问者模式笔记

设计模式之访问者模式笔记 说明Iterator(访问者)目录访问者模式示例类图抽象访问者角色类抽象元素角色类宠物猫类宠物狗类自己类其他人类家类测试类 说明 记录下学习设计模式-访问者模式的写法。JDK使用版本为1.8版本。 Iterator(访问者) 意图:表示一个作用于某对象结构中的…

平面设计师实习报告范文十篇

平面设计师实习报告范文1 经过了四年的系统性理论学习&#xff0c;使我对平面设计有了基本掌握&#xff0c;对于平面设计这个专业也有了一个系统的了解和掌握。我们即将离开大学校园&#xff0c;作为平面设计专业的毕业生&#xff0c;心中想得更多的是如何去做好一个平面设计师…

ETCD集群运维

ETCD集群运维 ETCD扩容节点ETCD参数调优时间参数快照磁盘网络 监控维护压缩历史版本消除碎片化存储配额快照备份 灾难恢复快照恢复 etcd gateway作用什么时候不应该使用网关启动方式 gRPC代理可扩展的watch API租约请求缓存启动gRPC代理 ETCD扩容节点 节点1&#xff1a;10.1.1…

管理类联考——逻辑——知识篇——论证推理——一、削弱——haimian

削弱 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量223262132 主要问法 以下哪项如果为真&#xff0c;则能最严重地削弱&#xff08;反驳&#xff09;上述结论?以下哪项如果为真&#xff0c;则最能削弱&#xff08;反驳&#xff09…

文心一言 VS 讯飞星火 VS chatgpt (46)-- 算法导论6.1 4题

四、假设一个最大堆的所有元素都不相同&#xff0c;那么该堆的最小元素应该位于哪里? 文心一言&#xff1a; 最大堆的最小元素位于堆的根结点。由于最大堆的性质&#xff0c;根结点是堆中所有元素的最大值&#xff0c;因此它也是堆中所有元素的最小值。 讯飞星火&#xff1a…

工具及方法 - 如何进行任务管理

如何做好你该做的事&#xff0c;这是你的一生之敌。 没有一步到位的工具和一劳永逸的方法&#xff0c;只有不断变化的需求和自己用起来顺手的感觉。 对于GTD工具&#xff0c;在手机上安装过番茄工作法的工具&#xff0c;在电脑的Excel上也做过任务列表&#xff0c;在免费的项目…

解决 kali换源之后签名无效

报错问题&#xff1a; apt-get update 报错 更新扩展知识&#xff1a;kali更新源 终端输入 vi /etc/apt/sources.list #中科大 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main …

计算机视觉中,有哪些基于控制点对的图像变换?

这里探讨的所有图像变换&#xff08;二维&#xff09;都是基于控制点对的&#xff0c;它们的主要区别在于&#xff1a; 1、它们是如何通过两张图像的控制点对产生变换场&#xff08;变换矩阵或者变换公式中的参数&#xff09;的 2、控制点之间的对应关系严格程度 这里说的变换…

远心镜头案例锦集

远心镜头&#xff08;Telecentric lens&#xff09; 定义&#xff1a;远心镜头是一类将其入瞳或出瞳放置于无穷远的光学系统。是为纠正传统镜头视差而设计&#xff0c;它可以在一定的物距范围内&#xff0c;使得到的图像放大倍率不会变化&#xff0c;即这种镜头拍出来的图像没…

人工智能(4):模型评估

模型评估是模型开发过程不可或缺的一部分。它有助于发现表达数据的最佳模型和所选模型将来工作的性能如何。 按照数据集的目标值不同&#xff0c;可以把模型评估分为分类模型评估和回归模型评估。 1 分类模型评估 准确率 预测正确的数占样本总数的比例。 其他评价指标…