DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。其成本低、长期稳定、可以测量相对湿度和温度测量,并可以只使用一根数据线进行温湿度采集。
模块来源
规格参数
厂家资料下载见百度网盘链接
工作电压:3-5.5V
工作电流:1MA
测量分辨率:8 bit
湿度量程: 20 - 90 %RH
湿度精度:±5 %RH
温度量程: 0 - 50 ℃
温度精度:±2 ℃
通信协议:单总线
管脚数量:3 Pin(2.54mm间距排针)
移植过程
我们的目标是在开发板上能够实现读取温湿度的功能。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
查看资料
DHT11使用的是单总线通信,即发送数据与接收数据都在一根数据线上,通过规定的时序进行控制。
从左向右看,时序一开始,主机信号就保持着高电平,所以引脚初始化完毕时,及时给引脚输出高电平。因为 模块的数据线要求空闲时,要保持高电平状态。(其实模块上已经接了上拉电阻,使数据线一直保持高电平)
根据时序图可以知道,主机(开发板)发送一次开始信号,待主机开始信号结束后,DHT11 发送响应信号,送出 温湿度数据,并触发一次数据采集给下一次数据读取作准备。因此完成一次数据读取需要进行起始信号、响应信号、数据接收、结束信号。
读取数据步骤:
- 起始信号:主机(开发板)接入数据线的I/O输出低电平,且低电平保持时间不能小于 18ms
响应信号:等待模块的响应信号到来。将数据线改为输入模式,如果接入到低电平,说明接收到模块的响应。
数据传输:主机接收模块发送的40位数据,其中,位数据 ‘0’ 表示54us的低电平,27us的高电平;位数据 ‘1’ 表示54us的低电平,74us的高电平。两个格式的分辨主要是高电平的输出时长不同。
- 结束信号:模块的数据线输出 40 位数据后,是以低电平结束,它会继续输出低电平 54 微秒后转为输入状态,主机需要转为输出状态,输出高电平释放总线。
数据接收完成,但是这40位数据要如何转化为温湿度数据?并如何保证传输的数据没有错误?
DHT11模块一次完整的数据传输为40bit,高位先出。 数据格式:
8bit湿度整数数据 + 8bit湿度小数数据 + 8bi温度整数数据 + 8bit温度小数数据 + 8bit校验和
注意
湿度小数部分数据一直为0。
数据传送正确时,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。举几个例子。
示例一:接收的40位数据分别为:
0011 0101 | 0000 0000 | 0001 1000 | 0000 0100 | 0101 0001 |
---|---|---|---|---|
湿度高8位 | 湿度低8位 | 温度高8位 | 温度低8位 | 校验位 |
校验和为 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 | 0100 1001 |
---|---|---|---|---|
湿度高8位 | 湿度低8位 | 温度高8位 | 温度低8位 | 校验位 |
校验和为 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据不一致 计算的数据为0101 0001,接收的数据为0100 1001,两者不一致说明数据不准确,丢弃这次数据,重新接收。
以下为数据处理的实现代码:
//val为接收到的40位数据。
// 湿高8 + 湿低8 + 温高8 + 温低8
verify_num = (val>>32) + (val>>24) + (val>>16) + (val>>8);
//计算的校验和 与 接收的校验和 的差为0说明一致,不为0说明不一致
//(val&0xff)是因为val的大小为64位,我们只需要val的最后8位校验和
verify_num = verify_num - (val&0xff);
//进行校验
if( verify_num )//如果不为0,说明校验失败
{
// 校验错误
return 0;
}
else //校验成功
{
//数据处理
humidity = (val>>32)&0xff; //湿度前8位(小数点前数据)
small_point = (val>>24)&0x00ff; //湿度后8位(小数点后数据)
small_point = small_point * 0.1; //换算为小数点
humidity = humidity + small_point; //小数前+小数后
printf("湿度:%.2f\r\n",humidity);
temperature = (val>>16)&0x0000ff; //温度前8位(小数点前数据)
small_point = (val>>8)&0x000000ff; //温度后8位(小数点后数据)
small_point = small_point * 0.1; //换算为小数点
temperature = temperature + small_point;//小数前+小数后
printf("温度:%.2f\r\n",temperature);
return val>>8; //返回未处理的数据
}
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
引脚选择
该模块需要设置3个接口
这里选择的引脚见引脚接线表
代码移植
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# DHT11传感器
source "application/rt-thread/helloworld/user-bsp/dht11/Kconfig"
2
添加完成之后:
menuconfig操作
1、我们 双击 luban-lite
文件夹下的 win_env.bat
脚本打开env工具:
2、输入以下命令列出所有可用的默认配置:
scons --list-def
3、选择 d13x_JLC_rt-thread_helloworld
这个配置!这个是我们衡山派开发板的默认配置!输入以下命令即可:
scons --apply-def=7
或者
scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig
这两个命令作用是一样的,一个是 文件名 ,一个是 编号 !!!
4、输入以下命令进入menuconfig菜单
scons --menuconfig
进入以下界面:
5、选中 Porting code using the LCKFB module
按 Y
键!
按
Y
选中按
N
取消选中方向键
左右
调整 最下面菜单的选项方向键
上下
调整 列表的选项
回车
执行最下面菜单的选项
6、回车进入 Porting code using the LCKFB module
菜单
7、按方向键 上下
选中 Use dht11 sensor
后按 Y
键,看到前面括号中出现一个 *
号,就可以下一步了。
8、按方向键 左右
选中 <Save>
然后一路回车
,然后 退出
即可
编译
我们 保存并退出menuconfig菜单 之后,输入以下命令进行编译:
scons
或
scons -j16
-j 用来选择参与编译的核心数: 我这里是选择16
大家可以根据自己的电脑来选择
核心越多编译越快
如果写的数量高于电脑本身,那么就自动按照最高可用的来运行!
镜像烧录
编译完成之后会在 \luban-lite\output\d13x_JLC_rt-thread_helloworld\images
文件夹下生成一个 d13x_JLC_v1.0.0.img
镜像文件!
然后我们烧录镜像,具体的教程请查看:镜像烧录(点击跳转🚀)
到这里完成了,请移步到 最后一节 进行移植验证。
工程代码解析
bsp_dht11.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include "bsp_dht11.h"
#include <stdio.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#define uchar unsigned char
#define uint8 unsigned char
#define uint16 unsigned short
static float temperature = 0;
static float humidity = 0;
static void delay_ms(uint16 ms)
{
rt_thread_mdelay(ms);
}
static void delay_us(uint16 us)
{
aicos_udelay(us);
}
//温湿度定义
static uchar ucharFLAG,uchartemp;
static float Humi,Temp;
static uchar ucharT_data_H,ucharT_data_L,ucharRH_data_H,ucharRH_data_L,ucharcheckdata;
static uchar ucharT_data_H_temp,ucharT_data_L_temp,ucharRH_data_H_temp,ucharRH_data_L_temp,ucharcheckdata_temp;
static uchar ucharcomdata;
static uchar Humi_small;
static uchar Temp_small;
static void InputInitial(void)//设置为输入
{
rt_pin_mode(GPIO_DHT11, PIN_MODE_INPUT);
}
static void OutputHigh(void)//输出1
{
rt_pin_mode(GPIO_DHT11, PIN_MODE_OUTPUT);
rt_pin_write(GPIO_DHT11, PIN_HIGH);
}
static void OutputLow(void)//输出0
{
rt_pin_mode(GPIO_DHT11, PIN_MODE_OUTPUT);
rt_pin_write(GPIO_DHT11, PIN_LOW);
}
static uint8 getData()//读取状态
{
rt_pin_mode(GPIO_DHT11, PIN_MODE_INPUT);
return rt_pin_read(GPIO_DHT11);
}
//读取一个字节数据
static void COM(void)
{
uchar i;
for(i=0;i<8;i++)
{
ucharFLAG=2;
//等待IO口变低,变低后,通过延时去判断是0还是1
while((getData()==0)&&ucharFLAG++) delay_us(10);
delay_us(35);//延时35us
uchartemp=0;
//如果这个位是1,35us后,还是1,否则为0
if(getData()==1)
uchartemp=1;
ucharFLAG=2;
//等待IO口变高,变高后,表示可以读取下一位
while((getData()==1)&&ucharFLAG++)
delay_us(10);
if(ucharFLAG==1)
break;
ucharcomdata<<=1;
ucharcomdata|=uchartemp;
}
}
int DHT11_Read(float *read_buff) //温湿传感启动
{
OutputLow();
delay_ms(19); //>18MS
OutputHigh();
InputInitial(); //输入
delay_us(30);
if( !getData() )//表示传感器拉低总线
{
ucharFLAG=2;
//等待总线被传感器拉高
while((!getData())&&ucharFLAG++)
delay_us(10);
//等待总线被传感器拉低
while((getData())&&ucharFLAG++)
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; // 湿度高8
ucharRH_data_L=ucharRH_data_L_temp; // 湿度低8
ucharT_data_H=ucharT_data_H_temp; // 温度高8
ucharT_data_L=ucharT_data_L_temp; // 温度低8
ucharcheckdata=ucharcheckdata_temp;
//保存温度和湿度
Humi = ucharRH_data_H;
Humi_small = ucharRH_data_L * 0.1;
Humi = Humi + Humi_small;
Temp = ucharT_data_H;
Temp_small = ucharT_data_L * 0.1;
Temp = Temp + Temp_small;
read_buff[0] = Temp; // 温度
read_buff[1] = Humi; // 湿度
return RT_EOK;
}
else // 校验失败
{
Humi=100;
Temp=100;
return RT_ERROR;
}
}
else //没用成功读取
{
Humi=0,
Temp=0;
return RT_ERROR;
}
}
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
bsp_dht11.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef _BSP_DHT11_H_
#define _BSP_DHT11_H_
#include <stdio.h>
#include <inttypes.h>
#include "string.h"
/**************引脚修改此处****************/
#define GPIO_DHT11 rt_pin_get("PC.7")
int DHT11_Read(float *read_buff); //温湿传感读取数据
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_DHT11
bool "Use dht11 sensor"
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
SConscript
自动化构建文件,如果定义了 LCKFB_DHT11
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_DHT11') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-dht11', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_dht11.c
这个文件定义了一个用于处理DHT11温湿度传感器的线程,初始化了DHT11传感器,并设置了线程的优先级、栈大小和时间片。
线程的主要任务是周期性地读取DHT11的温度和湿度值,并将读取到的数据打印到控制台。线程在读取数据时会进行错误检查,并在一定次数的读取后自动结束。通过命令行接口,用户可以启动这个线程来测试DHT11传感器的功能。
线程入口函数逻辑
- 初始化一个浮点数数组
read_buff
用于存储读取的温度和湿度值。 - 设置读取次数
read_num
为默认的50次。 - 在一个循环中,循环次数由
read_num
决定。 - 使用
rt_memset
清零read_buff
数组。 - 调用
DHT11_Read
函数读取传感器的数据。 - 如果读取成功(返回
RT_EOK
),将读取到的温度和湿度值转换为整数和小数格式并打印到控制台。 - 如果读取失败,不打印任何值。
- 当循环次数达到0次时,打印结束读取的提示信息。
- 在每次循环结束时,线程会挂起1000毫秒。
DHT11启动函数逻辑
创建名为"dht11_thread"的线程,入口函数为dht11_thread_entry
,无参数,设置栈大小、优先级和时间片。 如果线程创建成功,启动线程。
提示
MSH_CMD_EXPORT
宏将test_dht11
函数导出为RT-Thread命令行接口的命令,这样用户可以在RT-Thread的命令行中直接运行dht11 test
命令来启动DHT11传感器的读取。
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_dht11.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 4096 // 线程大小
#define THREAD_TIMESLICE 10 // 时间片
static rt_thread_t dht11_thread = RT_NULL; // 线程控制块
// DHT11读取次数
// 默认读取50次
static int read_num = 50;
// 线程入口函数
static void dht11_thread_entry(void *param)
{
float read_buff[2] = {0};
while(read_num--)
{
rt_memset(read_buff,0,sizeof(read_buff));
int ret = DHT11_Read(read_buff);
if(ret == RT_EOK)
{
rt_kprintf("\n===========[%d]=============\n",read_num+1);
rt_kprintf("DHT11 temperature: %d.%d\n",
(int)read_buff[0],(((uint32_t)(read_buff[0]*100))%100));
rt_kprintf("DHT11 humidity: %d.%d\n",
(int)read_buff[1],(((uint32_t)(read_buff[1]*100))%100));
}
rt_thread_mdelay(1000);
}
rt_kprintf("\nRead DHT11 End!!\n");
}
static void test_dht11(int argc, char **argv)
{
/* 创建线程,名称是 dht11_thread,入口是 dht11_thread_entry */
dht11_thread = rt_thread_create("dht11_thread",
dht11_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (dht11_thread != RT_NULL)
rt_thread_startup(dht11_thread);
}
// 导出函数为命令
MSH_CMD_EXPORT(test_dht11, dht11 test);
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
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_dht11
上电效果:(默认采集50次数据)