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 | P407 |
GND | GND |
3、图形化配置
- 打开图形化界面:
- 设置引脚
Pin
:
Ctrl + S
保存!
4、代码编写
我们在工程中新建两个文件 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 "hal_data.h"
#include <stdio.h>
#ifndef delay_ms
#define delay_ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS)
#endif
#ifndef delay_1ms
#define delay_1ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS)
#endif
#ifndef delay_us
#define delay_us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS)
#endif
#ifndef delay_1us
#define delay_1us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS)
#endif
#ifndef u8
#define u8 uint8_t
#endif
#ifndef u16
#define u16 uint16_t
#endif
#ifndef u32
#define u32 uint32_t
#endif
#define DHT11_DAT_GPIO_PIN BSP_IO_PORT_04_PIN_07 // DHT11数据引脚
// 设置DHT11输出高或低电平
#define DATA_GPIO_OUT(x) R_IOPORT_PinWrite(&g_ioport_ctrl, DHT11_DAT_GPIO_PIN, x)
// 获取DHT11数据引脚高低电平状态
static inline bsp_io_level_t DATA_GPIO_IN(void)
{
bsp_io_level_t p_pin_value;
fsp_err_t err = R_IOPORT_PinRead(&g_ioport_ctrl, DHT11_DAT_GPIO_PIN, &p_pin_value);
if(err != FSP_SUCCESS)
{
printf("DATA_GPIO_IN Failed!!\r\n");
}
return p_pin_value;
}
void DHT11_Init(void);
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网: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)
{
//调用 R_IOPORT_Open 函数来初始化 IOPORT 模块
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
/* 设置DHT11数据引脚输出高电平 */
DATA_GPIO_OUT(1);
delay_1ms(100); // 等待DHT11稳定
}
/******************************************************************
* 函 数 名 称:DHT11_GPIO_Mode_OUT
* 函 数 说 明:配置DHT11的数据引脚为输出模式
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
static void DHT11_GPIO_Mode_OUT(void)
{
// 在运行过程中配置DHT11数据引脚为输出模式
// 这里使用R_IOPORT_PinCfg函数配置引脚
fsp_err_t err = R_IOPORT_PinCfg(&g_ioport_ctrl, DHT11_DAT_GPIO_PIN,
((uint32_t) IOPORT_CFG_DRIVE_HIGH
| (uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT
| (uint32_t) IOPORT_CFG_PORT_OUTPUT_HIGH));
if(err != FSP_SUCCESS)
{
printf("DHT11_GPIO_Mode_OUT Failed!!\r\n");
}
}
/******************************************************************
* 函 数 名 称:DHT11_GPIO_Mode_IN
* 函 数 说 明:配置DHT11的数据引脚为输入模式
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
static void DHT11_GPIO_Mode_IN(void)
{
// 在运行过程中配置DHT11数据引脚为输入模式
// 这里使用R_IOPORT_PinCfg函数配置引脚
fsp_err_t err = R_IOPORT_PinCfg(&g_ioport_ctrl, DHT11_DAT_GPIO_PIN,
(uint32_t) IOPORT_CFG_PORT_DIRECTION_INPUT);
if(err != FSP_SUCCESS)
{
printf("DHT11_GPIO_Mode_IN Failed!!\r\n");
}
}
/******************************************************************
* 函 数 名 称:DHT11_Start
* 函 数 说 明:开始DHT11测量
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void DHT11_Start(void)
{
DHT11_GPIO_Mode_OUT(); // 输出模式
DATA_GPIO_OUT(1);
DATA_GPIO_OUT(0);
delay_ms(25); // 保持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
256
257
258
259
260
四、移植验证
在 src\Applay\app.c
中输入代码如下:
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include "app.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "bsp_dht11.h"
/******************************************************************
* 函 数 名 称:led_blink
* 函 数 说 明:该函数用于控制LED灯的闪烁效果
* 运行时,LED灯每隔500毫秒闪烁一次
* 完整运行此函数需要1s时间
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
static void led_blink(void)
{
/* Set the pin to low */
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_02, BSP_IO_LEVEL_LOW);
/* Delay for 500 milliseconds */
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
/* Set the pin to high */
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_02, BSP_IO_LEVEL_HIGH);
/* Delay for another 500 milliseconds */
R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
}
/******************************************************************
* 函 数 名 称:Run
* 函 数 说 明:该函数是用户自定义的入口函数,等效于 main_app() 函数。
* 在此函数中可以编写用户的应用逻辑代码。
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void Run(void)
{
/* 初始化调试串口 */
/* | RX:P100 | TX:P101 | */
UART0_Debug_Init();
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
printf("\r\n=== Welcome to use the DQX-R7FA6E2BB3CNE development board ====\r\n");
printf("\r\n======================= www.lckfb.com =========================\r\n");
printf("\r\n======================= wiki.lckfb.com ========================\r\n");
printf("\r\n======================= [Debug Uart0] =========================\r\n");
printf("\r\n=================== | RX:P100 | TX:P101 | =====================\r\n");
printf("\r\n= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =\r\n");
/* 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
上电效果:
【代码下载】
- 跳转去下载模块移植代码:【点击跳转🚀】