stm32f103c8t6+dht11测量温湿度(单总线协议)

2023-10-27

核心问题:如何根据电路和时序来写程序
接口协议:单总线(意味着一根数据线实现接收和发送,所以相应的GPIO口要设置为2种模式(输入/输出))
电路:如果没有在电路中作5k上拉,可以把GPIO模式设置成GPIO_Mode_IPU实现软件上拉的效果在这里插入图片描述时序:在这里插入图片描述时序解读:初始化(MCU复位+DHT11响应)、数据传输、结束
(1)初始化
在这里插入图片描述初始化可分为2部分,即主机复位+从机响应
主机复位的时序图为红框所示:主机至少拉低18us,然后主机拉高20~40us
(注意;此时的GPIO口为输出模式,并且为了模拟上拉效果,使用GPIO_Mode_IPU)

void DHT11_User_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
}

/*GPIO初始化*/
void DHT_Mode(uint16_t mode)//mode==1时,配置为输出模式; mode==0,配置为输入模式
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	if(mode == 1)
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	}	
	else
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//软件实现5K上拉电阻
	}	
	GPIO_Init(GPIOB,&GPIO_InitStructure);	//调用库函数初始化GPIOB	
}

/*向dht11发送启动信号,这也叫复位信号*/
void Send_Start(void)
{
	DHT_Mode(OUT);//OUT被宏定义为1,即DHT_Mode(1):输出模式
	
	DHT_Low;//#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)
	delay_ms(18);//拉低总线,保持18ms
	
	DHT_High;//释放总线,#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
	delay_us(40);//释放总线后要等待20~40us
}

DHT11响应的时序是绿色框所示
在这里插入图片描述先拉低80us,再拉高80us
这里为了省事(不严谨),不管dht11有没有应答,都用程序来模拟“先拉低80us,再拉高80us”的信号,让单片机认为dht11应答成功
(注意:此时DHT11发送应答信号,是从机向主机发送信号,所以GPIO口配置为输入模式)

/*传感器发送应答信号*/
//当器件检测到MCU复位信号后,拉低总线80us表示应答。再拉高总线80us开始传输数据
//这里强制认为dht11应答成功
void DHT_Send_Response(void)
{
	DHT_Mode(INT);//单片机的引脚改为接收信号的状态
	
	//检测从机低电平的时间长度,如果100us内从机不应答就退出while,继续往下执行
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
	//检测从机高电平的时间长度
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持1
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
}

(2)数据传输
数据无非是0/1
在这里插入图片描述dht11向主机发送高低电平
50us的低电平+26~28us高电平,表示数据0

在这里插入图片描述dht11向主机发送高低电平
50us的低电平+70us高电平,表示数据1
(注意:把数据中该写1的部分写1之后,其余部分自动写0,所以程序中只要写1的部分。为什么?我也不知道,B站的一个视频是这么说的)

/*dht11读取1字节数据*/
uint8_t DHT11_Read_Byte(void)
{
	uint8_t data = 0;
	uint8_t i;
	for(i=0;i<8;i++)//按位接受8位数据
	{
		DHT_Mode(INT);
		
		//来1个50us的延时,50us的低电平是数据传输的开始
		while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 50))//每1us,Time自增1次   判断50us内,总线信号有没有一直保持0
		{
			Time++;
			delay_us(1);
		}
		Time = 0;//上述while循环的判断结束后,Time清零
		
		
		data <<= 1;
		delay_us(40);//26~28us的高电平指数据0  70us的高电平指数据1   40是自己取的介于26~28与70之间的值
		//40us后依然为高电平就是数据1
		//这里只要写1就行,程序设置什么时候写1。剩下的自动填充为0
		
		if(GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1)
		{
			data |=0x01;//数据从低位往高位移
			
			//等待高电平结束
			//30us+delay_us(40)==70us。70us的高电平即是数据1
			while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 30))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
			{
				Time++;
				delay_us(1);
			}
			Time = 0;//上述while循环的判断结束后,Time清零
		}
	}
	return data;
}

(3)数据传输结束后,通过程序来解读温湿度并校验
一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度

