DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。其成本低、长期稳定、可以测量相对湿度和温度测量,并可以只使用一根数据线进行温湿度采集。
一、模块来源
二、规格参数
工作电压:3-5.5V
工作电流:1MA
测量分辨率:8 bit
湿度量程: 20 - 90 %RH
湿度精度:±5 %RH
温度量程: 0 - 50 ℃
温度精度:±2 ℃
通信协议:单总线
管脚数量:3 Pin(2.54mm间距排针)
以上信息见厂家资料文件
三、移植过程
我们的目标是将例程移植至开发板上【实现读取温湿度的功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
1、查看资料
DHT11使用的是单总线通信,即发送数据与接收数据都在一根数据线上,通过规定的时序进行控制。
从左向右看,时序一开始,主机信号就保持着高电平,所以引脚初始化完毕时,及时给引脚输出高电平。因为模块的数据线要求空闲时,要保持高电平状态。(其实模块上已经接了上拉电阻,使数据线一直保持高电平)
根据时序图可以知道,主机(开发板)发送一次开始信号,待主机开始信号结束后,DHT11 发送响应信号,送出 温湿度数据,并触发一次数据采集给下一次数据读取作准备。因此完成一次数据读取需要进行起始信号、响应信号、数据接收、结束信号。
读取数据步骤:
- 起始信号:主机(开发板)接入数据线的I/O输出低电平,且低电平保持时间不能小于 18ms。
- 响应信号:等待模块的响应信号到来。将数据线改为输入模式,如果接入到低电平,说明接收到模块的响应。
- 数据传输:主机接收模块发送的40位数据,其中,位数据 ‘0’ 表示54us的低电平,27us的高电平;位数据 ‘1’ 表示54us的低电平,74us的高电平。两个格式的分辨主要是高电平的输出时长不同。
- 结束信号:模块的数据线输出 40 位数据后,是以低电平结束,它会继续输出低电平 54 微秒后转为输入状态,主机需要转为输出状态,输出高电平释放总线。
DHT11_GPIO_Mode_OUT();//转为输出模式
DATA_GPIO_OUT(1);//主机释放总线
2
数据接收完成,但是这40位数据要如何转化为温湿度数据?并如何保证传输的数据没有错误?
DHT11模块一次完整的数据传输为40bit,高位先出。 数据格式:
INFO
湿度小数部分数据一直为0!!
数据传送正确时,校验和数据等于 “ 8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 ” 所得结果的末8位。举几个例子。
示例一:接收的40位数据分别为:
校验和为 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据一致
湿度为 0011 0101 + 0000 0000 = 35 + 0 = 35%RH
温度为 0001 1000 0000 0100 = 24 + 4 = 24.4℃
示例二:接收的40位数据分别为:
校验和为 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据不一致
计算的数据为0101 0001,接收的数据为0100 1001,两者不一致说明数据不准确,丢弃这次数据,重新接收。
2、引脚选择
VCC | 5V |
DAT | PA5 |
GND | GND |
3、代码编写
我们在工程中新建两个文件 bsp_dht11.c
和 bsp_dht11.h
,并且添加文件路径到工程中。
添加完成之后,就会在我们本地文件夹路径中有所显示:
在文件 bsp_dht11.c
和 bsp_dht11.h
中,编写如下代码。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef BSP_CODE_BSP_DHT11_H_
#define BSP_CODE_BSP_DHT11_H_
#include "gd32vw55x.h"
#include "systick.h"
#ifndef delay_ms
#define delay_ms(x) delay_1ms(x)
#endif
#ifndef delay_us
#define delay_us(x) delay_1us(x)
#endif
#define DHT11_DAT_GPIO_RCU RCU_GPIOA // DHT11数据引脚
#define DHT11_DAT_GPIO_PORT GPIOA // DHT11数据端口
#define DHT11_DAT_GPIO_PIN GPIO_PIN_5 // DHT11数据引脚
// 开启DHT11数据引脚时钟
#define DHT11_DAT_GPIO_CLK_ENABLE() rcu_periph_clock_enable(DHT11_DAT_GPIO_RCU)
// 设置DHT11输出高或低电平
#define DATA_GPIO_OUT(x) gpio_bit_write(DHT11_DAT_GPIO_PORT, DHT11_DAT_GPIO_PIN, x)
// 获取DHT11数据引脚高低电平状态
#define DATA_GPIO_IN gpio_input_bit_get(DHT11_DAT_GPIO_PORT, DHT11_DAT_GPIO_PIN)
void DHT11_Init(void);//初始化DHT11
uint8_t DHT11_Read_Data(float *temperature, float *humidity);
#endif /* BSP_CODE_BSP_DHT11_H_ */
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
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include "bsp_dht11.h"
#define DHT11_DEBUG 0 // 调试信息 1:开启 0:关闭
#define DHT11_TIMEOUT 1000 // 超时阈值
/******************************************************************
* 函 数 名 称:DHT11_Init
* 函 数 说 明:配置DHT11的初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DHT11_Init(void)
{
/* 使能DHT11数据引脚时钟 */
DHT11_DAT_GPIO_CLK_ENABLE();
/* 配置DHT11数据引脚为上拉输出模式 */
gpio_mode_set(DHT11_DAT_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, DHT11_DAT_GPIO_PIN);
/* 设置DHT11数据引脚为推挽输出,速度25MHz */
gpio_output_options_set(DHT11_DAT_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, DHT11_DAT_GPIO_PIN);
/* 设置DHT11数据引脚输出高电平 */
DATA_GPIO_OUT(1);
delay_1ms(100); // 等待DHT11稳定
}
/******************************************************************
* 函 数 名 称:DHT11_GPIO_Mode_OUT
* 函 数 说 明:配置DHT11的数据引脚为输出模式
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DHT11_GPIO_Mode_OUT(void)
{
/* 配置DHT11数据引脚为输出模式 */
gpio_mode_set(DHT11_DAT_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, DHT11_DAT_GPIO_PIN);
/* 设置DHT11数据引脚为推挽输出,速度25MHz */
gpio_output_options_set(DHT11_DAT_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, DHT11_DAT_GPIO_PIN);
}
/******************************************************************
* 函 数 名 称:DHT11_GPIO_Mode_IN
* 函 数 说 明:配置DHT11的数据引脚为输入模式
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DHT11_GPIO_Mode_IN(void)
{
/* 配置DHT11数据引脚为上拉输入模式 */
gpio_mode_set(DHT11_DAT_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, DHT11_DAT_GPIO_PIN);
}
/******************************************************************
* 函 数 名 称:DHT11_Start
* 函 数 说 明:开始DHT11测量
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DHT11_Start(void)
{
DHT11_GPIO_Mode_OUT(); // 输出模式
DATA_GPIO_OUT(1);
DATA_GPIO_OUT(0);
delay_ms(20); // 保持18+ms低电平
DATA_GPIO_OUT(1);
delay_us(25); // 主机释放总线
DHT11_GPIO_Mode_IN(); // 切换输入模式
}
/******************************************************************
* 函 数 名 称:DHT11_CheckResponse
* 函 数 说 明:检查DHT11从机的相应是否正确
* 函 数 形 参:无
* 函 数 返 回:1:正确 0:错误
* 作 者:LC
* 备 注:无
******************************************************************/
uint8_t DHT11_CheckResponse(void)
{
uint32_t timeout = 0;
// 等待从机响应发送电平响应(低电平)(80µs)
timeout = DHT11_TIMEOUT;
while(DATA_GPIO_IN && timeout--)
{
if(timeout == 0)
{
printf("DHT11_CheckResponse Failed[1]!!\r\n");
return 0; // 失败
}
}
// 等待从机响应发送电平响应(高电平)(80µs)
timeout = DHT11_TIMEOUT;
while((!DATA_GPIO_IN) && timeout--)
{
if(timeout == 0)
{
printf("DHT11_CheckResponse Failed[2]!!\r\n");
return 0; // 失败
}
}
// 进入前导低电平
timeout = DHT11_TIMEOUT;
while(DATA_GPIO_IN && timeout--)
{
if(timeout == 0)
{
printf("DHT11_CheckResponse Failed[3]!!\r\n");
return 0; // 失败
}
}
return 1;
}
/******************************************************************
* 函 数 名 称:DHT11_ReadBit
* 函 数 说 明:读取一位数据位
* 函 数 形 参:无
* 函 数 返 回:0 或 1
* 作 者:LC
* 备 注:无
******************************************************************/
uint8_t DHT11_ReadBit(void)
{
uint8_t bit = 0;
uint8_t timeCount = 0;
uint32_t timeout;
// 等待前导低电平过去
// 等待信号线由低变高
timeout = DHT11_TIMEOUT;
while((!DATA_GPIO_IN) && timeout--)
{
if(timeout == 0)
{
printf("DHT11_ReadBit Failed[1]!!\r\n");
return 0;
}
}
// 判断是0还是1?
timeout = DHT11_TIMEOUT;
while(DATA_GPIO_IN && timeout--)
{
timeCount++;
delay_us(1); // 等待1us
if(timeout == 0)
{
printf("DHT11_ReadBit Failed[2]!!\r\n");
return 0; // 失败
}
}
// 只要大于30us的高电平即可判断为数据1
if(timeCount >= 30)
bit = 1; // 数据1
else
bit = 0; // 数据0
return bit;
}
/******************************************************************
* 函 数 名 称:DHT11_Read_Data
* 函 数 说 明:根据时序读取温湿度数据
* 函 数 形 参:
* 函 数 返 回:0=数据校验失败 其他=错误
* 作 者:LC
* 备 注:无
******************************************************************/
uint8_t DHT11_Read_Data(float *temperature, float *humidity)
{
int i;
uint8_t data[5] = {0};
uint64_t val = 0;
// 开始起始信号
DHT11_Start();
// 检查响应是否合规
if(0 == DHT11_CheckResponse())
{
printf("DHT11_CheckResponse Failed!!\r\n");
return 0;
}
// 数据读取核心(40-bit)
for(i = 0; i < 40; i++)
{
val <<= 1;
val |= DHT11_ReadBit();
}
// 校验与数据提取
data[0] = (val >> 32) & 0xFF; // 湿度整数
data[1] = (val >> 24) & 0xFF; // 湿度小数
data[2] = (val >> 16) & 0xFF; // 温度整数
data[3] = (val >> 8) & 0xFF; // 温度小数
data[4] = val & 0xFF; // 校验和
#if DHT11_DEBUG
printf("data[0] = %d\r\n",data[0]);
printf("data[1] = %d\r\n",data[1]);
printf("data[2] = %d\r\n",data[2]);
printf("data[3] = %d\r\n",data[3]);
printf("data[4] = %d\r\n",data[4]);
#endif
// 校验计算:前4字节和 = 第5字节
if ((data[4] != (data[0] + data[1] + data[2] + data[3])) ||
(data[4] == 0)) {
printf("CheckSum Failed!!!\r\n");
return 0; // 校验失败
}
// 保存温湿度
*humidity = data[0] + (data[1] * 0.1f); // 湿度(%)
*temperature = data[2] + (data[3] * 0.1f); // 温度(℃)
#if DHT11_DEBUG
printf("T: %d\r\n",(int)*temperature);
printf("H: %d\r\n",(int)*humidity);
#endif
return 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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
四、移植验证
在 src\main.c
中输入代码如下:
#include "gd32vw55x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32vw553h_eval.h"
#include "bsp_dht11.h"
/*!
\brief toggle the led every 500ms
\param[in] none
\param[out] none
\retval none
*/
void led_spark(void)
{
static __IO uint32_t timingdelaylocal = 0U;
if(timingdelaylocal) {
if(timingdelaylocal < 500U) {
gd_eval_led_on(LED1);
} else {
gd_eval_led_off(LED1);
}
timingdelaylocal--;
} else {
timingdelaylocal = 1000U;
}
}
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
#ifdef __FIRMWARE_VERSION_DEFINE
uint32_t fw_ver = 0;
#endif /* __FIRMWARE_VERSION_DEFINE */
/* configure systick */
systick_config();
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
/* initilize the LEDs, USART and key */
gd_eval_led_init(LED1);
gd_eval_com_init(EVAL_COM0);
gd_eval_key_init(KEY_TAMPER_WAKEUP, KEY_MODE_GPIO);
#ifdef __FIRMWARE_VERSION_DEFINE
fw_ver = gd32vw55x_firmware_version_get();
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n\n");
printf("\r\n=== Welcome to use the LC-GD32VW553-HMQ6 development board ====\r\n\n");
printf("\r\n======================= www.lckfb.com =========================\r\n\n");
printf("\r\n======================= wiki.lckfb.com ========================\r\n\n");
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
/* print firmware version */
printf("\r\nGD32VW55X series firmware version: V%d.%d.%d", (uint8_t)(fw_ver >> 24), (uint8_t)(fw_ver >> 16), (uint8_t)(fw_ver >> 8));
#endif /* __FIRMWARE_VERSION_DEFINE */
/* print out the clock frequency of system, AHB, APB1 and APB2 */
printf("\r\nCK_SYS is %d\r\n", rcu_clock_freq_get(CK_SYS));
printf("\r\nCK_AHB is %d\r\n", rcu_clock_freq_get(CK_AHB));
printf("\r\nCK_APB1 is %d\r\n", rcu_clock_freq_get(CK_APB1));
printf("\r\nCK_APB2 is %d\r\n", rcu_clock_freq_get(CK_APB2));
/* initialize DHT11 */
DHT11_Init();
/* 空读两次 */
DHT11_Read_Data(NULL, NULL);
delay_1ms(1000); // 等待DHT11稳定
DHT11_Read_Data(NULL, NULL);
delay_1ms(1000); // 等待DHT11稳定
printf("\r\n= = = = = = = = = = = = DHT11 Demo Start = = = = = = = = = = =\r\n");
while(1)
{
float temperature = 0.0;
float humidity = 0.0;
if(DHT11_Read_Data(&temperature, &humidity))
{
printf("\n");
printf("Temperature = %.2f\r\n", temperature);
printf("Humidity = %.2f\r\n", humidity);
}
else
{
printf("\r\nRead Error!!\r\n");
}
delay_ms(1000);
}
}
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
上电效果:
注意⚠
如果发现自己的工程无法使用 printf
打印浮点信息(无法使用%f
)那么请看常见问题与解决办法🚀
【代码下载】
- 跳转到
下载中心
去下载模块移植代码:【点击跳转🚀】