基于stm32的智能时钟的实践(3) --- 温湿度的测量

1 前言

在我们日常的生活环境中,对于温度和湿度的测量很是需要

所以这次我们使用DHT11这个传感器来实现温湿度的测量

2 DHT11的介绍

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性和卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,使其成为该类应用中,在苛刻应用场合的最佳选择。产品为4针单排引脚封装,连接方便。

 可以看到,这款DHT11的封装很是好看.

DHT11的详细参数如下所示

 3 DHT11串行通信说明(单线双向)

3.1 单总线说明

DHT11器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总
线完成。设备(主机或从机)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 4.7kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。

3.2 单总线传送数据位定义

DATA用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式,一次传送40位数据,
高位先出。
数据格式:
8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验位。
注意:其中湿度小数部分为0。

3.3 数据处理

“8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据”
8bit校验位等于所得结果的末8位。
下面是数据的处理举例

在处理数据的时候,我们要注意一些情况.

例如,我们的湿度的小数位永远都为0,所以湿度打印的时候不需要小数位.

再比如,温度也有细节,就是温度有负数哦,在一些偏北方的地区,在春冬季节经常都是零下,那么这款DHT11如何来测量零下的温度呢? 

 

 可以看到,其实处理起来也并不复杂,在零下的温度下面DHT11发送数据的温度的低八位的最高位会是1,我们只需要检测一下最高位是否为1,就能判断测量的温度是否为负数了.

如下是我写的相关代码,仅供参考

        DHT11_Data->humi_high8bit = DHT11_ReadByte();
		DHT11_Data->humi_low8bit = DHT11_ReadByte();
		DHT11_Data->temp_high8bit= DHT11_ReadByte();
		DHT11_Data->temp_low8bit = DHT11_ReadByte();
		DHT11_Data->check_sum    = DHT11_ReadByte();
		
		/*读取结束,引脚改为输出模式*/
		DHT11_Mode_Out_PP();
		/*主机拉高*/
		DHT11_Dout_HIGH();
		
		/* 对数据进行处理 */
		humi_temp=DHT11_Data->humi_high8bit*10+DHT11_Data->humi_low8bit;
		DHT11_Data->humidity =(float)humi_temp/10;
		
		if(((DHT11_Data->temp_low8bit)>>7) == 1)
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=-((float)humi_temp/10); 
		}
		else
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=(float)humi_temp/10;    
		}

最后就是关于校验位的处理

		/*检查读取的数据是否正确*/
		temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit + 
			   DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
	    
		if(DHT11_Data->check_sum==temp)
		{ 
		  return 1;
		}
		else 
		  return 0;

 很简单吧,其实就是对传过来的40位数据根据手册上面的说明及举例顺水推舟.

4 数据时序及读取步骤

 4.1 读取DHT11 40位数据的时序

 可以看到,从机(DHT11)发送信号的时候,会先发送低电平大约54us,然后拉高,发送高电平时间大约在23-27us确定从机发送的是低电平,在大约68-74us发送的是高电平.

根据这个规则,代码如下,仅供参考

/**
  * 函数功能: 从DHT11读取一个字节
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
uint8_t DHT11_ReadByte ( void )
{
	uint8_t i, temp=0;
	
	for(i=0;i<8;i++)    
	{	 
  
		while(DHT11_Data_IN()==Bit_RESET);
		
		Delay_us(40); 	   	  

		if(DHT11_Data_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
		{
			
			/* 等待数据1的高电平结束 */
			while(DHT11_Data_IN()==Bit_SET);
			
			temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1 
		}
		else	 // x us后为低电平表示数据“0”
		{			
			temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0
		}
	}
	return temp;
}

这个函数实现了接受从机的1个字节的信号

4.2 整体步骤

如下就是整体的时序图

可以看出,大体的步骤就是主机先发,从机确认后从机准备发送,主机有确认好之后发送40位数据,发送玩之后就释放总线结束. 

具体下来

