STM32第九课:DHT11温湿度传感器

news2024/11/25 7:01:57

文章目录

  • 需求
  • 一、DHT11温湿度传感器
  • 二、模块配置流程
    • 1.配置时钟和IO
    • 2.读取数据
    • 3.数据处理
  • 三、导入语音模块
  • 四、关键代码
  • 总结


需求

1.完成DHT11温湿度检测模块的配置。
2.处理DHT11获取的数据,在串口打印处理后的实时数据。
2.通过Su-03t语音识别模块实现实时温湿度的问答。

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


一、DHT11温湿度传感器

  DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有枀高的可靠性与卓越的长期稳定性。传感器包括一个电容式感湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。

引脚说明:
1、VDD 供电 3.3~5.5V DC
2、DATA 串行数据,单总线
3、NC 空脚
4、GND 接地,电源负枀

二、模块配置流程

1.配置时钟和IO

在这里插入图片描述
在这里插入图片描述
由原理图可知该模块连接的是stm32的PG11引脚所以此时我们只需要配置PG11的引脚即可。
在这里插入图片描述
根据该模块手册中的数据时序图可知:该引脚需要既能输入也能输出,所以为了能够同时满足输入和输出,我们将PG11引脚配置成开漏模式。

在这里插入图片描述

代码如下(示例):

void DHT11_Config()
{
	//配置为开漏模式
	//开时钟
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
	//配置io
	 GPIO_InitTypeDef GPIO_InitStruct = {0};
	 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
	 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_Init(GPIOG,&GPIO_InitStruct);
	 DHT11HIGH();
}

配置完成后拉高电平是为了满足主机“拉高等待”状态。
为了方便后续操作,先重定义一下拉高电平,拉低电平和读操作。

#define DHT11HIGH() GPIO_SetBits(GPIOG,GPIO_Pin_11)

#define DHT11LOW()  GPIO_ResetBits(GPIOG,GPIO_Pin_11)

#define DHT11read() GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_11)

2.读取数据

根据官方提供的外设读取步骤一步步进行操作即可

由图可知要先拉高电平
在这里插入图片描述
再拉低电平并持续18ms
在这里插入图片描述
再输出高电平,然后用while检测83us的低电平应答信号。
收到应答信号后,再用while检测87us的高电平外设准备接受信号。
在这里插入图片描述
在这里插入图片描述
接收到高电平外设准备接受信号后就可以接收40位的数据了。
直接定义一个数组进行for循环接收。
由于该模块发送的数据为高低电平信号,并且是根据高电平持续时间的不同来判断究竟是高电平还是低电平,此时我们只需要加个延迟,比差值大 ,比74us小即可,此处我用的是45us。

3.数据处理

在这里插入图片描述
由手册可知,为了能够准确的将温湿度转换为10进制
此刻我们先用for循环将40个数据进行8位分组处理。

	for(i=0;i<40;i++)
	{ //0~7  8~15  16~23  24~31 32~39
		data[i/8]+=(arr[i]<<(7-i%8));
	}

分完组后在计算下和校验一下。
最后根据手册说明将16位数据转换为10进制并打印出来。

//根据DHT11时序读取数据
void DHT11_ReadData()
{
	uint16_t i = 0;
	uint16_t timeout = 0;
	uint8_t data[5] = {0};
  uint8_t arr[50] = {0};
	//输出一个最少18ms的低电平,最大30ms
	DHT11HIGH();
	DHT11LOW();
	Delay_nms(18);
	DHT11HIGH();
	//检测有持续83us低电平和87us的高电平
	timeout=0;
	while(DHT11read()==1)//检测83us低电平
	{
		timeout++;
			 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
	}
	timeout=0;
		while(DHT11read()==0)//检测87us的高电平
	{
		timeout++;
		Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
	}
	//读取40位数据
	for(i=0;i<40;i++)
		{
			timeout=0;
			while(DHT11read()==1)//等待us低电平
	   {
				timeout++;
			 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
		}
		 timeout=0;
		  while(DHT11read()==0)//等待能判断的高电平标志
   	 {
		 timeout++;
		 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
		}
		 Delay_nus(45);
	   arr[i]=DHT11read();
	}
			for(i=0;i<40;i++){ //0~7  8~15  16~23  24~31 32~39
		data[i/8]+=(arr[i]<<(7-i%8));
	}
	if(((data[0]+data[1]+data[2]+data[3])&0xff) != data[4]){
		return;
	}

	hum=data[0]+data[1]/10.0;
	tem=data[2]+(data[3]&0x7f)/10.0;
	if((data[3]&0x80) != 0){
		tem = 0-tem;
	}
	printf("读取完毕:湿度:%.1f   温度:%.1f\r\n",hum,tem);
	return;
}

