OpenHarmony学习笔记——Hi3861使用DHT11获取温湿度

前言

此文主要是使用Hi3861的GPIO口,模拟1—Wire时序,获取类单总线协议器件DHT11的温湿度信息,由于小熊派官方的温湿度采集是用的I2C接口的SHT30来实现的,而笔者手头没有这个模块,只有一个DHT11,结合前面的经验,Hi3861的代码逻辑跟STM32差不多,理论上是可以移植的,于是就有了以下写bug的时光。

DHT11简介

DHT11内部包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接,在使用过程中,这个内置的8位单片机会实时采集温湿度并做好转换,等到主机端的起始信号就开始发送40位的数据。学习过单总线的其他器件的同学都知道,单总线的一般通信流程是遵循初始化、ROM、命令功能命令三部曲来实现的,而DHT11由于内部有单片机的存在,所以不需要写入ROM和命令来实现功能,这需要一个起始信号即可实现数据传输。想要深入了解的同学可以参考此文——DHT11使用笔记。这里重点关注下其传输的一帧数据的内容。
在这里插入图片描述

数据传送正确时校验和数据等于“ 8bit 湿度整数数据 +8bit 湿度小数数据
+8bi 温度整数数据 +8bit 温度小数数据 ”所得结果的末8位。

通信流程

对于编程,最主要还是要搞清楚通信流程,尤其是这种使用GPIO进行模拟时序的。DHT11的通信流程如下:
1.总线初始化;(主机拉低总线,时长不小于18ms,然后拉高总线,使得总线回到高电平20-40us,等待从机应答)。
2.DHT11应答;(DHT11将总线拉低80us,然后再拉高80us)。
3.输出数据帧(一次完整的数据传输为40bit,高位先出)。
在这里插入图片描述
实测波形如下,后面框起来的是应答信号和40位的数据(图片截取自立创EDA的【DHT11温湿度计】)。
请添加图片描述
使用逻辑分析仪抓取的波形:
在这里插入图片描述

有关这个波形的详细讲解可以去看立创EDA的——【DHT11温湿度计】在示波器上的波形!

硬件连接

由于采用的是模拟1-Wire的时序,所以可以选取任意一个GPIO口来实现,笔者这里使用的是GPIO11,根据芯片手册最好是使用带上拉电阻的模块。
在这里插入图片描述
接线如下:

Hi3861开发板DHT11引脚
GPIO11OUT
5V+
GND-
在这里插入图片描述

编程实现

GPIO API简介

弄清楚了上述的通信流程,可以发现,整个过程中,传输数据的GPIO管脚需要进行输入与输出的切换,在产生起始信号时,GPIO需要配置为输出模式,产生高低电平的切换;在检测应答信号及接收数据时,需要配置成浮空输入模式检测总线的高低电平状态。在这个过程中需要使用到Hi3861的GPIO API接口函数。有关GPIO控制的接口函数目录位置为base\iot_hardware\interfaces\kits\wifiiot_lite
wifiiot_gpio.hwifiiot_gpio_ex.h包含了所有和GPIO操作有关的函数接口。
在这里插入图片描述
这里简介一下需要使用到函数,详细用法直接.h的注释吧,和STM32的GPIO类似:
在这里插入图片描述

复位总线

在产生起始信号时的配置方法:
首先,GpioInit()初始化GPIO;然后配置为普通IO模式;再然后配置为输出模式,最后根据时序要求配置输出高低电平。
笔者这里GPIO2是用来做运行指示灯的。代码如下:

#define DHT11_GPIO  WIFI_IOT_IO_NAME_GPIO_11
IO操作函数											   
#define	DHT11_DQ_OUT_High GpioSetOutputVal(DHT11_GPIO, 1); //设置GPIO输出高电平
#define	DHT11_DQ_OUT_Low GpioSetOutputVal(DHT11_GPIO, 0); //设置GPIO输出低电平  
/****************************************
设置端口为输出
*****************************************/
void DHT11_IO_OUT(void)
{
    //设置GPIO_11为输出模式
    GpioSetDir(DHT11_GPIO, WIFI_IOT_GPIO_DIR_OUT);
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在    	 
u8 DHT11_Init(void)
{	 
	//初始化GPIO
    GpioInit();
    //设置GPIO_2的复用功能为普通GPIO
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_2, WIFI_IOT_IO_FUNC_GPIO_2_GPIO);
    //设置GPIO_2为输出模式
    GpioSetDir(WIFI_IOT_GPIO_IDX_2, WIFI_IOT_GPIO_DIR_OUT);
		 //设置GPIO_2输出高电平点亮LED灯
    GpioSetOutputVal(WIFI_IOT_GPIO_IDX_2, 1);
    //设置GPIO_11的复用功能为普通GPIO
	 IoSetFunc(DHT11_GPIO, WIFI_IOT_IO_FUNC_GPIO_11_GPIO);
    //设置GPIO_11为输出模式
    GpioSetDir(DHT11_GPIO, WIFI_IOT_GPIO_DIR_OUT);
		 //设置GPIO_11输出高电平
    GpioSetOutputVal(DHT11_GPIO, 1);		    
		DHT11_Rst();  //复位DHT11
		 return DHT11_Check();//等待DHT11的回应
} 