步骤一:
DHT11上电后(DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令),测
试环境温湿度数据,并记录数据,同时DHT11的DATA数据线由上拉电阻拉高一直保持高电平;
此时DHT11的DATA引脚处于输入状态,时刻检测外部信号。
步骤二:
微处理器的I/O设置为输出同时输出低电平,且低电平保持时间不能小于18ms(最大不得
超过30ms),然后微处理器的I/O设置为输入状态,由于上拉电阻,微处理器的I/O即DHT11的
DATA数据线也随之变高,等待DHT11作出回答信号。
步骤三:
DHT11的DATA引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后DHT11的
DATA引脚处于输出状态,输出83微秒的低电平作为应答信号,紧接着输出87微秒的高电平通知
外设准备接收数据,微处理器的I/O此时处于输入状态,检测到I/O有低电平(DHT11回应信号)
后,等待87微秒的高电平后的数据接收.
步骤四:
由DHT11的DATA引脚输出40位数据,微处理器根据I/O电平的变化接收40位数据,位数据
“0”的格式为:54微秒的低电平和23-27微秒的高电平,位数据“1”的格式为:54微秒的低
电平加68-74微秒的高电平。
具体代码如下,仅供参考
/**
  * 函数功能: 一次完整的数据传输为40bit,高位先出
  * 输入参数: DHT11_Data:DHT11数据类型
  * 返 回 值: 0:  读取出错
  *           1:读取成功
  * 说    明:8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
  */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{  
  uint8_t temp;
  uint16_t humi_temp;
  
	/*输出模式*/
	DHT11_Mode_Out_PP();
	/*主机拉低*/
	DHT11_Dout_LOW();
	/*延时18ms*/
	Delay_ms(18);

	/*总线拉高 主机延时30us*/
	DHT11_Dout_HIGH(); 

	Delay_us(30);   //延时30us

	/*主机设为输入 判断从机响应信号*/ 
	DHT11_Mode_IPU();
	
	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
	if(DHT11_Data_IN()==Bit_RESET)     
	{
		
		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
		while(DHT11_Data_IN()==Bit_RESET);

		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
		while(DHT11_Data_IN()==Bit_SET);
		
		/*开始接收数据*/   
		DHT11_Data->humi_high8bit = DHT11_ReadByte();
		DHT11_Data->humi_low8bit = DHT11_ReadByte();
		DHT11_Data->temp_high8bit= DHT11_ReadByte();
		DHT11_Data->temp_low8bit = DHT11_ReadByte();
		DHT11_Data->check_sum    = DHT11_ReadByte();
		
		/*读取结束,引脚改为输出模式*/
		DHT11_Mode_Out_PP();
		/*主机拉高*/
		DHT11_Dout_HIGH();
		
		/* 对数据进行处理 */
		humi_temp=DHT11_Data->humi_high8bit*10+DHT11_Data->humi_low8bit;
		DHT11_Data->humidity =(float)humi_temp/10;
		
		if(((DHT11_Data->temp_low8bit)>>7) == 1)
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=-((float)humi_temp/10); 
		}
		else
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=(float)humi_temp/10;    
		}
		/*检查读取的数据是否正确*/
		temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit + 
			   DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
	    
		if(DHT11_Data->check_sum==temp)
		{ 
		  return 1;
		}
		else 
		  return 0;
	}	
	else
		return 0;
}

5 总结

最后的实验结果如下

这一次,实现了对温湿度数据的读取,但是,我们这毕竟是一个智能时钟的项目啊,总不能通过串口将数据显示到电脑上面来吧,所以,我在下一次的实践中,将会实现在数码管上面的实现.实现很简单,但是其底层的原理也需要研究,我将使用的是TM1638,该芯片可以控制数码管,LED,还有按键,我将会在下一次深入研究. 

下面是dht11.c和dht11.h

#include "Device/Include/stm32f10x.h"   // Device header
#include "dht11.h"
#include "delay.h"
#include "LED.h"

void DHT11_Mode_IPU(void);
void DHT11_Mode_Out_PP(void);
uint8_t DHT11_ReadByte(void);


/**
  * 函数功能: DHT11 初始化函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void DHT11_Init ( void )
{
	DHT11_Dout_GPIO_CLK_ENABLE();
  
	DHT11_Mode_Out_PP();
	
	DHT11_Dout_HIGH();  // 拉高GPIO
}

/**
  * 函数功能: 使DHT11-DATA引脚变为上拉输入模式
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void DHT11_Mode_IPU(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  GPIO_InitStruct.GPIO_Pin = DHT11_Dout_PIN;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DHT11_Dout_PORT, &GPIO_InitStruct);
	
}

/**
  * 函数功能: 使DHT11-DATA引脚变为推挽输出模式
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void DHT11_Mode_Out_PP(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
 
  GPIO_InitStruct.GPIO_Pin = DHT11_Dout_PIN;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(DHT11_Dout_PORT, &GPIO_InitStruct); 	 
}

/**
  * 函数功能: 从DHT11读取一个字节
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
uint8_t DHT11_ReadByte ( void )
{
	uint8_t i, temp=0;
	
	for(i=0;i<8;i++)    
	{	 
  
		while(DHT11_Data_IN()==Bit_RESET);
		
		Delay_us(40); 	   	  

		if(DHT11_Data_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
		{
			
			/* 等待数据1的高电平结束 */
			while(DHT11_Data_IN()==Bit_SET);
			
			temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1 
		}
		else	 // x us后为低电平表示数据“0”
		{			
			temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0
		}
	}
	return temp;
}

