2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
第二十六章 DHT11数字温湿度传感器实验
DHT11是奥松(AoSong)公司生产的一款数字温湿度复合传感器。该传感器用途广泛、抗
干扰能力强、可靠性高,在家电、汽车、医疗等方面有广泛的应用。本章我们将使用FPGA开发
板实现对DHT11数据的采集,并将温湿度数据显示在数码管上。
本章包括以下几个部分:
26.1 DHT11简介
26.2 实验任务
26.3 硬件设计
26.4 程序设计
26.5 下载验证
DHT11简介
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它使用专
用的数字模块采集技术和温湿度传感技术,具有极高的可靠性与卓越的长期稳定性。传感器包
括一个电阻式感湿
元件和一个NTC(负温度系数热敏电阻器)测温元件,并与一个高性能8位MCU
相连接。每个DHT11传感器都在湿度校验室中校准过,校准系数以程序的形式储存在OTP(一次
性可编程)内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。DHT11使用单
线制串行接口,4针单排引脚封装,信号传输距离可达20米以上,在各类应用甚至很苛刻的环
境中都能正常工作。
图 26.1.1 DHT11内部原理图
上图为DHT11的内部原理图,可以看出感湿原件、感温元件和OTP内存直接连接在内部一个
八位MCU上,该MCU通过计算得出测量数值。
DATA用于FPGA与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,
数据分为整数部分和小数部分,数据格式如下:
一次完整的数据传输为40bit,高位在前。
数据格式:8bit湿度整数数据+8bit湿度小数数据
+8bit温度整数数据+8bit温度小数数据
+8bit校验和数据
数据传送正确时校验和数据等于“8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度
整数数据 + 8bit温度小数数据”所得结果的末8位)。
接下来我们介绍一下DHT11的传输时序,DHT11的数据发送流程如图 26.1.2
错误!未找到
引用源。所示。

图 26.1.2 DHT11数据发送流程
主机(此处指FPGA)首先发送一次开始信号,即:拉低数据线,保持t1(至少18ms)时间;
然后拉高数据线保持t2(20~40us)时间,随后开始读取DHT11的响应;如果操作正确的话,DHT11
会拉低数据线,保持t3(80us)时间,作为响应信号;接下来DHT11会拉高数据线,保持t4(80us)
时间,随后开始输出有效数据。
DHT11共输出40bit有效数据,每1bit数据都是以50us低电平开始,高电平的持续时间作为
判断数据位的条件。当数据位为0时,高电平的持续时间为26~28us;当数据位为1时,高电平
的持续时间为70us。
DHT11数据位“0”时序图和数据位“1”时序图如图 26.1.3和图 26.1.4所示。
图 26.1.3 DHT11数据位“0”时序图

图 26.1.4 DHT11数据位“1”时序图
需要注意的是,DHT11的温度和湿度转换较慢,如果读取速度过快会导致DHT11无法响应的
情况。本次实验每100ms读取一次,如果DHT11长时间未响应,则重新发起开始信号。
实验任务
本节实验任务是使用开拓者FPGA开发板完成对DHT11温湿度数据的采集,并通过按键KEY0
控制温度和湿度在数码管上的切换显示。
硬件设计
我们的开拓者开发板上有一个DS18B20/DTH11的扩展接口,该接口可以用来连接DS18B20或
DHT11。其原理图如图 26.3.1所示。
图 26.3.1 DHT11/DS18B20扩展接口原理图
DHT11通过4个排针与外部连接,如图 26.3.2所示,将DHT11直接插在开发板上即可,接插
之前注意正负极的方向,以免短路。

图 26.3.2 DHT11连接实物图
本次实验的管脚分配如下表所示:
表 26.3.1 管脚分配
程序设计
根据实验任务,我们可以大致规划出系统的工作流程:FPGA控制DHT11采集温度和湿度,
并将收到的温度和湿度数据转换成十进制显示在数码管上。本次实验使用按键来控制数码管显
示温度和湿度,所以还需要添加消抖模块来对按键进行消抖,以及按键控制模块控制数码管切
换显示温度和湿度。由此画出的系统框图如下图所示。