/*DATA[0]~DATA[4]:8位的湿度正数数据  8位的湿度小数数据  8位温度整数数据  8位温度小数数据 8位的校验和*/
//读取温湿度的值并校验
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp)
{
	uint8_t i;
	uint8_t DATA[5] = {0,0,0,0,0};
	Send_Start();//初始化
	DHT_Send_Response();//dht11应答成功
	
	//dht11应答成功之后就开始传输数据了
	
	for( i=0;i<5;i++)//读取40位数据
	{
		DATA[i] = DHT11_Read_Byte();
	}
	
	delay_ms(1);
	
	if((DATA[0]+DATA[1]+DATA[2]+DATA[3] == DATA[4]))//如果校验没问题则输出测量结果
	{
		*humi = DATA[0];
		*temp = DATA[2];
	}
	
	else//否则数据清零 
	{
		for( i=0;i<5;i++)
		{
		DATA[i] = 0;
		}
	}
}

整体程序代码块
dht11.c

#include "dht11.h"
#include "delay.h"

uint16_t Time = 0;

/*开时钟*/
void DHT11_User_Config(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
}

/*GPIO初始化*/
void DHT_Mode(uint16_t mode)//mode==1时,配置为输出模式; mode==0,配置为输入模式
{
	GPIO_InitTypeDef GPIO_InitStructure;	
	if(mode == 1)
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	}	
	else
	{
		GPIO_InitStructure.GPIO_Pin = DHT;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//软件实现5K上拉电阻
	}	
	GPIO_Init(GPIOB,&GPIO_InitStructure);	//调用库函数初始化GPIOB	
}

/*向dht11发送启动信号,这也叫复位信号*/
void Send_Start(void)
{
	DHT_Mode(OUT);//OUT被宏定义为1,即DHT_Mode(1):输出模式
	
	DHT_Low;//#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)
	delay_ms(18);//拉低总线,保持18ms
	
	DHT_High;//释放总线,#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
	delay_us(40);//释放总线后要等待20~40us
}

/*传感器发送应答信号*/
//当器件检测到MCU复位信号后,拉低总线80us表示应答。再拉高总线80us开始传输数据
//这里强制认为dht11应答成功
void DHT_Send_Response(void)
{
	DHT_Mode(INT);//单片机的引脚改为接收信号的状态
	
	//检测从机低电平的时间长度,如果100us内从机不应答就退出while,继续往下执行
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
	//检测从机高电平的时间长度
	while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 100))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持1
	{
	Time++;
	delay_us(1);
	}
	Time = 0;//上述while循环的判断结束后,Time清零
	
}


 /*dht11读取1字节数据*/
uint8_t DHT11_Read_Byte(void)
{
	uint8_t data = 0;
	uint8_t i;
	for(i=0;i<8;i++)//按位接受8位数据
	{
		DHT_Mode(INT);
		
		//来1个50us的延时,50us的低电平是数据传输的开始
		while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 0) && (Time < 50))//每1us,Time自增1次   判断50us内,总线信号有没有一直保持0
		{
			Time++;
			delay_us(1);
		}
		Time = 0;//上述while循环的判断结束后,Time清零
		
		
		data <<= 1;
		delay_us(40);//26~28us的高电平指数据0  70us的高电平指数据1   40是自己取的介于26~28与70之间的值
		//40us后依然为高电平就是数据1
		//这里只要写1就行,程序设置什么时候写1。剩下的自动填充为0
		
		if(GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1)
		{
			data |=0x01;//数据从低位往高位移
			
			//等待高电平结束
			//30us+delay_us(40)==70us。70us的高电平即是数据1
			while((GPIO_ReadInputDataBit(DHT_PROT,DHT) == 1) && (Time < 30))//每1us,Time自增1次   判断100us内,总线信号有没有一直保持0
			{
				Time++;
				delay_us(1);
			}
			Time = 0;//上述while循环的判断结束后,Time清零
		}
	}
	return data;
}


