【单片机】温湿度传感器 DHT11
时间:2022-08-25 14:30:00
文章目录
- 前言
- 模块介绍(可跳)
- 模块使用
-
- DHT11的参数说明
- 数据包格式
- DHT11传输时序
-
- 发起通信
- DHT11发送0和1时序
- 代码:h文件
- 代码使用方法
- 代码获取
前言
大家好,我是林白柏;
希望看完能有所收获。请纠正不足!
PS:本文提到的所有模块都使用正点原子stm32开发板战舰某宝现成的模块驱动模块。
PS:代码是正原子的代码,整理成自己习惯的界面
模块介绍(可跳过)
模块长这个亚子
DHT11包装了8位MCU,单片机连接一个电阻感湿元件和一个NTC测温元件;
这8位数据的采集和处理都是由这8位数据处理的MCU当我们完成它时,我们只需要使用它MCU读数据;
传感器的外围电路也相对简单,只需要数据线DATA上拉。
模块使用
DHT11的参数说明
- 湿度:20-90%RH;±5%RH
- 温度:0-50℃;±2℃
- 采样间隔:1s
数据包格式
DHT11采用单线通信,数据包长度40bit(5bytes),数据包格式如下:
校验和是前四个字节的累加和,取低八位。
DHT11传输时序
发起通信
主机发送开始信号(降低)DATA线时间大于18ms),DHT11开始信号结束后发送响应信号(降低)DATA线80us),然后延时40-50us然后开始传输数据。
因此,主机发送开始信号后,会io设置为输入,延迟20-40us后检测DATA线电平状态可以知道DHT是否有响应。
对应代码如下
定义了几个宏,便于配置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”的时序
“1”的时序
对应代码如下
从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
的格式如下
因为dht11_data_t
成员变量都是uint8_t [1byte]
,所以为单字节对齐,跟buf
是匹配的,可以直接用memcpy函数
拷贝,存储格式如下
代码: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