DHT11驱动原理
1.1接线:我使用的是ESP32 Wrover,DHT11接线也不复杂,总共三根线,供电电压为3.3V-5V,DATA端就随便接一个IO口即可。
官方使用说明上写着data端上拉5K电阻,而我没有上拉,也能实现数据读取。下面介绍该模块的时序逻辑。
1.2时序图
????????用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主 机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集。这个过程主要分为三大步,分别是①单片机开始信号的发送,②检测DHT11的响应信号,③读取温湿度数据。
????????那要怎样发送开始信号才能让DHT11模块知道该把数据送到单片机呢?主要分下面几步即可完成开始信号的发送:
(1)将data连接的IO口设置为拉低输出状态,这个过程要大于18ms,那么直接采用延时函数延时个二十多秒即可。
(2)在完成第一步之后紧接着把IO口配置为高电平输出,这一步不需要延时处理
(3)完成上面两步后将IO口配置为输入状态,仅仅只是输入,电平状态可以不管,此过程持续20us-40us,我选择延时30us。示例代码如下:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
OutputLow();//低电平输出
Delay_ms(25); //>18MS
OutputHigh();//高电平输出
InputInitial(); //输入
ets_delay_us(30);
?????????下面介绍如何检测DHT11的响应信号,在我们向DHT11发送开始信号后,DHT11就开始回应一个响应信号,这个信号由80us的低电平和80us的高电平构成,响应信号结束就开始发送温湿度数据。那么我们如何让单片机知道什么时候去读数据呢?
?
? ? ? ? (1)那我们就去检测IO口的电平值,响应信号不是先低后高吗,我就先判断DHT11是否拉低了IO口电平,如果我读到的IO口电平为高,那说明DHT11根本就没响应,要么传感器坏了,要么开始信号没发送成功。
? ? ? ? (2)在上一步检测到IO口的低电平之后,我们需要等待响应信号的低电平结束,那么直接用一个while循环,循环中我们不断检测端口电平,一旦端口电平变高就跳出循环。
????????(3)低电平结束之后迎来高电平信号,用同样的方法等待80us的高电平过去,高电平结束再等待个20—40us即可检测温湿度数据
if(!getData())//表示传感器拉低总线
{
ucharFLAG=2;
//等待低电平结束
while((!getData())&&ucharFLAG++)
ets_delay_us(10);
//等待高电平结束
while((getData())&&ucharFLAG++)
ets_delay_us(40);
}
else //没用成功读取,返回0
{
Humi=0,
Temp=0;
printf("ReadError");
}
? ? ? ? 总算来到第三步,开始读数据了,因为DHT11会发送40位的数据。
数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 +8bit校验和
这40位的数据每一位的0/1状态又是怎么确定的呢?下图就是数字0和1的信号表示方法
?????????因为温湿度数据由四个8bit的数据构成,那我们就读每8位读一次,总共读5次,最后一次获取的是校验和。从上图我们可以看出不管是0信号还是1信号都有低电平+高电平表示,而且低电平持续时间相同,都是50us,那么就要求我们从高电平持续的时间来判断0/1信号。
? ? ? ? 具体做法是(1)利用for循环,循环8次,每次读1位数据,每一位数据都由低电平开始,高电平结束。所以我们要等,等这一位数据的低电平部分过去,还是用while循环嵌套电平检测去做,检测到高电平就跳出循环。
????????(2)当检测到这一位数据的高电平后,我们要看这段高电平究竟会持续多长时间,0信号的高电平持续26-28us,1信号则持续70us。所以在上一步检测到高电平之后我们不妨等一等,等个30us看看高电平是不是还是存在,若还是高电平,那这一位信号就是1信号,若已经变低了,就是0信号,将这个1或0赋值给一个变量。
????????(3)上面读到了一位数据,我们要读8次才行,那要怎样存放变量呢,答案是左移一位再或上你刚读到的这一位数据,这样进行8次就能把一个温湿度部分读完。
static void COM(void) // 温湿写入
{
uchar i=0;
for(i=0;i<8;i++)
{
//等待IO口变低,变低后,通过延时去判断是0还是1
while((getData()==0)&&ucharFLAG++) ;
ets_delay_us(35);//延时35us
uchartemp=0;
//如果这个位是1,35us后,还是1,则该位为1
if(getData()==1)
{
uchartemp=0x01;
}
else
{
uchartemp=0x00;
}
ucharcomdata<<=1;//左移1位
ucharcomdata|=uchartemp;//或上刚读到的值
//等待IO口变高,变高后,表示可以读取下一位
while((getData()==1)&&ucharFLAG++)
ets_delay_us(10);
}
}
?其实还有一步,那就是每8位读一次,读5次
COM();//读取第1字节,
ucharRH_data_H_temp=ucharcomdata;
COM();//读取第2字节,
ucharRH_data_L_temp=ucharcomdata;
COM();//读取第3字节,
ucharT_data_H_temp=ucharcomdata;
COM();//读取第4字节,
ucharT_data_L_temp=ucharcomdata;
COM();//读取第5字节,
ucharcheckdata_temp=ucharcomdata;
OutputHigh();
//判断校验和是否一致
uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_temp+ucharRH_data_L_temp);
if(uchartemp==ucharcheckdata_temp)
{
//校验和一致,
ucharRH_data_H=ucharRH_data_H_temp;
ucharRH_data_L=ucharRH_data_L_temp;
ucharT_data_H=ucharT_data_H_temp;
ucharT_data_L=ucharT_data_L_temp;
ucharcheckdata=ucharcheckdata_temp;
//保存温度和湿度
Humi=ucharRH_data_H;//湿度高8位
Humi_small=ucharRH_data_L;//湿度低8位
Temp=ucharT_data_H;//温度高8位
Temp_small=ucharT_data_L;//温度低8位
mytemp = ucharT_data_H;
mytemp = ((uint16)mytemp << 8 | ucharT_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
myhumi = ucharRH_data_H;
myhumi = ((uint16)myhumi << 8 | ucharRH_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
printf("temprature=%.2f humidity=%.2f%%RH \r\n",mytemp,myhumi);//打印至上位机
}
代码实现??
????????该代码源于(23条消息) esp32 采集dht11温湿度数据_Joker2019.-CSDN博客_esp32读取dht11
?????????十分感谢这位大佬,我的代码大部分来源于他的代码,但他的代码我试过不对,打印出来的值是有问题的,问题在于他将四段数据分割存放,这就导致数据位权发生改变。在同事唐兄的帮助下(真心感谢唐兄),我终于理清了思路。下面这段代码至关重要,我看了很多帖子,要不就是用传感器库,要不遮遮掩掩,贴几行啥也不是的代码让你抓耳挠腮。他们的方法我发送到上位机后数据都不对,下面是唐兄教我写的:
mytemp = ucharT_data_H;
mytemp = ((uint16)mytemp << 8 | ucharT_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
myhumi = ucharRH_data_H;
myhumi = ((uint16)myhumi << 8 | ucharRH_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
printf("temprature=%.2f humidity=%.2f%%RH \r\n",mytemp,myhumi);//打印至上位机
下面我贴上我的完整代码,只写了一页,没其他.c文件。说是我自己的代码也不准确,因为我也是东拼西凑搞的,十分感谢大家看到这里。
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include <stdio.h>
#include "driver/uart.h"
#include "driver/gpio.h"
#include "string.h"
//#include "esp_event_loop.h"
#define DHT11_PIN 21 //定义DHT11的引脚
#define uchar unsigned char
#define uint8 unsigned char
#define uint16 unsigned short
//温湿度定义
uchar ucharFLAG,uchartemp;
uchar Humi,Humi_small,Temp,Temp_small;
uchar ucharT_data_H,ucharT_data_L,ucharRH_data_H,ucharRH_data_L,ucharcheckdata;
uchar ucharT_data_H_temp,ucharT_data_L_temp,ucharRH_data_H_temp,ucharRH_data_L_temp,ucharcheckdata_temp;
uchar ucharcomdata;
float mytemp;
float myhumi;
static void InputInitial(void)//设置端口为输入
{
gpio_pad_select_gpio(DHT11_PIN);
gpio_set_direction(DHT11_PIN, GPIO_MODE_INPUT);
}
static void OutputHigh(void)//输出1
{
gpio_pad_select_gpio(DHT11_PIN);
gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(DHT11_PIN, 1);
}
static void OutputLow(void)//输出0
{
gpio_pad_select_gpio(DHT11_PIN);
gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(DHT11_PIN, 0);
}
static uint8 getData()//读取状态
{
return gpio_get_level(DHT11_PIN);
}
//读取一个字节数据
static void COM(void) // 温湿写入
{
uchar i=0;
for(i=0;i<8;i++)
{
//等待IO口变低,变低后,通过延时去判断是0还是1
while((getData()==0)&&ucharFLAG++) ;
ets_delay_us(35);//延时35us
uchartemp=0;
//如果这个位是1,35us后,还是1,则该位为1
if(getData()==1)
{
uchartemp=0x01;
}
else
{
uchartemp=0x00;
}
ucharcomdata<<=1;//左移1位
ucharcomdata|=uchartemp;//或上刚读到的值
//等待IO口变高,变高后,表示可以读取下一位
while((getData()==1)&&ucharFLAG++)
ets_delay_us(10);
}
}
void Delay_ms(uint16 ms)
{
int i=0;
for(i=0; i<ms; i++){
ets_delay_us(1000);
}
}
void DHT11(void) //温湿传感启动
{
OutputLow();//低电平输出
Delay_ms(25); //>18MS
OutputHigh();//高电平输出
InputInitial(); //输入
ets_delay_us(30);
if(!getData())//表示传感器拉低总线
{
ucharFLAG=2;
//等待低电平结束
while((!getData())&&ucharFLAG++)
ets_delay_us(10);
//等待高电平结束
while((getData())&&ucharFLAG++)
ets_delay_us(40);
COM();//读取第1字节,
ucharRH_data_H_temp=ucharcomdata;
COM();//读取第2字节,
ucharRH_data_L_temp=ucharcomdata;
COM();//读取第3字节,
ucharT_data_H_temp=ucharcomdata;
COM();//读取第4字节,
ucharT_data_L_temp=ucharcomdata;
COM();//读取第5字节,
ucharcheckdata_temp=ucharcomdata;
OutputHigh();
//判断校验和是否一致
uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_temp+ucharRH_data_L_temp);
if(uchartemp==ucharcheckdata_temp)
{
//校验和一致,
ucharRH_data_H=ucharRH_data_H_temp;
ucharRH_data_L=ucharRH_data_L_temp;
ucharT_data_H=ucharT_data_H_temp;
ucharT_data_L=ucharT_data_L_temp;
ucharcheckdata=ucharcheckdata_temp;
//保存温度和湿度
Humi=ucharRH_data_H;//湿度高8位
Humi_small=ucharRH_data_L;//湿度低8位
Temp=ucharT_data_H;//温度高8位
Temp_small=ucharT_data_L;//温度低8位
mytemp = ucharT_data_H;
mytemp = ((uint16)mytemp << 8 | ucharT_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
myhumi = ucharRH_data_H;
myhumi = ((uint16)myhumi << 8 | ucharRH_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
printf("temprature=%.2f humidity=%.2f%%RH \r\n",mytemp,myhumi);//打印至上位机
}
else
{
Humi=100;
Temp=100;
}
}
else //没用成功读取,返回0
{
Humi=0,
Temp=0;
printf("ReadError");
}
OutputHigh(); //输出
}
void app_main()
{
while(1)
{
DHT11(); //读取温湿度
vTaskDelay(300); //延时300毫秒
}
}
效果图
?
|