三、导入语音模块

先在su03t.c文件中外部声明一下处理后的温度和湿度。

extern float hum;
extern float tem;

查看语音模块固件烧录时定义的指令,找到湿度温度播报的指令
在这里插入图片描述
可以看到温度指令为06,湿度指令为07。
最后将该数据和指令添加到int Su03tDealData()函数中就完成了。
在这里插入图片描述

四、关键代码

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "kqm.h"
#include "string.h"
#include "su03t.h"
#include "dht11.h"

int main()
{
		NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    Usart1_Config(); 
	  SysTick_Config(72000);
	  Kqm_U4Config();
	  Su03t_U5Config();
	  DHT11_Config();
	    while(1)
    {	
			if(led2cnt[0]>=led2cnt[1])
			{//过去2s
			led2cnt[0]=0;
			DHT11_ReadData();
			}
	  	KQM_DealData();
			Su03tDealData();
    }
}

dht11.c

#include "dht11.h"
float hum,tem;
//PG11要能够切换输入输出
void DHT11_Config()
{
	//配置为开漏模式
	//开时钟
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
	//配置io
	 GPIO_InitTypeDef GPIO_InitStruct = {0};
	 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
	 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	 GPIO_Init(GPIOG,&GPIO_InitStruct);
	 DHT11HIGH();
}

//根据DHT11时序读取数据
void DHT11_ReadData()
{
	uint16_t i = 0;
	uint16_t timeout = 0;
	uint8_t data[5] = {0};
  uint8_t arr[50] = {0};
	//输出一个最少18ms的低电平,最大30ms
	DHT11HIGH();
	DHT11LOW();
	Delay_nms(18);
	DHT11HIGH();
	//检测有持续83us低电平和87us的高电平
	timeout=0;
	while(DHT11read()==1)//检测83us低电平
	{
		timeout++;
			 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
	}
	timeout=0;
		while(DHT11read()==0)//检测87us的高电平
	{
		timeout++;
		Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
	}
	//读取40位数据
	for(i=0;i<40;i++)
		{
			timeout=0;
			while(DHT11read()==1)//等待us低电平
	   {
				timeout++;
			 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
		 }
		 timeout=0;
		  while(DHT11read()==0)//等待能判断的高电平标志
   	 {
		 timeout++;
		 Delay_nus(1);
		if(timeout>=100)
		{
			return ;
		}
		}
		 
		 Delay_nus(45);
	   arr[i]=DHT11read();
	}
			for(i=0;i<40;i++){ //0~7  8~15  16~23  24~31 32~39
		data[i/8]+=(arr[i]<<(7-i%8));
	}
	if(((data[0]+data[1]+data[2]+data[3])&0xff) != data[4]){
		return;
	}

	hum=data[0]+data[1]/10.0;
	tem=data[2]+(data[3]&0x7f)/10.0;
	if((data[3]&0x80) != 0){
		tem = 0-tem;
	}
		printf("读取完毕:湿度:%.1f   温度:%.1f\r\n",hum,tem);
		return;
}

dht11.h

#ifndef _DHT11_H_
#define _DHT11_H_
#include "stm32f10x.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"
void DHT11_Config();
void DHT11_ReadData();

#define DHT11HIGH() GPIO_SetBits(GPIOG,GPIO_Pin_11)

#define DHT11LOW()  GPIO_ResetBits(GPIOG,GPIO_Pin_11)

#define DHT11read() GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_11)

#endif
		

su03t.c

#include "stm32f10x.h"
#include "stdio.h"
#include "string.h"
#include "su03t.h"

typedef struct{
	uint8_t u5_recv[10];//保存数据数组
	uint8_t  u5_cnt;//数组下标
	uint8_t u5_tflag;
}UART5DATA;//数据类型