/**
  * 函数功能: 一次完整的数据传输为40bit,高位先出
  * 输入参数: DHT11_Data:DHT11数据类型
  * 返 回 值: 0:  读取出错
  *           1:读取成功
  * 说    明:8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
  */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{  
  uint8_t temp;
  uint16_t humi_temp;
  
	/*输出模式*/
	DHT11_Mode_Out_PP();
	/*主机拉低*/
	DHT11_Dout_LOW();
	/*延时18ms*/
	Delay_ms(18);

	/*总线拉高 主机延时30us*/
	DHT11_Dout_HIGH(); 

	Delay_us(30);   //延时30us

	/*主机设为输入 判断从机响应信号*/ 
	DHT11_Mode_IPU();
	
	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
	if(DHT11_Data_IN()==Bit_RESET)     
	{
		
		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
		while(DHT11_Data_IN()==Bit_RESET);

		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
		while(DHT11_Data_IN()==Bit_SET);
		
		/*开始接收数据*/   
		DHT11_Data->humi_high8bit = DHT11_ReadByte();
		DHT11_Data->humi_low8bit = DHT11_ReadByte();
		DHT11_Data->temp_high8bit= DHT11_ReadByte();
		DHT11_Data->temp_low8bit = DHT11_ReadByte();
		DHT11_Data->check_sum    = DHT11_ReadByte();
		
		/*读取结束,引脚改为输出模式*/
		DHT11_Mode_Out_PP();
		/*主机拉高*/
		DHT11_Dout_HIGH();
		
		/* 对数据进行处理 */
		humi_temp=DHT11_Data->humi_high8bit*10+DHT11_Data->humi_low8bit;
		DHT11_Data->humidity =(float)humi_temp/10;
		
		if(((DHT11_Data->temp_low8bit)>>7) == 1)
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=-((float)humi_temp/10); 
		}
		else
		{
			humi_temp=DHT11_Data->temp_high8bit*10+DHT11_Data->temp_low8bit;
			DHT11_Data->temperature=(float)humi_temp/10;    
		}
		/*检查读取的数据是否正确*/
		temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit + 
			   DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
	    
		if(DHT11_Data->check_sum==temp)
		{ 
		  return 1;
		}
		else 
		  return 0;
	}	
	else
		return 0;
}

#ifndef __DHT11_H__
#define __DHT11_H__

#include "Device/Include/stm32f10x.h"   // Device header

typedef struct
{
	uint8_t  humi_high8bit;		//原始数据:湿度高8位
	uint8_t  humi_low8bit;	 	//原始数据:湿度低8位
	uint8_t  temp_high8bit;	 	//原始数据:温度高8位
	uint8_t  temp_low8bit;	 	//原始数据:温度高8位
	uint8_t  check_sum;	 	    //校验和
  float    humidity;        //实际湿度
  float    temperature;     //实际温度  
} DHT11_Data_TypeDef;

/* 宏定义 -------------------------------------------------------------------*/
/***********************   DHT11 连接引脚定义  **************************/
#define DHT11_Dout_GPIO_CLK_ENABLE()              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE)
#define DHT11_Dout_PORT                           GPIOD
#define DHT11_Dout_PIN                            GPIO_Pin_2

/***********************   DHT11 函数宏定义  ****************************/
#define DHT11_Dout_LOW()                          GPIO_WriteBit(DHT11_Dout_PORT,DHT11_Dout_PIN,Bit_RESET) 
#define DHT11_Dout_HIGH()                         GPIO_WriteBit(DHT11_Dout_PORT,DHT11_Dout_PIN,Bit_SET)
#define DHT11_Data_IN()	                          GPIO_ReadInputDataBit(DHT11_Dout_PORT,DHT11_Dout_PIN)

/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void DHT11_Init( void );
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef * DHT11_Data);


#endif

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值