趁热打铁,接上篇DS18B20的介绍再出一篇关于单总线传感器DHT11的介绍,相比之下,DHT11的时序和操作更简单易懂。
DHT11 是广州奥松有限公司生产的一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。 DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。 DHT11 功耗很低, 5V 电源电压下,工作平均最大电流 0.5mA。性能指标和特性如下:
■ 工作电压范围: 3.5V-5.5V
■ 工作电流 :平均 0.5mA
■ 湿度测量范围:20-90%RH
■ 温度测量范围:0-50℃
■ 湿度分辨率 : 1%RH 8 位
■ 温度分辨率 : 1℃ 8 位
■ 采样周期 : 1S
■ 单总线结构
■ 与 TTL 兼容(5V)
DHT11 数字湿温度传感器连接方法极为简单。第一脚接电源正,第四脚接电源地端。数据端为第二脚。可直接接主机(单片机)的 I/O 口。为提高稳定性,建议在数据端和电源正之间接一只 4.7K 的上拉电阻。第三脚为空脚,此管脚悬空不用。
主机拉低信号维持18ms后释放总线(变为高电平),20-40us后,DHT11会将总线拉低维持40-50微秒再将总线拉高维持40-50微秒,主机在对应时间段内依次检测到低电平信号和高电平信号,表示有从机在位,否则从机不在位。
以12-14微秒的低电平作为始,随之而来的高电平信号维持26-28微秒后拉低,此时序表示信号 0
同样以12-14微秒的低电平作为始,随之而来的高电平信号维持116-118微秒后拉低,此时序表示信号 1
总结来讲就是看高电平时间维持的长短,可使用while循环等待电平信号是否为低,循环依次延时1us,计数器+1,如果跳出循环则判断计数器数值,小于30则为0,大于30且小于120则为1,大于120则为错误。
DHT11数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由5Byte( 40Bit)组成。数据分小数部分和整数部分,具体格式在下面说明。
一次完整的数据传输为40bit,高位先出。
数据格式: 8bit湿度整数数据+8bit湿度小数数据
+8bit温度整数数据+8bit温度小数数据
+8bit校验和
校验和数据为前四个字节相加。
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。如果,某次从传感器中读取如下5Byte数据:
由以上数据就可得到湿度和温度的值,计算方法:
humi (湿度)= byte4 . byte3=45.0 (%RH)
temp (温度)= byte2 . byte1=28.0 ( ℃ )
jiaoyan(校验)= byte4+ byte3+ byte2+ byte1=73(=humi+temp)(校验正确)
注意: DHT11一次通讯时间最大3ms,主机连续采样间隔建议不小于100ms。
#ifndef __DHT11_H
#define __DHT11_H
#include "stdio.h"
#include "Config.h" //包含初始化GPIO,初始化时钟
#include "SysTick.h"
/*******************************************
*DHT11 devier
********************************************/
#define DHT11_IS_READY (1u)
#define DHT11_NOT_READY (0u)
#define DHT11_DQ_IN() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}//PG9 输入模式
#define DHT11_DQ_OUT() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}//PG9 输出模式
#define DHT11_DQ_PORT GPIOG
#define DHT11_DQ_PIN GPIO_Pin_9
#define DHT11_DQ_LOW GPIO_ResetBits(DHT11_DQ_PORT,DHT11_DQ_PIN)
#define DHT11_DQ_HIGH GPIO_SetBits(DHT11_DQ_PORT,DHT11_DQ_PIN)
#define DHT11_DQ_STATUS GPIO_ReadInputDataBit(DHT11_DQ_PORT,DHT11_DQ_PIN) //读取DQ状态
#define DHT11_delay_us(a) SysCtlDelayus(a) //延时函数us
#define DHT11_delay_ms(a) SysCtlDelayus(a * 1000) //延时函数ms
#define DHT11_ACK_TIMEOUT (uint8_t)100 //等待DHT11响应信号延时
#define DHT11_BIT_START_TIMEOUT (uint8_t)100 //1bit起始信号延时时间
#define DHT11_BIT_STATUS_TIMEOUT (uint8_t)130 //表示 0 的延时时间
typedef enum
{
DHT11_1 = 0,
DHT11_2,
DHT11_3,
DHT11_4,
DHT11_Num_Counter
}DHT11_Num;
typedef struct
{
uint8_t DHT11_IndexNumber;
float DHT11_Temp;
float DHT11_Humi;
}DHT11_Data_Type;
extern uint8_t DHT11_Init(void);
extern void DHT11_Reset(void);
extern uint8_t DHT11_GetTemp_Main(void);
extern float DHT11_Get_Temperature(uint8_t Index);
extern float DHT11_Get_Humidity(uint8_t Index);
#endif
## 1.时序
.c文件:
#ifndef __DHT11_H
#define __DHT11_H
#include "stdio.h"
#include "Config.h" //包含初始化GPIO,初始化时钟
#include "SysTick.h"
/*******************************************
*DHT11 devier
********************************************/
#define DHT11_IS_READY (1u)
#define DHT11_NOT_READY (0u)
#define DHT11_DQ_IN() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}//PG9 输入模式
#define DHT11_DQ_OUT() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}//PG9 输出模式
#define DHT11_DQ_PORT GPIOG
#define DHT11_DQ_PIN GPIO_Pin_9
#define DHT11_DQ_LOW GPIO_ResetBits(DHT11_DQ_PORT,DHT11_DQ_PIN)
#define DHT11_DQ_HIGH GPIO_SetBits(DHT11_DQ_PORT,DHT11_DQ_PIN)
#define DHT11_DQ_STATUS GPIO_ReadInputDataBit(DHT11_DQ_PORT,DHT11_DQ_PIN) //读取DQ状态
#define DHT11_delay_us(a) SysCtlDelayus(a) //延时函数us
#define DHT11_delay_ms(a) SysCtlDelayus(a * 1000) //延时函数ms
#define DHT11_ACK_TIMEOUT (uint8_t)100 //等待DHT11响应信号延时
#define DHT11_BIT_START_TIMEOUT (uint8_t)100 //1bit起始信号延时时间
#define DHT11_BIT_STATUS_TIMEOUT (uint8_t)130 //表示 0 的延时时间
typedef enum
{
DHT11_1 = 0,
DHT11_2,
DHT11_3,
DHT11_4,
DHT11_Num_Counter
}DHT11_Num;
typedef struct
{
uint8_t DHT11_IndexNumber;
float DHT11_Temp;
float DHT11_Humi;
}DHT11_Data_Type;
extern uint8_t DHT11_Init(void);
extern void DHT11_Reset(void);
extern uint8_t DHT11_GetTemp_Main(void);
extern float DHT11_Get_Temperature(uint8_t Index);
extern float DHT11_Get_Humidity(uint8_t Index);
#endif
#include "DHT11.h"
//#define DHT11_MORE_THAN_ONE
DHT11_Data_Type DHT11_Temp[DHT11_Num_Counter] =
{
{DHT11_1,0.0,0.0},
{DHT11_2,0.0,0.0},
{DHT11_3,0.0,0.0},
{DHT11_4,0.0,0.0}
};
static uint8_t DHT11_Read_Bit(void);
static uint8_t DHT11_Read_Byte(void);
static uint8_t DHT11_Check(void);
/************************************************************************************
*@fuction :DHT11_Init
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
extern uint8_t DHT11_Init(void)
{
DHT11_Reset();
if(DHT11_Check() == DHT11_IS_READY)
{
return DHT11_IS_READY;
}
}
/************************************************************************************
*@fuction :DHT11_Reset
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
void DHT11_Reset(void)
{
//DHT11 复位时序:DQ输出模式 DQ = 0(20us), DQ = 1(30us.
DHT11_DQ_OUT();
DHT11_DQ_LOW;
DHT11_delay_ms(20);
DHT11_DQ_HIGH;
DHT11_delay_us(30);
}
/************************************************************************************
*@fuction :DHT11_Check
*@brief :
*@param :--
*@return :1-device ok/0-device error
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
uint8_t DHT11_Check(void)
{
uint8_t wait_time = 0;
uint8_t Ready_Dev = 0;
//DQ输入模式
DHT11_DQ_IN();
//等待DQ脚被DHT11拉低
while((DHT11_DQ_STATUS) && (wait_time < DHT11_ACK_TIMEOUT))
{
wait_time++;
DHT11_delay_us(1);
};
if(wait_time >= DHT11_ACK_TIMEOUT)
{
//如果等待时间超时,则退出等待
return (uint8_t)DHT11_NOT_READY;
}
else
{
wait_time = 0;
while((!DHT11_DQ_STATUS) && (wait_time < DHT11_ACK_TIMEOUT))
{
wait_time++;
DHT11_delay_us(1);
};
if(wait_time >= DHT11_ACK_TIMEOUT)
{
//如果等待时间超时,则退出等待
return (uint8_t)DHT11_NOT_READY;
}
else
{
//如果未超时,则说明设备存在
return (uint8_t)DHT11_IS_READY;
}
}
}
/************************************************************************************
*@fuction :DHT11_Read_Bit
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
static uint8_t DHT11_Read_Bit(void) //read one bit
{
uint8_t wait_time = 0;
uint8_t Bit_Status = 0;
DHT11_DQ_IN();
//等待DQ脚被DHT11拉低
while((DHT11_DQ_STATUS) && (wait_time < 100))
{
wait_time++;
DHT11_delay_us(1);
}
if(wait_time >= 100)
{
//如果等待时间超时,则出现错误,退出等待
Bit_Status = 0xFF;
}
else
{
wait_time = 0;
//等待DQ脚被DHT11抬高
while((!DHT11_DQ_STATUS) && (wait_time < DHT11_BIT_START_TIMEOUT))
{
wait_time++;
DHT11_delay_us(1);
}
if(wait_time >= DHT11_BIT_START_TIMEOUT)
{
//如果等待时间超时,则退出等待
Bit_Status = 0xFF;
}
else
{
//如果未超时,则说明1bit起始信号正常,
wait_time = 0;
//进入高电平时长判断
while((DHT11_DQ_STATUS) && (wait_time < DHT11_BIT_STATUS_TIMEOUT))
{
wait_time++;
DHT11_delay_us(1);
}
//如果高电平保持时间小于50us则为0,大于50us则为1
if(wait_time <= 50)
{
Bit_Status = 0;
}
else if((50 < wait_time) && (wait_time <= 150))
{
Bit_Status = 1;
}
else
{
Bit_Status = 0xFF;
}
}
}
return Bit_Status;
}
/************************************************************************************
*@fuction :DHT11_Read_Byte
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
static uint8_t DHT11_Read_Byte(void)
{
uint8_t i = 0 ,Bit_Status = 0,aByte = 0;
//DQ为输入模式
DHT11_DQ_IN();
for (i = 0; i < 8; i++)
{
//高位先出
Bit_Status = DHT11_Read_Bit();
aByte = (aByte << 1) | Bit_Status;
}
return aByte;
}
/************************************************************************************
*@fuction :DHT11_CheckSumFun
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
uint8_t DHT11_CheckSumFun(uint8_t* MsgArr, uint8_t Lengh)
{
uint8_t CheckSum = 0;
uint8_t i = 0;
if((MsgArr == NULL_Ptr) && (Lengh == NULL))
{
return CheckSum;
}
for(i = 0; i < Lengh; i++)
{
CheckSum = (CheckSum + MsgArr[i]) % 256;
}
return CheckSum;
}
/************************************************************************************
*@fuction :SplicingFloat
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
float SpliceFloat(uint8_t Integer, uint8_t Decimal)
{
float Temp = Integer;
if(Decimal & 0x80)
{
Temp = -1 - Temp;
}
Temp += (Decimal & 0x0f) * 0.1;
return Temp;
}
/************************************************************************************
*@fuction :DHT11_GetTemp_Main
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
extern uint8_t DHT11_GetTemp_Main(void)
{
uint8_t i = 0,temp[5];
DHT11_Reset();
if(DHT11_Check() == DHT11_IS_READY)
{
for(i = 0;i < 5;i++)
{
temp[i] = DHT11_Read_Byte();
}
if(DHT11_CheckSumFun(temp,4) == temp[4])
{
DHT11_Temp[0].DHT11_Humi = SpliceFloat(temp[0],temp[1]);
DHT11_Temp[0].DHT11_Temp = SpliceFloat(temp[2],temp[3]);
return E_OK;
}
}
}
/************************************************************************************
*@fuction :DHT11_Get_Temp
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
extern float DHT11_Get_Temperature(uint8_t Index)
{
return DHT11_Temp[Index].DHT11_Temp;
}
/************************************************************************************
*@fuction :DHT11_Get_Temp
*@brief :
*@param :--
*@return :void
*@author :_Awen
*@date :2022-12-04
************************************************************************************/
extern float DHT11_Get_Humidity(uint8_t Index)
{
return DHT11_Temp[Index].DHT11_Humi;
}
注意:
1.硬件电路中DQ脚外部会加上拉电阻,主机释放总线会被上拉电阻自动上拉,但为保险器件我们将主机释放中线写为主动上拉为高电平
2. .h文件中对延时函数DS18B20_delay_us(a)的定义,关于 SysCtlDelayus(a)实际是ARM汇编的一种延时函数的写法,参见另一篇关于延时函数的博客(汇编延时)https://blog.csdn.net/Yin_w/article/details/130036593?spm=1001.2014.3001.5501