图 26.4.1 DHT11数字温湿度传感器系统框图
各模块端口及信号连接如图 26.4.2所示:
图 26.4.2 顶层模块原理图
FPGA部分包括5个模块,顶层模块(top_dht11)、DHT11驱动模块(dht11_drive)、按键
消抖模块(key_debounce)、按键控制模块(dht11_key)、数码管驱动模块(seg_led)。其
中在顶层模块完成对其他模块的例化。
DHT11驱动模块(dht11_drive):dht11_drive模块通过单总线引脚读取DHT11的温度值和湿度值,并将读取到的数据输出至按键控制模块。
按键消抖模块(key_debounce):消除按键抖动,在检测到有按键按下或释放时对按键数
据进行消抖处理。
按键控制模块(dht11_key):根据输入的按键控制信号,将温度数据和湿度数据选择输
出至数码管显示模块。
数码管显示模块(seg_led):将输入的数据显示到数码管上。
顶层模块代码如下:
1
module top_dht11
(
2
input sys_clk
, //系统时钟
3
input sys_rst_n
, //系统复位
4
5
input key
, //按键
6
inout dht11
, //dht11总线
7
output [5
:0
] sel
, //数码管位选信号
8
output [7
:0
] seg_led //数码管段选信号
9
);
10 //wire define
11
wire [31
:0
] data_valid
;
12
wire [19
:0
] data
;
13
wire [5
:0
] point
;
14
15 //*****************************************************
16 //** main code
17 //*****************************************************
18
19 //dht11驱动模块
20 dht11_drive u_dht11_drive
(
21
.sys_clk
(sys_clk
) ,
22
.rst_n
(sys_rst_n
),
23
.dht11
(dht11
) ,
24
.data_valid
(data_valid
)
25
);
26
27 //数码管模块
28 seg_led u_seg_led
(
29
.clk
(sys_clk
),
30
.rst_n
(sys_rst_n
),
31
.sel
(sel
),
32
.seg_led
(seg_led
),
33
.data
(data
),
34
.point
(point
),
35
.en
(en
),
36
.sign
(sign
)
37
);
38
39 //消抖模块
40 key_debounce u_key_debounce
(
41
.sys_clk
(sys_clk
),
42
.sys_rst_n
(sys_rst_n
),
43
.key
(key
),
44
.key_flag
(key_flag
),
45
.key_value
(key_value
)
46
);
47
48 //按键控制
49 dht11_key u_dht11_key
(
50
.sys_clk
(sys_clk
), //系统时钟
51
.sys_rst_n
(sys_rst_n
), //系统复位
52
.key_flag
(key_flag
), //按键有效
53
.key_value
(key_value
),
54
.data_valid
(data_valid
),
55
.data
(data
),
56
.sign
(sign
),
57
.en
(en
),
58
.point
(point
)
59
);
60
61
endmodule
顶层模块完成对其他模块的例化,dht11_drivr模块输出的数据信号(data_valid)和
key_debounce模块输出的按键信号(key_flag和key_value)连接至dht11_key模块,dht11_key
模块输出的温度/湿度数据(data)以及数码管控制信号连接至seg_led模块。
由本章简介部分介绍的DHT11传输时序可以发现,DHT11的传输时序适合用状态机来编写。
DHT11驱动模块状态跳转图如下所示。

