【单片机】温湿度传感器 DHT11

前言

大家好,我是林白柏;

希望你看完之后,能有所收获,不足请指正!

PS:本文提到的模块都使用正点原子的stm32开发板战舰驱动,模块用的某宝现成的模块。

PS:代码为正点原子的代码,整理成自己习惯的接口


模块介绍(可跳过)

模块长这个亚子

image-20220423093916157

DHT11里面封装了一个8位MCU,单片机连接了一个电阻式感湿元件和一个NTC测温元件;

数据的采集处理都由这个8位MCU完成,使用时我们只需要从这个MCU读出数据即可;

image-20220423095019199

传感器的外围电路也比较简单,只需要对数据线DATA上拉。

image-20220423094954678

模块使用

DHT11的参数说明

  • 湿度:20-90%RH;±5%RH
  • 温度:0-50℃;±2℃
  • 采样间隔:1s

数据包格式

DHT11采用单线通信数据包长度40bit(5bytes),数据包格式如下:

image-20220423095744473

校验和是前面4个字节的累加和,取低八位。

DHT11传输时序

发起通信

主机发送开始信号(拉低DATA线时间大于18ms),DHT11在开始信号结束后发送响应信号(拉低DATA线80us),然后延时40-50us后开始传输数据。
所以主机发送开始信号后,将io设置为输入,延时20-40us后检测DATA线电平状态,即可知道DHT11是否有响应。
image-20220423102106943
对应代码如下
定义了几个宏,方便配置IO输入/输出、读/写

#define DHT11_IO_IN()  {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=8<<12;} //IO配置为输入
#define DHT11_IO_OUT() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=3<<12;} //IO配置为输出
#define	DHT11_DQ_OUT PGout(11) //写IO DHT11_DQ_OUT = 1;/DHT11_DQ_OUT = 0;
#define	DHT11_DQ_IN  PGin(11)  //读IO

发送开始信号并延时等待

static void DHT11_Rst(void)
{                 
	DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//拉低DQ
    delay_ms(20);    	//拉低至少18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	delay_us(30);     	//主机拉高20~40us
}

检测DHT11是否有响应信号

u8 dht11_check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us(响应信号)
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100) { return 1;}//超时,无响应信号
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉高40-50us,结束后开始传输数据
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100) { return 1;}//超时,DHT11发送响应信号后,未把DATA线拉高
	return 0;
}

DHT11发送“0”和“1”的时序

发送“0”和“1”的区别只是高电平持续时间不一样。

“0”的时序
image-20220423112717952
“1”的时序
image-20220423112738439
对应代码如下

从DHT11读取1个bit,此处不判断每个bit开始阶段的低电平时间,只判断由低电平变到高电平后,高电平时间持续是否超过40us(“0”传输时间接近40us,如果设置再长一点可能导致通讯失败);

static u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//等待变为低电平
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//等待变高电平
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//等待40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}

知道1个bit怎么读取之后,就可以读取1个byte

static u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}

知道1个byte读取过程,也就可以读取一帧完整的数据了(数据结构体部分看下文解释)

u8 dht11_read_data( dht11_data_t * data )  //data为用户传进来的结构体指针,用于返回读取的数据
{        
 	u8 buf[5];
	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]) {	//数据校验
		    memcpy( data, buf, sizeof( dht11_data_t ) );//校验通过,将数据传回给用户
            #if 0
                data->humi_int      = buf[0];
                data->humi_decimals = buf[1];
                data->temp_int      = buf[2];
                data->temp_decimals = buf[3];
            #endif
		}
	} 
    else { 
        return 1;
    }
    
	return 0; 
}

这个地方用结构体表示一帧数据(不包含校验和,因为用户不需要关心校验和,能传给用户的数据肯定是校验过的),因为使用了memcpy函数拷贝(该函数需要包含头文件string.h),dht11_data_t的数据存储格式需要跟buf匹配;也可以直接对结构体成员变量赋值,这样就不用关心结构体的存储格式

typedef struct {
    uint8_t humi_int;
    uint8_t humi_decimals;
    uint8_t temp_int;
    uint8_t temp_decimals;
}dht11_data_t;

dht11_read_data中读取数据时,数据存到buf的格式如下

image-20220423105255091

因为dht11_data_t成员变量都是uint8_t [1byte],所以为单字节对齐,跟buf是匹配的,可以直接用memcpy函数拷贝,存储格式如下

image-20220423105629307

代码:h文件

头文件声明了两个函数:初始化函数,读取函数;DHT11采样间隔为1s,所以读取间隔需要大于1s,否则会读取失败;

uint8_t dht11_init(void);// 0:suc; 1:fail
uint8_t dht11_read_data( dht11_data_t * data );// 0:suc; 1:fail; 实测读取间隔需要大于1200ms

代码使用方法

读取前,需要先定义一个保存数据的结构体,然后把结构体的地址传进给dht11_read_data函数。

int main(void){
    dht11_data_t data;
    
    dht11_init();
    
    while(1) {
        
        if( !dht11_read_data( &data ) ) {
            _LOG_DEBUG( "[dht] humi %d.%d%%, temp %d.%dC\n\n", data.humi_int, data.humi_decimals
                                                             , data.temp_int, data.temp_decimals );
        }
        else {
            _LOG_INFO("[err] dht read\n");
        }
        delay_ms(2000);
    }
}

代码获取

共用代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/common

驱动代码:https://gitee.com/sumoting1629/mcu-practice/tree/master/component

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值