//复位DHT11
void DHT11_Rst(void)	   
{                
	DHT11_IO_OUT(); 	//SET OUTPUT
   DHT11_DQ_OUT_Low; 	//拉低DQ
    hi_udelay(20000);//拉低至少18ms
   DHT11_DQ_OUT_High; 	//DQ=1 
	hi_udelay(35);     	//主机拉高20~40us
}

DHT11应答

在等待应答阶段,需要将GPIO口配置为浮空输入模式,并且需要检测GPIO的状态来判断DHT11连接是否正常。
代码流程:
首先将GPIO切换为输入模式,然后检测GPIO状态,判断总线的高低电平状态是否满足应答信号。
代码如下:

//获取GPIO输入状态
u8 GPIOGETINPUT(WifiIotIoName id,WifiIotGpioValue *val)
{
    GpioGetInputVal(id,val);
    return *val;
}

/****************************************
设置端口为输入
*****************************************/
void DHT11_IO_IN(void)
{
    GpioSetDir(DHT11_GPIO, WIFI_IOT_GPIO_DIR_IN);//配置为输入模式
    IoSetPull( DHT11_GPIO, WIFI_IOT_IO_PULL_NONE);//配置为浮空输入
}

//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	  DHT11_IO_IN();//SET INPUT	 
    while (GPIOGETINPUT(DHT11_GPIO,&DHT11_DQ_IN)&&retry<100)//DHT11会拉低40~80us
	{
		retry++;
		hi_udelay(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    
    while ((!GPIOGETINPUT(DHT11_GPIO,&DHT11_DQ_IN))&&retry<100)//DHT11拉低后会再次拉高40~80us
	{
		retry++;
		hi_udelay(1);
	};
	if(retry>=100)return 1;	   
	return 0;
}

数据读取

在检测到DHT11的应答后,DHT11会紧接着发送40位BIT的数据,需要通过固定时间间隔去读取总线状态来获取。
编程流程:
继续配置为输入模式,间隔固定时间去读取总线电平,进而得到数据,最后根据数据格式解析出所需内容。
代码如下:

//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
  while(GPIOGETINPUT(DHT11_GPIO,&DHT11_DQ_IN)&&retry<100){//等待变为低电平
        retry++;
        hi_udelay(1);
    }
    retry=0;
    while((!GPIOGETINPUT(DHT11_GPIO,&DHT11_DQ_IN))&&retry<100){//等待变高电平
        retry++;
        hi_udelay(1);
    }
    hi_udelay(40);//等待40us	//用于判断高低电平,即数据1或0
    if(GPIOGETINPUT(DHT11_GPIO,&DHT11_DQ_IN))return 1; else return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
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读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5]={ 0 };
	u8 i;
	DHT11_Rst();
	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;	    
}

以上三步对应的示波器实际效果如下图(图片截取自立创EDA):
请添加图片描述
注:这里的实现需要导入hi_time.h头文件,调用hi_udelay()实现而不能用usleep()函数。
至此就可以通过DHT11获取到温湿度了,上述代码有极少部分缺失,如有需要,私信笔者,建议大家自己试着配配看。

效果一览

在这里插入图片描述

总结

本文主要是使用使用Hi3861的GPIO口去模拟单总线的时序,进而读取到DHT11的温湿度值,整体代码可以参考任何一个STM32版本的代码,需要特别注意的是,整个过程中需要使用阻塞式的延时hi_udelay(1),而不能使用非阻塞式的usleep(),非阻塞式的延时会产生任务流转,会影响时序,造成数据读取失败,笔者踩了这个坑,分享给大家避雷用。

目录

OpenHarmony学习笔记——南向开发环境搭建
OpenHarmony学习笔记——编辑器访问Linux服务器进行编译
OpenHarmony学习笔记——点亮你的LED
OpenHarmony学习笔记——多线程的创建
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕
OpenHarmony学习笔记——Hi3861使用DHT11获取温湿度
OpenHarmony学习笔记——Hi3861接入OneNET
手把手教你OneNET数据可视化
OpenHarmony学习笔记——Hi386+ASR-01的语音识别助手

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值