图 26.4.3 DHT11驱动状态跳转图
DHT11驱动模块使用三段式状态机来读取DHT11的温度和湿度值,从上图可以比较直观的看
到每个状态实现的功能以及跳转都下一个状态的条件。这里需要注意的一点是,由于DHT11温
度和湿度转换较慢,如果读取速度过快会导致DHT11无法响应的情况,所以我们在每次读操作
结束后延时两秒。
由于DHT11驱动模块的代码较长,我们仅贴出部分源代码。
131
case (cur_state
)
132 //上电后延时1秒等待DHT11稳定
133 st_power_on_wait
: begin
134
if(us_cnt
< POWER_ON_NUM
) begin
135 dht11_buffer
<= 1'bz
; //空闲状态释放总线
136 us_cnt_clr
<= 1'b0
;
137
end
138
else begin
139 next_state
<= st_low_20ms
;
140 us_cnt_clr
<= 1'b1
;
141
end
142
end
143 //FPGA发送起始信号(20ms的低电平)
144 st_low_20ms
: begin
145
if(us_cnt
< 20000
) begin
146 dht11_buffer
<= 1'b0
; //起始信号为低电平
147 us_cnt_clr
<= 1'b0
;
148
end
149
else begin
150 dht11_buffer
<= 1'bz
; //起始信号结束后释放总线
151 next_state
<= st_high_13us
;
152 us_cnt_clr
<= 1'b1
;
153
end
154
end
155 //等待DHT11的响应信号(等待10~20us)
156 st_high_13us
:begin
157
if (us_cnt
< 20
) begin
158 us_cnt_clr
<= 1'b0
;
159
if(dht11_neg
) begin //检测到DHT11响应信号
160 next_state
<= st_rec_low_83us
;
161 us_cnt_clr
<= 1'b1
;
162
end
163
end
164
else //超过20us未响应
165 next_state
<= st_delay
;
166
end
167 //等待DHT11的83us低电平响应信号结束
168 st_rec_low_83us
: begin
169
if(dht11_pos
)
170 next_state
<= st_rec_high_87us
;
171
end
172 //DHT11拉高87us通知FPGA准备接收数据
173 st_rec_high_87us
: begin
174
if(dht11_neg
) begin //准备时间结束
175 next_state
<= st_rec_data
;
176 us_cnt_clr
<= 1'b1
;
177
end
178
else begin //高电平准备接收数据
179 data_cnt
<= 6'd0
;
180 data_temp
<= 40'd0
;
181 step
<= 1'b0
;
182
end
183
end
184 //连续接收40位数据
185 st_rec_data
: begin
186
case(step
)
187 0
: begin //接收数据低电平
188
if(dht11_pos
) begin
189 step
<= 1'b1
;
190 us_cnt_clr
<= 1'b1
;
191
end
192
else //等待数据低电平结束
193 us_cnt_clr
<= 1'b0
;
194
end
195 1
: begin //接收数据高电平
196
if(dht11_neg
) begin
197 data_cnt
<= data_cnt
+ 1'b1
;
198 //判断接收数据为0/1
199
if(us_cnt
< 60
)
200 data_temp
<= {data_temp
[38
:0
],1'b0
};
201
else
202 data_temp
<= {data_temp
[38
:0
],1'b1
};
203 step
<= 1'b0
;
204 us_cnt_clr
<= 1'b1
;
205
end
206
else //等待数据高电平结束
207 us_cnt_clr
<= 1'b0
;
208
end
209
endcase
210
211
if(data_cnt
== 40
) begin //数据传输结束,验证校验位
212 next_state
<= st_delay
;
213
if(data_temp
[7
:0
] == data_temp
[39
:32
] + data_temp
[31
:24
]
214
+ data_temp
[23
:16
] + data_temp
[15
:8
])
215 data_valid
<= data_temp
[39
:8
];
216
end
217
end
218 //完成一次数据采集后延时2s
219 st_delay
:begin
220
if(us_cnt
< 2000_000
)
221 us_cnt_clr
<= 1'b0
;
222
else begin //延时结束后重新发送起始信号
223 next_state
<= st_low_20ms
;
224 us_cnt_clr
<= 1'b1
;
225
end
226
end
227
default : ;
228
endcase
从代码的第165行可知,在st_high_13us状态下,当DHT11长时间未响应时(超时20us),则进入延时状态(st_delay),延时两秒后重新发起开始信号。
图 26.4.4为SignalTap抓取的波形图,从图中可以清晰的看到DHT11驱动模块各个状态跳
转的时序图。st_rec_data为解析数据的状态,数据解析完成之后,开始进入st_delay(延时)
状态,延时状态结束之后,准备重新发起开始信号。
图 26.4.4 SignalTap抓取的波形图
按键控制模块的代码如下所示:
1
module dht11_key
(
2
input sys_clk
, //系统时钟
3
input sys_rst_n
, //系统复位
4
5
input key_flag
, //按键有效
6
input key_value
,
7
input [31
:0
] data_valid
,
8
output [31
:0
] data
,
9
output reg sign
,
10
output reg en
,
11
output reg [5
:0
] point
12
);
13
14 //reg define
15
reg flag
;
16
reg [7
:0
] data0
;
17
reg [7
:0
] data1
;
18
19 //*****************************************************
20 //** main code
21 //*****************************************************
22
23 //采集按键上升沿
24
assign key_pos
= (~key_value
) & key_flag
;
25 //由于要显示的数值由整数部分和小数部分组成,所以需要合并起来
26
assign data
= data1
* 100
+ data0
*10
;
27
28 //确定显示的数据是温度值还是湿度值,flag为“0”时显示温度,为“1”时显示湿度
29
always @ (posedge sys_clk
or negedge sys_rst_n
) begin
30
if(!sys_rst_n
)
31 flag
<= 1'b0
;
32
else if (key_pos
)
33 flag
<= flag
+ 1'b1
;
34
end
35
36
always @ (posedge sys_clk
or negedge sys_rst_n
) begin
37
if(!sys_rst_n
) begin
38 data0
<= 8'b0
;
39 data1
<= 8'b0
;
40
end
41
else if (flag
== 1'b0
) begin
42 data1
<= data_valid
[15
:8
];
43 data0
<= data_valid
[7
:0
];
44
end
45
else begin
46 data1
<= data_valid
[31
:24
];
47 data0
<= data_valid
[23
:16
];
48
end
49
end
50
51 //确定小数点显示的位置,这里在百位显示小数点
52
always @ (posedge sys_clk
or negedge sys_rst_n
) begin
53
if(!sys_rst_n
) begin
54 point
<= 6'b111111
;
55 en
<= 1'b0
;
56 sign
<= 1'b0
;
57
end
58
else begin
59 point
<= 6'b000100
;
60 en
<= 1'b1
;
61 sign
<= 1'b0
;
62
end
63
end
64
65
endmodule
在代码的第29行开始的always中,每按下一次按键KEY0,flag的值会改变一次,当flag的
值等于0时,输出的值为温度值;当flag的值等于1时,输出的值为湿度值。在代码的第26行中,
我们将温湿度值的整数部分放大100倍,小数部分放大10倍,显示在数码管上。
按键消抖模块和数码管显示模块分别在“按键控制蜂鸣器试验”和“动态数码管显示试验”
中作出了详细介绍,在此不再赘述。
下载验证
首先我们打开DHT11数字温湿度传感器实验工程,在工程所在的路径下打开top_dht11/par
文件夹,在里面找到“top_dht11.qpf”并双击打开。注意工程所在的路径名只能由字母、数
字以及下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 26.5.1所示。

图 26.5.1 DHT11工程打开界面
将DHT11接插到开拓者开发板上的单总线接口,如下图所示。然后将下载器一端连电脑,
另一端与开发板上对应端口连接,最后连接
电源线并打开电源开关。
图 26.5.2 DHT11接插至开拓者开发板
工程打开后通过点击工具栏中的“Programmer”图标(图中红框位置)打开下载界面,通过
“Add File”按钮选择DHT11数字温湿度传感器工程中top_dht11/par/output_files目录下
“top_dht11.sof”文件,下载界面如图 26.5.3所示(图中已下载完毕)。

图 26.5.3 下载界面
接下来我们点击左侧的Start,下载程序,验证温湿度传感器数码管显示功能。
下载完成后,数码管默认显示的是温度值,当按下KEY0按键后,数码管会显示湿度值,说
明本次DHT11数字温湿度传感器实验验证成功。这里需要注意的是温度值的单位是:℃(摄氏
度);湿度值的单位是:%RH(相对湿度),温度值和湿度值的单位在数码管上没有显示。

图 26.5.4 开发板KEY0按键