DHT11温湿度传感器编程思路以及代码的实现

本文介绍DHT11温湿度传感器的基础知识及应用,包括其特点、参数、时序控制方法及STM32单片机上的编程实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们刚开始进入单片机的学习中,练习写传感器的时序是必不可少的,其实我比较推荐大家刚开始练习的时候使用DHT11来练习

推荐的原因:

  1. 因为DHT11的时序简单。
  2. DHT11是国产的,全中文的参考手册,不用担心英文看不懂。
  3. 功能少,就只有一个测量温湿度的功能。

综上,因此DHT11我认为是非常适合刚开始入门单片机的朋友学习的。

 

那么在使用传感器前,我们必须要先看数据手册,并不需要全部浏览,我们只需要看他重要的点,就OK了。

这就是DHT11温湿度传感器的外观,我们了解一下就好。

 

接下来我们看一起DHT11的参数特性

用红框圈出来的,就是重点,我们必须知道,这个DHT11温湿度传感器的测量范围,以及精度、分辨率。如果超出了这个范围,那么DHT11就不能够使用了。

接下来看一下引脚说明。            我们要注意的是:这个器件使用的是单总线协议是总所周知的了,但是他的供电范围我们也需要了解一下,范围是3.5-5.5v。如果超出这个范围,传感器可能会烧、假如低于这个范围,可能传感器会读出错误的温湿度数据或者压根就罢工了。   因此,我们在使用那些3.3v单片机做编程的时候,就要注意这一点了。

这个是数据手册上显示的DHT11典型的电路连接方法,我们再数据口上要接上一个上拉电阻。供电所使用的为7805的稳压电源,也就是5V。实际上,只要我们满足他的供电电压范围,都是能够工作的。

接下来我们看一下它的数据格式(重点)

        DHT11用的是单总线协议,一次传送40位的数据。     注意了,看到这一句话,也就是说我们每次读取DHT11的数据时,都要一次性读取40次,也就是读取40位。并且数据前16位是与湿度相关的,中间16位是与温度相关的,最后八位是用来校验的,当我们校验成功后,证明这一次的温湿度结果正确的,我们单片机就可以使用这个温湿度值;如果校验不通过,那么就代表我们这次读取出来的温湿度值,是错误的(也许是我们的时序错误了,也许是传感器的问题),我们不进行采样。

        同时呢,商家的数据手册还给出了一个校验数据的示例图,而且还是全中文的,所以说我说的没错吧,这个器件是真的简单到不能再简单了,非常适合新手入门练习如何写时序。

        DHT11的总体通信流程。第一步:主机先发送开始信号,从机会返回一个相应信号进行应答。    第二步:主机信号线拉高准备接收数据。    第三部:开始接收数据(一次接收40位)。

那么这个就是一个人数据读取的一个流程,那么我们每一个流程又应该怎么做呢?

 

步骤一:DHT11 上电后(DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境
温湿度数据,幵记录数据,同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电平;此时 DHT11 的
DATA 引脚处于输入状态,时刻检测外部信号。

步骤二:微处理器的 I/O 设置为输出同时输出低电平,且低电平保持时间不能小于 18ms,然后微处理器的 I/O
设置为输入状态,由于上拉电阻,微处理器的 I/O 即 DHT11 的 DATA 数据线也随之变高,等待 DHT11 作
出回答信号,发送信号如图所示:

步骤三:DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA
引脚处于输出状态,输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接
收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11 回应信号)后,等待 80 微秒
的高电平后的数据接收,发送信号如图所示:
 

步骤四:由 DHT11 的 DATA 引脚输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据,位数据“0”
的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加 70
微秒的高电平。位数据“0”、“1”格式信号如图所示:

(我们可以把这一段的时序理解为,我们主机先把数据线拉低50us,然后延时等待40us,然后再去读取信号线的电平,如果为低电平,则为位“0”;如果为高电平,则为位“1”)。

结束信号:DHT11 的 DATA 引脚输出 40 位数据后,继续输出低电平 50 微秒后转为输入状态,由于上拉电阻随
之变为高电平。但 DHT11 内部重测环境温湿度数据,幵记录数据,等待外部信号的到来。

我们在数据手册上了解的就这么多就可以了。

 

同时,我自己也对DHT11的时序做了一个总结

一. 单片机上点后1s内不读取(不重要)

二. 主机(单片机)发送起始信号:1.主机先拉高data。2.拉低data延迟18ms。
                                3.拉高data(单片机引脚设置为输入)。
                                
三. 从机(DHT11)收到起始信号后进行应答:
        从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线,
        持续80us,直到高电平结束,意味着主机可以开始接受数据。
       
四. 主机开始接收数据:
        1.主机先把data线拉高(io设置为输入)。
        2.从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
        从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线
        电平,如果为低电平,则为“0”,如果为高电平,则为“1”。
        3.继续重复上述1,2步骤累计40次。

五. data线拉低50us代表读取结束

六. 校验数据

 

那么我们在程序上应该如何设计呢?(这里我的程序是基于stm32微处理器来讲解的,其他单片机也一样的操作,时序都是相同的)

 