void Su03t_U5Config()//串口5 PC12(TX) PD2(RX)
{
	
		//开时钟U5 PD12(TX) PD2(RX)
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,ENABLE);
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);
	  //配置PC12(TX)
		GPIO_InitTypeDef GPIO_InitStruct = {0};
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推完输出
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOC,&GPIO_InitStruct);
		//PD2(RX)
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOD,&GPIO_InitStruct);
		
  	//配置串口5  波特率9600 数据位8,校验位0,停止位1
		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
		USART_InitStruct.USART_BaudRate = 9600;//波特率
		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
		USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//打开接收
		USART_InitStruct.USART_Parity = USART_Parity_No;
		USART_InitStruct.USART_StopBits = USART_StopBits_1;
		USART_InitStruct.USART_WordLength = USART_WordLength_8b;
		USART_Init(UART5,&USART_InitStruct);
	  //使能串口
		USART_Cmd(UART5,ENABLE);
   	//配置串口4的中断(采用中断接收)
		USART_ITConfig(UART5,USART_IT_RXNE,ENABLE);//使能串口4	的接收非空中断
		USART_ITConfig(UART5,USART_IT_IDLE,ENABLE);//总线空闲中断
		NVIC_SetPriority(UART5_IRQn,6);//设置优先级0~15
		NVIC_EnableIRQ(UART5_IRQn);//使能中断通道
}

//串口5发送单字节函数
void Uart5Senddata(uint8_t data)
{
	//等待发送完成
	while(USART_GetFlagStatus(UART5,USART_FLAG_TC)==0);
	//如果上次发送完成,就发送
	USART_SendData(UART5,data);
}

//串口5发送数组函数
void U5_Sendarr(uint8_t * data,uint32_t len)
{
	uint32_t i=0;
	for(i=0;i<len;i++){
		Uart5Senddata(*data);
		data++;
	}
}

UART5DATA u5_data={0};
void UART5_IRQHandler()//串口5中断执行
{
		uint8_t data=0;
	//判断接收中断是否发生
	if(USART_GetITStatus(UART5,USART_IT_RXNE)==SET)
	{
		data = UART5->DR;
		//USART1->DR = data;//回显
		u5_data.u5_recv[u5_data.u5_cnt]=data;
		u5_data.u5_cnt++;
		u5_data.u5_cnt%=10;
	}
	//触发空闲中断,表示总线空闲,接收完毕
	if(USART_GetITStatus(UART5,USART_IT_IDLE)==SET)
	{
		data = UART5->SR;//清理空闲中断,先读SR再读DR
		data = UART5->DR;	
		u5_data.u5_tflag=1;
	}
}

//将double转换成8位类型数组arr
void DoubleToUint8(double data,uint8_t *arr)
{
	
	uint8_t *p = (uint8_t *)&data;
	uint8_t i=0;
	for(i=0;i<8;i++)
	{
		arr[i]=p[i];
	printf("%02x ",*(p+i));
	}
	printf("\r\n");
	return;
}

extern float voc;
extern float ch2o;
extern float co2;
extern float hum;
extern float tem;

int Su03tDealData()
{
	
	if(u5_data.u5_tflag!=1)
	{
		return 1;
	}
	
	if(u5_data.u5_recv[0]!=0xAA||u5_data.u5_recv[1]!=0x55)
	{
		printf("数据帧头出错\r\n");
		return 2;
	}
		if(u5_data.u5_recv[4]!=0xAA||u5_data.u5_recv[3]!=0x55)
	{
		printf("数据帧尾出错\r\n");
		return 3;
	}	
	switch(u5_data.u5_recv[2])
	{
		case 1:printf("接收01,空气质量指令\r\n");
			Su03tSendMsg(1,voc);//voc
		break;
		case 2:	printf(" 接收02,甲醛指令\r\n");
		Su03tSendMsg(2,ch2o);//voc
			break;
		case 3:	printf(" 接收03,Co2指令\r\n");
		Su03tSendMsg(3,co2);//voc
			break;
		case 6:	printf(" 接收06,温度指令\r\n");
		Su03tSendMsg(6,tem);
		break;
		case 7:	printf(" 接收07,湿度指令\r\n");
		Su03tSendMsg(7,hum);
		break;
	}
  memset(&u5_data,0,sizeof(u5_data));
	return 0;
}

//拼接指令函数 AA 55 04 00 00 00 00 00 80 37 40 55 AA
void Su03tSendMsg(uint8_t cmd,double data)
{
	uint8_t msg[13]={0};//存放要发送的指令
	msg[0]=0xAA;
	msg[1]=0x55;
	msg[2]=cmd;
	DoubleToUint8(data,&msg[3]);
	msg[11]=0x55;
	msg[12]=0xAA;
	//通过串口5发送
	U5_Sendarr(msg,13);
}	

