DHT11 温湿度传感器的数据读取一般分为 四个步骤,下面详细介绍每个步骤的具体内容:
步骤一:主机发送起始信号
-
主机(如 MCU)主动向 DHT11 发送开始信号,方式为:
-
将数据线拉低 至少 18ms(确保 DHT11 能够识别这是一个起始信号);
-
然后拉高数据线 20~40μs;
-
-
这个动作通知 DHT11 准备发送数据。
步骤二:DHT11 发出响应信号
-
接收到主机的起始信号后,DHT11 做出响应:
-
首先将数据线拉低 80μs;
-
然后拉高数据线 80μs;
-
-
表示 DHT11 已准备好传输数据。
步骤三:DHT11 传输40位数据
-
DHT11 按顺序传送 40 位数据(高位先传),格式如下:
8位湿度整数 + 8位湿度小数 + 8位温度整数 + 8位温度小数 + 8位校验和
-
每一位的传输方式:
-
逻辑"0":拉高约 26~28μs;
-
逻辑"1":拉高约 70μs。
-
起始位:先拉低数据线约 50μs;
-
数据位:
-
步骤四:主机校验数据完整性
-
主机接收完 40 位数据后,将前四个字节相加(无进位):
校验 = 湿度整数 + 湿度小数 + 温度整数 + 温度小数
-
与第五字节(校验和)进行比较,验证数据是否正确;
-
若相符,说明读取成功。
在DHT11.c文件中
#include "DHT11.h"#include "delay.h"
//初始化DHT11_Data引脚void DHT11_Init(){ ///因为PB3是不可用的,所以我们先解除把不可用
//打开GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_PinRemapConfig (GPIO_Remap_SWJ_JTAGDisable , ENABLE); //初始化GPIO口为开漏输出
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init (GPIOA, &GPIO_InitStruct);//打开GPIOA时钟
DHT11_H; //初始化为高电平 delay_ms(1000); //进行一个延时,目的越过模块不稳定期
}
//主机发送起始信号void Start(){ DHT11_L; delay_ms(20);
DHT11_H // 释放总线,等待从机响应
}
//响应函数,就是主机读取IO口,是否为在指定电平,判断响应完成//比如返回0,表示响应成功u8 Respond(){ u8 time_out = 0; while(DHT11_R == 1)// 等待主机响应 { time_out++; delay_us(1); if(time_out > 50) //说明等待时间超长,没有等到应答信号 { return 1;
}
} time_out =0; while(DHT11_R == 0) // 开始响应 { time_out++; if(time_out > 100) //等待时间>100 { return 2;
}
} if(time_out < 50) //响应时间过短 { return 3;
}
while(DHT11_R == 1) //从机等待主机准备
return 0;
}
//读取DHT1140位数据
void DHT11_Read(u8 *pData){ u8 i,j; for(i = 0 ; i<5;i++) { for(j = 0;j<8;j++) { while(DHT11_R == 0); delay_us(50);
pData[i] <<= 1; if(DHT11_R == 1) { pData[i] |= 1; while(DHT11_R == 1); }
}
}
}
//获取温湿度数据
u8 DHT11_Get(u8 *shi,u8 * temp){ u8 ret; u8 datas[5];
Start(); ret = Respond(); if(ret ==1) { return 1; //相应失败
} DHT11_Read(datas); if( (datas[0]+ datas[1] + datas[2] + datas[3]) != datas[4]) { return 2 ;// 接收数据错误 } *shi = datas[0] + datas[1]/10; if(datas[3] & 0x80) //判断温度为负数 { *temp = (datas[2] + (datas[3]& 0x0F) / 10 )* -1;
} else{
*temp = datas[2] + datas[3]/10; } return 0;
}
重点讲一下,DHT11获取函数和DHT11读取函数
一、读取 DHT11 的 40 位数据:DHT11_Read(u8 *pData)
一、读取 DHT11 的 40 位数据:DHT11_Read(u8 *pData)
void DHT11_Read(u8 *pData){ u8 i, j; for(i = 0; i < 5; i++) // 总共要读取 5 个字节(8 位 * 5 = 40 位) { for(j = 0; j < 8; j++) // 每个字节 8 位 { while(DHT11_R == 0); // 等待 DHT11 拉高,表示开始发送这一位的高电平部分 delay_us(50); // 延时 50us,用于判断当前是 0 还是 1 pData[i] <<= 1; // 左移一位,为当前位腾出位置 if(DHT11_R == 1) // 如果此时仍然为高电平,则是"1" { pData[i] |= 1; // 将当前最低位置 1 while(DHT11_R == 1); // 等待高电平结束 } // 如果延时后是低电平,就什么都不做,当前位默认是 0 } }}🔍 核心原理解释:DHT11 发送每一位数据时,先是一个固定的低电平(约 50μs),然后是一个高电平:高电平持续 26~28μs 代表 0;高电平持续 70μs 代表 1;所以主机延时 50μs 后读取数据线状态,来判断是 0 还是 1。
二、获取温湿度数据:DHT11_Get(u8 *shi,u8 *temp)
u8 DHT11_Get(u8 *shi, u8 *temp){ u8 ret; u8 datas[5]; Start(); // 主机发送起始信号 ret = Respond(); // 等待 DHT11 响应 if(ret == 1) { return 1; // 响应失败 } DHT11_Read(datas); // 读取 40 位数据到 datas[0] ~ datas[4] // 校验和验证数据完整性 if ((datas[0] + datas[1] + datas[2] + datas[3]) != datas[4]) { return 2; // 校验失败,数据错误 } // 获取湿度,datas[0]是整数部分,datas[1]是小数部分 *shi = datas[0] + datas[1] / 10; // 获取温度,datas[2]是整数部分,datas[3]是小数部分 if(datas[3] & 0x80) // 判断是否为负温(DHT11 其实不支持负温度,但兼容设计) { *temp = (datas[2] + (datas[3] & 0x0F) / 10) * -1; } else { *temp = datas[2] + datas[3] / 10; } return 0; // 读取成功}🔍 注意点:Start() 和 Respond() 是发送起始信号和等待响应,属于步骤 1 和 2;校验和确保数据的可靠性;DHT11 实际只返回 整数值,小数部分一般为 0,设计保留是为了与 DHT22 等更高精度传感器兼容;datas[3] & 0x80 判负值是为兼容 DHT12 或 DHT22 数据格式,DHT11 实际温度是非负的,所以这段代码更具有通用性。
在DHT11.h文件
#ifndef _DHT11_H_#define _DHT11_H_
#include "stm32f10x.h"
#define DHT11_H GPIO_SetBits (GPIOB,GPIO_Pin_3);#define DHT11_L GPIO_ResetBits (GPIOB, GPIO_Pin_3)#define DHT11_R GPIO_ReadInputDataBit (GPIOB, GPIO_Pin_3)
void DHT11_Init();u8 DHT11_Get(u8 *shi,u8 * temp);
#endif