【单片机】温湿度传感器 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
    评论
G:\大四上\课设\程序\2012课设可用\dht11.c #include "DHT11.h" #include "delay.h" //uchar aa[12]; //uchar TH_data,TL_data,RH_data,RL_data,CK_data; char receive() { unsigned char respond; unsigned char i,temp, com_data=0; for(i=0;i<8;i++) { respond=2; while((!TRH)&&respond++); delay_us(); delay_us(); delay_us(); if(TRH) { temp=1; respond=2; while((TRH)&&respond++); } else temp=0; com_data <<=1; com_data "=temp; } return( com_data); } void read_TRH(unsigned char *temp) { unsigned char respond; unsigned char TH_temp, TL_temp, RH_temp, RL_temp, CK_temp,untemp; TRH=0; //主机拉低18ms delay_ms(18); TRH=1; //DATA总线由上拉电阻拉高主机延时20us delay_us(); delay_us(); delay_us(); delay_us(); //主机设为输入判断从机响应信号 TRH=1; //判断DHT11是否有低电平响应信号 如不响 if(!TRH) { respond=2; //判断DHT11发出 80u while((!TRH)&& respond++); respond=2; //判断从机是否发出 while(TRH && respond++); //数据接收状态 RH_temp = receive(); RL_temp = receive(); TH_temp = receive(); TL_temp = receive(); CK_temp = receive(); TRH=1; //数据校验 untemp=( RH_temp+ RL_temp+ TH_temp+ TL_temp); if(untemp== CK_temp) { *temp = RH_temp; temp++; *temp = RL_temp; temp++; *temp = TH_temp; temp++; *temp = TL_temp; temp++; *temp = CK_temp; Page: 1 G:\大四上\课设\程序\2012课设可用\dht11.c } } } /* void lcd_display_time_wenshi() { read_TRH(); aa[0]=RL_data/10+0x30; aa[1]=RL_data%10+0x30; aa[2]=TL_data/10+0x30; aa[3]=TL_data%10+0x30; wr_com(0x80); wr_dat(aa[0]); wr_dat(aa[1]); wr_com(0x80+0x40); wr_dat(aa[2]); wr_dat(aa[3]); } void main () { lcd_init() ; while(1) { delay_ms(100); lcd_display_time_wenshi(); } }*/ Page: 2
STM32F407单片机读写OneWire_DHT11湿度传感器(串口屏显示)软件工程源码,可以做为你的学习设计参考。 int main(void) { /* 复位所有外设,初始化Flash接口和系统滴答定时器 */ HAL_Init(); /* 配置系统时钟 */ SystemClock_Config(); /* 初始化串口并配置串口中断优先级 */ MX_DEBUG_USART_Init(); HMI_USARTx_Init(); DHT11_Init(); /* 无限循环 */ while (1) { /*调用DHT11_Read_TempAndHumidity读取湿度,若成功则输出该信息*/ if(DHT11_Read_TempAndHumidity(&DHT11_Data)==SUCCESS) { HMI_value_setting("page1.gross.val",DHT11_Data.humidity*10); HMI_value_setting("page1.net.val",DHT11_Data.temperature*10); printf("读取DHT11成功!-->湿度为%.1f %RH ,温度为 %.1f℃ \n",DHT11_Data.humidity,DHT11_Data.temperature); } else { printf("读取DHT11信息失败\n"); } HAL_Delay(1000); } } /** * 函数功能: 向串口屏发送数据 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void HMI_value_setting(const char *val_str,uint32_t value) { uint8_t tmp_str[30]={0}; uint8_t i; sprintf((char *)tmp_str,"%s=%d",val_str,value); for(i=0;iDR=tmp_str[i]; while(__HAL_UART_GET_FLAG(&husartx_HMI, UART_FLAG_TXE) == RESET); } HMI_USARTx->DR=0xFF; while(__HAL_UART_GET_FLAG(&husartx_HMI, UART_FLAG_TXE) == RESET); HMI_USARTx->DR=0xFF; while(__HAL_UART_GET_FLAG(&husartx_HMI, UART_FLAG_TXE) == RESET); HMI_USARTx->DR=0xFF; while(__HAL_UART_GET_FLAG(&husartx_HMI, UART_FLAG_TXE) == RESET); } /** * 函数功能: 向串口屏发送浮点数据 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void HMI_string_setting(const char *val_str,int32_t value) { uint8_t tmp_str[50]={0}; uint8_t i; float temp=(float)value; sprintf((char *)tmp_str,"%s=\"%.1f\"",val_str,temp/100); for(i=0;iDR=tmp_str[i]; while(__HAL_UART_GET_FLAG(&husartx_HMI, UART_FLAG_TXE) == RESET); } HMI_USARTx->DR=0xFF;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值