su03t.h

#ifndef _SU03T_H_
#define _SU03T_H_
#include "stm32f10x.h"
void Su03t_U5Config();
void Uart5Senddata(uint8_t data);
void U5_Sendarr(uint8_t * data,uint32_t len);
void UART5_IRQHandler();
void DoubleToUint8(double data,uint8_t *arr);
int Su03tDealData();
void Su03tSendMsg(uint8_t cmd,double data);
#endif
		

总结

1.先根据原理图和该模块的手册,配置对应的时钟和io。
2.进行数据读取,严格按照该模块手册中的时序一步一步来。
3.最后进行数据处理并导入到语音模块。

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

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

相关文章

WLAN 4-Way Handshake如何生成GTK?

关于Wi-Fi的加密认证过程&#xff0c;可以参考如下链接&#xff0c;今天我们来理解如何生成GTK。 WLAN数据加密机制_tls加密wifi-CSDN博客 1 GTK GTK&#xff08;Group Temporal Key&#xff09;是由AP通过GMK生成&#xff0c;长度为128位&#xff0c;并在四次握手的第三步中…

哈尔滨高校大学智能制造实验室数字孪生可视化系统平台项目的验收

哈尔滨高校大学智能制造实验室数字孪生可视化系统平台项目的验收&#xff0c;标志着这一技术在教育领域的应用取得了新的突破。项目旨在开发一个数字孪生可视化系统平台&#xff0c;用于哈尔滨高校大学智能制造实验室的设备模拟、监测与数据分析。项目的主要目标包括&#xff1…

数据结构_线性表

线性表的定义和特点 线性表是具有相同特性的数据元素的一个有限序列 :线性起点/起始节点 :的直接前驱 :的直接后继 :线性终点/终端节点 n:元素总个数,表长 下标:是元素的序号,表示元素在表中的位置 n0时称为空表 线性表 由n(n>0)个数据元素(结点),组成的有限序列 将…

Quantlab5.0:一切围绕可实盘策略驱动开发

原创文章第573篇&#xff0c;专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 2024年上半年即将结束&#xff0c;开始准备星球下半年的工作。 目前设想的——Quantlab5.0&#xff0c;之所以升级一个大版本&#xff0c;与4.x有很大不同。 5.0专注策略开发&…

负载均衡器有什么用?

负载均衡器有什么用&#xff1f; 负载均衡器是一种在多个服务器之间分配网络或应用程序流量的设备或软件应用程序。其主要目的是确保没有一台服务器承担过多的需求&#xff0c;从而提高应用程序的响应速度和可用性。 在计算机发展的早期&#xff0c;负载均衡是一个手动过程。…

【Sublime】Sublime Text 中运行终端

Sublime Text 本身并不是一个终端仿真器&#xff0c;可以使用插件来在 Sublime Text 中集成终端功能。最常用的插件之一是“Terminal”。 使用“Terminal”插件在 Sublime Text 中启动终端 以下是安装和使用该插件的步骤&#xff1a; 安装 Package Control&#xff1a; 如果你…

【自动驾驶汽车通讯协议】深入理解PCI Express(PCIe)技术

文章目录 0. 前言1. PCIe简介1.1 PCIe外观1.2 PCIe的技术迭代 2. PCIe的通道&#xff08;lane&#xff09;配置2.1 通道配置详解2.2 通道配置的影响 3. PCIe的架构3.1 架构层次3.2 核心组件 4. PCIe的特性5. PCIe在自动驾驶中的应用 0. 前言 按照国际惯例&#xff0c;首先声明&…

mybatis框架介绍 , 环境的搭建和代码实现

1.mybatis框架介绍 mybatis框架介绍 mybatis是Apache软件基金会下的一个开源项目&#xff0c;前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下&#xff0c;改名为mybatis。2013年11月又迁移到了github(GitHub 是一个面向开源及私有 软件项目的托管平…

40V 60V 80V 100V 400V高压LDO三端稳压器选择,技术参数

40V 60V 80V 100V 400V高压LDO三端稳压器选择,技术参数

基于yolo的物体识别坐标转换