/*DATA[0]~DATA[4]:8位的湿度正数数据  8位的湿度小数数据  8位温度整数数据  8位温度小数数据 8位的校验和*/
//读取温湿度的值并校验
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp)
{
	uint8_t i;
	uint8_t DATA[5] = {0,0,0,0,0};
	Send_Start();//初始化
	DHT_Send_Response();//dht11应答成功
	
	//dht11应答成功之后就开始传输数据了
	
	for( i=0;i<5;i++)//读取40位数据
	{
		DATA[i] = DHT11_Read_Byte();
	}
	
	delay_ms(1);
	
	if((DATA[0]+DATA[1]+DATA[2]+DATA[3] == DATA[4]))//如果校验没问题则输出测量结果
	{
		*humi = DATA[0];
		*temp = DATA[2];
	}
	
	else//否则数据清零 
	{
		for( i=0;i<5;i++)
		{
		DATA[i] = 0;
		}
	}
}

dht11…h

#ifndef __DHT11_H
#define __DHT11_H	
#include "sys.h"

#define DHT        GPIO_Pin_5
#define DHT_PROT   GPIOB

#define OUT 1
#define INT 0

#define DHT_High   GPIO_SetBits(DHT_PROT,DHT)
#define DHT_Low    GPIO_ResetBits(DHT_PROT,DHT)


void DHT11_User_Config(void);
void DHT_Mode(uint16_t mode);
void Send_Start(void);
void DHT_Send_Response(void);
uint8_t DHT11_Read_Byte(void);
void DHT11_Read_Data(uint8_t *humi,uint8_t *temp);
#endif

main.c


#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "GPIOLIKE51.h"//sys.h和GPIOLIKE51.h基本一致,delay.h中包含了sys.h所以不再使用GPIOLIKE51.h
#include "delay.h"

#include "oled.h"
#include "dht11.h"



//extern const unsigned char BMP1[];
//extern const unsigned char BMP2[];

uint8_t humi,temp;

int main(void)
{
	float length;
	delay_init();
	delay_ms(1500);
	
	OLED_Init();
	OLED_Clear();	

	DHT11_User_Config();

	while(1)
	{

		DHT11_Read_Data(&humi,&temp);
		OLED_ShowNum(80,3,humi,3,16);
		OLED_ShowNum(80,5,temp,3,16);
	}

}

oled的驱动文件就不贴了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32f103c8t6+dht11测量温湿度(单总线协议) 的相关文章

