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。示例代码如下:
1
2
3
4
5OutputLow();//低电平输出 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即可检测温湿度数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16if(!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次就能把一个温湿度部分读完。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30static 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次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35COM();//读取第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 rn",mytemp,myhumi);//打印至上位机 }
代码实现
该代码源于(23条消息) esp32 采集dht11温湿度数据_Joker2019.-CSDN博客_esp32读取dht11
十分感谢这位大佬,我的代码大部分来源于他的代码,但他的代码我试过不对,打印出来的值是有问题的,问题在于他将四段数据分割存放,这就导致数据位权发生改变。在同事唐兄的帮助下(真心感谢唐兄),我终于理清了思路。下面这段代码至关重要,我看了很多帖子,要不就是用传感器库,要不遮遮掩掩,贴几行啥也不是的代码让你抓耳挠腮。他们的方法我发送到上位机后数据都不对,下面是唐兄教我写的:
1
2
3
4
5
6mytemp = 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 rn",mytemp,myhumi);//打印至上位机
下面我贴上我的完整代码,只写了一页,没其他.c文件。说是我自己的代码也不准确,因为我也是东拼西凑搞的,十分感谢大家看到这里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162#include <stdio.h> #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" #define DHT11_PIN 21//定义DHT11的引脚 #define uchar unsigned char #define uint8 unsigned char #define uint16 unsigned short //温湿度定义 uchar ucharFLAG,uchartemp; float Humi,Temp; 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; 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; for(i=0;i<8;i++) { ucharFLAG=2; //等待IO口变低,变低后,通过延时去判断是0还是1 while((getData()==0)&&ucharFLAG++) ets_delay_us(10); ets_delay_us(35);//延时35us uchartemp=0; //如果这个位是1,35us后,还是1,否则为0 if(getData()==1) uchartemp=1; ucharFLAG=2; //等待IO口变高,变高后,表示可以读取下一位 while((getData()==1)&&ucharFLAG++) ets_delay_us(10); if(ucharFLAG==1) break; ucharcomdata<<=1; ucharcomdata|=uchartemp; } } void Delay_ms(uint16 ms) { int i=0; for(i=0; i<ms; i++){ ets_delay_us(1000); } } void DHT11(void) //温湿传感启动 { OutputLow(); Delay_ms(19); //>18MS OutputHigh(); InputInitial(); //输入 ets_delay_us(30); if(!getData())//表示传感器拉低总线 { ucharFLAG=2; //等待总线被传感器拉高 while((!getData())&&ucharFLAG++) ets_delay_us(10); //等待总线被传感器拉低 while((getData())&&ucharFLAG++) ets_delay_us(10); 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; Humi=((uint16)Humi<<8|ucharRH_data_L)/10; Temp=ucharT_data_H; Temp=((uint16)Temp<<8|ucharT_data_L)/10; } else { Humi=100; Temp=100; } } else //没用成功读取,返回0 { Humi=0, Temp=0; } OutputHigh(); //输出 } void app_main() { char dht11_buff[50]={0}; while(1) { DHT11(); //读取温湿度 printf("Temp=%.2f--Humi=%.2f%%RH rn", Temp,Humi); vTaskDelay(300); //延时300毫秒 } }
效果图
最后
以上就是烂漫往事最近收集整理的关于ESP32驱动DHT11检测温湿度(ESP IDF环境)的全部内容,更多相关ESP32驱动DHT11检测温湿度(ESP内容请搜索靠谱客的其他文章。
发表评论 取消回复