一、模型简介: 1.1、小孔成像模型简图如下:不考虑实际相机中存在的场曲、畸变等问题 相对关系为: 为了表述与研究的方便,我们将像面至于小孔之前,且到小孔的距离仍然是焦距f,这样的模型与原来的小孔模型是等价的 相对关系为: 二、坐标系简介: **世界坐标系(world coo…

Qt中用QLabel创建状态灯

首先ui设计中分别创建了4个大灯和4个小灯。 编辑.h文件 #ifndef LED_H #define LED_H#include <QWidget> #include <QLabel>QT_BEGIN_NAMESPACE namespace Ui { class Led; } QT_END_NAMESPACEclass Led : public QWidget {Q_OBJECTpublic:Led(QWidget *parent n…

今天天气正好,开锐界L去追风

早就想开着它来个惬意的自驾游&#xff0c;结果因为工作原因一直在忙东忙西&#xff0c;锐界L这车都是上下班代步使用&#xff0c;今天终于空闲下来了&#xff0c;带着它来郊区转一圈&#xff0c;顺便交一篇极其不正式的游记吧&#xff0c;写的不好。 本来打算去的远一点&…

Nvidia Jetson/RK3588+AI双目立体相机,适合各种割草机器人、扫地机器人、AGV等应用

双目立体视觉是基于视差原理&#xff0c;依据成像设备从不同位置获取的被测物体的图像&#xff0c;匹配对应点的位置偏移&#xff0c;得到视差数据&#xff0c;进而计算物体的空间三维信息。为您带来高图像质量的双目立体相机&#xff0c;具有高分辨率、低功耗、远距离等优点&a…

一文掌握 Object 类里的所有方法(wait、notify、finalize)

Object 概述 Object 类是 Java 中所有类的父类&#xff0c;这个类中包含了若干方法&#xff0c;这也就意味着所有类都将继承这些方法。因此&#xff0c;掌握这个类的方法是非常必要的&#xff0c;毕竟所有类都能为你提供这些方法。 Object 类位于 java.base 模块下 java.lang…

python系列30:各种爬虫技术总结

1. 使用requests获取网页内容 以巴鲁夫产品为例&#xff0c;可以用get请求获取内容&#xff1a; https://www.balluff.com.cn/zh-cn/products/BES02YF 对应的网页为&#xff1a; 使用简单方法进行解析即可 import requests r BES02YF res requests.get("https://www.…

Apache IoTDB 监控详解 | 分布式系统监控基础

IoTDB 分布式系统监控的基础“须知”&#xff01; 我这个环境的系统性能一直无法提升&#xff0c;能否帮我找到系统的瓶颈在哪里&#xff1f; 系统优化后&#xff0c;虽然写入性能有所提升&#xff0c;但查询延迟却增加了&#xff0c;下一步我该如何排查和优化呢&#xff1f; 请…

办公软件的答案?ONLYOFFICE 桌面应用编辑器会是最好用的 Office 软件?ONLYOFFICE 桌面编辑器使用初体验

文章目录 &#x1f4cb;前言&#x1f3af;什么是 ONLYOFFICE&#x1f3af; 主要功能介绍及 8.1 新功能体验&#x1f3af; 在线体验&#x1f4dd;最后 &#x1f4cb;前言 提到办公软件&#xff0c;大家最常用的可能就是微软的 Microsoft Office 和国产的 WPS Office。这两款软件…

IBCS 虚拟专线用哪些特点!

当今数字化时代&#xff0c;高效、稳定、安全的网络连接对于企业和个人来说至关重要。IBCS 虚拟专线作为一种创新的网络解决方案&#xff0c;凭借其众多显著的优势&#xff0c;正逐渐成为众多用户的首选。 IBCS 虚拟专线最突出的优势之一在于其卓越的网络性能。它通过优化网络路…

Go 语言切片遍历地址会发生改变吗?

引言&#xff1a;今天面试的时候&#xff0c;面试官问了一道学 Go 语言的同学都会的简单代码&#xff0c;是关于 Go 语言 for 循环问题的&#xff0c;他询问了一个点&#xff0c;循环中共享变量的地址会发生改变吗&#xff1f; 相信听到这个问题的你&#xff0c;第一反应肯定是…

你还能顶几天?

A总&#xff1a;你还能顶几天&#xff1f; 汪汪队&#xff1a;顶到奉命撤退的那一天 A总&#xff1a;你在这守散钱点几十年了&#xff0c;从来没跟我提过任何的要求&#xff0c;难道你不困难吗&#xff1f; 汪汪队&#xff1a;有困难&#xff0c;但是我提了有什么用呢&#…