随机推荐

  • [Python] 利用os库和xlrd库实现根据excel清单批量修改文件名

    问题描述 已知有一文件夹存放着若干按照 身份证号码 姓名格式命名的图片 有一表格里记录身份证号码 姓名和学号 现在需要根据图片文件名上面的身份证号码去寻找表格里相应的学号 并将文件重命名为 学号 姓名 要求为 jpg格式 文件夹内容如下 e
  • bci测试如何整改_一个案例教会你EMC辐射抗干扰整改

    整改前状况 工况 供电直流24V 开机 摄像头抓取图像视频 通过线缆将CVBS信号传送给疲劳驾驶预警主机 主机将视频信息处理 再传送CVBS信号到监控器 测试标准 测试布置 测试现象 疲劳驾驶驾驶预警主机新版主机 监控器显示图像抖动 明暗变
  • gorm自动更新创建时间及更新时间,自动更新时间戳

    平时写代码 总是要处理更新时间和创建时间 要写不少的代码 而且还容易忘记 针对于这个问题研究了一下有没有什么比较好的方式 下面说一下如何摆脱体力劳动 假设场景 需要改分好毕业 CREATE TABLE user id int 11 NOT
  • cv2‘ has no attribute ‘gapi_wip_gst_GStreamerPipeline‘错误解决方法

    import albumentations as A 报错 cv2 has no attribute gapi wip gst GStreamerPipeline 解决方法 pip3 install user upgrade opencv
  • springboot农场农业技术信息化管理系统java

    本系统采用intellij idea软件开发 项目架构 B S架构 前端技术 Layui Html CSS 后端技术 Java mybatis Springmvc 运行环境 win10 win11 jdk1 8 数据库 mysql5 7 8
  • 关于C++中Eigen库效率提升的思考

    目录 引言 一 什么是Eigen 二 使用步骤 1 引入库 2 建立矩阵 3 基本操作 三 具体的例子 矩阵乘法 1 Eigen库 2 GPU并行计算 总结 可能的方案 引言 在处理矩阵运算上 各种语言都有了自己的处理办法 例如 Pytho
  • m.2接口和nvme区别_m.2 nvme和m.2有区别吗?

    m2接口有两种 一种走sata的 另外一种走pcie 就是nvme 的 走sata的速度跟传统sata接口的没区别 就是接口变成了m2的了 走pcie的 就是我们说的nvme接口是m2的 性能也强了 M 2接口有SATA也有NVMe PCI
  • 【解决】uniapp使用video标签在浏览器调试正常,安卓真机、手机上显示小黑块,无法正常显示

    问题 uniapp中使用video标签展示视频 在浏览器展示正常 但是运行到安卓真机 或者手机上显示小黑块 无法正常显示 示例代码如下
  • Ubuntu目录常见命令

    一 ls 1 ls a 查看隐藏文件 2 ls l 查看详细信息 3 ls h 查看文件的大小 h必须要和 l联合使用 ls l h ls lh 三者联合使用 ls lha 4 ls通配符使用 ls 1 找到1结尾的 代替一个 定义一个匹配
  • C++文件打开方法

    C 文件打开方法 为什么要打开文件 如何打开文件 C和C 都能用的 freopen版 fopen版 只有C 能用的 文件输入输出流 fstream 为什么要打开文件 有时候 我们需要把一些内容保存到文件中 以便后期使用 今天小编就来介绍一下
  • 关于PBFT算法

    PBFT算法 即为实用拜占庭容错算法 主要应用于联盟链中 它的关键技术是一致性协议 1 假设共有f个作恶节点 那么总节点需要大于等于3f 1个 即至少有2f 1个诚实节点 2 一致性协议 由诚实节点共同维护 若主节点的请求得到2f 1个诚实
  • 主流的Web自动化测试工具

    1 什么是自动化 概念 由机器设备代替人工自动完成指定目标的过程 1 1 优点 1 减少人工劳动力 2 提高工作效率 3 产品规格统一标准 4 规模化 批量生产 2 什么是自动化测试 软件测试 校验系统是否满足规定的需求 弄清预期结果与实际
  • elk笔记13--Query DSL

    elk笔记13 Query DSL 1 基础介绍 2 常见查询类型 2 1 Query and filter context 2 2 Compound queries 2 3 Full text queries 2 4 Geo querie
  • leptonica源码编译

    leptonica源码编译 原创作品 转载请说明出处 谢谢 原创作品 转载请说明出处 谢谢 原创作品 转载请说明出处 谢谢 环境软件 a leptonica1 78 0 b cppan c cmake d VS2015 e 操作系统Wind
  • python挖掘时间特征信息,计算日期是当年的第几周、当月的第几周

    需要使用的主要有两个工具包 from datetime import datetime from calendar import monthrange 分别计算当前的日期对应的 所在年份 所在月份 当前月的第几日 星期几 当月的天数 当年的
  • 手机厂商竞相推“新品牌”,想明白了吗?

    2013年 国产智能手机 新品 市场可谓热闹非凡 9月28日 联想发布高端子品牌VIBE 11月20日 金立发布全新互联网品牌INUI 华为也有意将荣耀品牌从华为分离出来 使其成为独立互联网手机品牌 11月17日 OPPO手机副总经理刘作虎
  • 【华为OD统一考试B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • 2022年网络安全比赛--压缩包文件暴力破解中职组(超详细)

    2022年比赛压缩包文件暴力破解解析 一 竞赛时间 180分钟 共计3小时 二 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 1 通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机服务版本信息 将 Telnet 版本信息字
  • Angular学习笔记29:事件绑定

    事件绑定 前面的绑定都是按照 元素到组件的方式 但是在一个页面不可能只是数据展示给用户 而用户对页面没有任何操作 不能进行和页面的交互操作 在与用的交互操作时 怎么才能知道用户进行了怎么样的操作和交互呢 这个时候 就用到了各种事件 如按下键
  • stm32f103c8t6+dht11测量温湿度(单总线协议)

    核心问题 如何根据电路和时序来写程序 接口协议 单总线 意味着一根数据线实现接收和发送 所以相应的GPIO口要设置为2种模式 输入 输出 电路 如果没有在电路中作5k上拉 可以把GPIO模式设置成GPIO Mode IPU实现软件上拉的效果