准备阶段我们先要有3个函数,数据引脚初始化函数,还有数据引脚切换输入输出方向的函数。

void dht11_init (void )
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Enable  clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  , ENABLE);
  
  /* Configure Ports */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_SetBits(GPIOA, GPIO_Pin_7);
}

void mode_input(void )
{
  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void mode_output(void )
{
  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

接下来我们就再写一个函数,来读取dht11数据即可

unsigned int dht11_read(void)
{
  int i;
  long long val;
  int timeout;

  GPIO_ResetBits(GPIOA, GPIO_Pin_7);
  delay_us(18000);  //pulldown  for 18ms
  GPIO_SetBits(GPIOA, GPIO_Pin_7);
  delay_us( 20 );	//pullup for 30us

  mode_input();

  //等待dht11拉高80us
  timeout = 5000;
  while( (! GPIO_ReadInputDataBit  (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--;	 //wait HIGH

  //等待dht11拉低80us
  timeout = 5000;
  while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ;	 //wait LOW

#define CHECK_TIME 28

  for(i=0;i<40;i++)
  {
	timeout = 5000;
	while( (! GPIO_ReadInputDataBit  (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--;	 //wait HIGH

	delay_us(CHECK_TIME);
	if ( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) )
	{
	  val=(val<<1)+1;
	} else {
	  val<<=1;
	}

	timeout = 5000;
	while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ;	 //wait LOW
  }

  mode_output();
  GPIO_SetBits(GPIOA, GPIO_Pin_7);

  if (((val>>32)+(val>>24)+(val>>16)+(val>>8) -val ) & 0xff  ) return 0; //校验
    else return val>>8; 

}

只要按照上述的时序步骤来操作,就能够读取出DHT11的温湿度值啦。

同时我们要注意,只有读出来的数据校验通过了,我们才使用这一次的温湿度数据。

还有他读取出来40位数据的数据结构: 8位湿度整数数据+8位湿度小数数据+8位温度整数数据+8位温度小数数据+8位校验位

基于温度湿度一体的传感器DHT11 以下是在51单片机上测试成功的代码 #include<at89x52.h> #include<intrins.h>//加上这句下面的 _nop_();就能用 bit xianshiqiehuan; // sbit dht11_dat=P1^6; //开发板用 sbit dht11_dat=P2^0; //使用版用 unsigned char c,count, dht11temp,dht11dat; unsigned char dht11value[5]; unsigned int x,y,z; unsigned char code dat[]={ 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,}; delay() { unsigned char a; for(a=200;a>0;a--); } display(unsigned char x) //使用版用 { P0=dat[(x0)/10];//十位 P2_3=0; delay(); P2_3=1; P0=dat[(x0)];//个位 P2_2=0; delay(); P2_2=1; } /*display(unsigned char x) //开发板用 { P0=dat[(x0)/10];//十位 P1_2=0; delay(); P1_2=1; P0=dat[(x0)];//个位 P1_3=0; delay(); P1_3=1; } */ delay_1s() { unsigned int i=50000; while(i--); } delay_10us() //10us { _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); } void delayms(unsigned char x) //1ms单位延时程序 { unsigned char j; while(x--) { for(j=0;j<123;j++){;} } } read_dht11() { unsigned char i; dht11_dat=1; _nop_(); //起始 dht11_dat=0;//拉低总线 delayms(18);//手册要求大于18ms dht11_dat=1;//拉高总线等待dht11回应 while(dht11_dat); // 等待dht11回应 若有回应 dht11_dat=0;往下执行 while(!dht11_dat);//回应后dht11将总线拉低80us,过后又将总线拉高,进入下一步 while(dht11_dat); //拉高80us 又变低,往下执行 进入50us延时 for(i=0;i<24;i++) { while(!dht11_dat);//50us过后...... dht11_dat=1;往下执行 delay_10us();delay_10us();delay_10us();//延时30us,查看总线是高是低, dht11temp=0; //先默认为0处理 if(dht11_dat) dht11temp=1; //1处理 dht11dat=dht11dat<<1; //必须先移动再或 若先或再移本次数据就移动了 dht11dat=dht11dat|dht11temp; dht11value[i/8]=dht11dat; while(dht11_dat);//如果处理的是1,30us过后总线还是1,那就在此等待总线变为0进入下一个50us低电平,不然会重复进行0处理 } } main() { delay_1s(); //要求上电等1秒,让dht11稳定 EA=1;//开放中断 TMOD=0x01;//设T0 为16位计数方式 ET0=1;//定时0中断允许 TR0=1;//开启TR0 while(1) { if(!xianshiqiehuan) //显示温度 display(dht11value[2]) ; else {display(dht11value[0]) ; //显示湿度 P0=0x92&0x7f; //千位显 S.代表湿度 P2_5=0; delay(); P2_5=1; } } } dingshi() interrupt 1 //定时器0服务程序 { TH0=0; TL0=0; count++; if(count==55){count=0;read_dht11();xianshiqiehuan=~xianshiqiehuan; } //在切换显示时采集,以防中断采样带来的显示闪烁 }
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fei...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值