zl程序教程

您现在的位置是:首页 >  后端

当前栏目

【正点原子FPGA连载】 第二十八章DHT11数字温湿度传感器实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

开发 指南 数字 实验 FPGA 原子 传感器 连载
2023-09-11 14:20:38 时间

1)实验平台:正点原子新起点V2开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子FPGA感兴趣的同学可以加群讨论:994244016
4)关注正点原子公众号,获取最新资料更新
在这里插入图片描述

第二十八章DHT11数字温湿度传感器实验

DHT11是奥松(AoSong)公司生产的一款数字温湿度复合传感器。该传感器用途广泛、抗干扰能力强、可靠性高,在家电、汽车、医疗等方面有广泛的应用。本章我们将使用FPGA开发板实现对DHT11数据的采集,并将温湿度数据显示在数码管上。
本章包括以下几个部分:
2727.1简介
27.2实验任务
27.3硬件设计
27.4程序设计
27.5下载验证

28.1简介
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它使用专用的数字模块采集技术和温湿度传感技术,具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC(负温度系数热敏电阻器)测温元件,并与一个高性能8位MCU相连接。每个DHT11传感器都在湿度校验室中校准过,校准系数以程序的形式储存在OTP(一次性可编程)内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。DHT11使用单线制串行接口,4针单排引脚封装,信号传输距离可达20米以上,在各类应用甚至很苛刻的环境中都能正常工作。
在这里插入图片描述

图 28.1.1 DHT11内部原理图
上图为DHT11的内部原理图,可以看出感湿原件、感温元件和OTP内存直接连接在内部一个八位MCU上,该MCU通过计算得出测量数值。
DATA用于FPGA与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分为整数部分和小数部分,数据格式如下:
一次完整的数据传输为40bit,高位在前。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和数据
数据传送正确时校验和数据等于“8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据”所得结果的末8位)。
接下来我们介绍一下DHT11的传输时序,DHT11的数据发送流程如图 28.1.2错误!未找到引用源。所示。
在这里插入图片描述

图 28.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”时序图如图 28.1.3和图 28.1.4所示。
在这里插入图片描述

图 28.1.3 DHT11数据位“0”时序图
在这里插入图片描述

图 28.1.4 DHT11数据位“1”时序图
需要注意的是,DHT11的温度和湿度转换较慢,如果读取速度过快会导致DHT11无法响应的情况。本次实验每100ms读取一次,如果DHT11长时间未响应,则重新发起开始信号。
28.2实验任务
本节实验任务是使用新起点FPGA开发板完成对DHT11温湿度数据的采集,并通过按键KEY0控制温度和湿度在数码管上的切换显示。
28.3硬件设计
我们的新起点开发板上有一个DS18B20/DTH11的扩展接口,该接口可以用来连接DS18B20或DHT11。其原理图如图 28.3.1所示。
在这里插入图片描述

图 28.3.1 DHT11/DS18B20扩展接口原理图
DHT11通过4个排针与外部连接,如图 28.3.2所示,将DHT11直接插在开发板上即可,接插之前注意正负极的方向,以免短路。
在这里插入图片描述

图 28.3.2 DHT11连接实物图
本次实验的管脚分配如下表所示:
表 28.3.1 管脚分配
在这里插入图片描述

本节实验使用的管脚基本都是数码管管脚,这里不再给出TCL文件。
28.4程序设计
根据实验任务,我们可以大致规划出系统的工作流程:FPGA控制DHT11采集温度和湿度,并将收到的温度和湿度数据转换成十进制显示在数码管上。本次实验使用按键来控制数码管显示温度和湿度,所以还需要添加消抖模块来对按键进行消抖,以及按键控制模块控制数码管切换显示温度和湿度。由此画出的系统框图如下图所示。
在这里插入图片描述

图 28.4.1 DHT11数字温湿度传感器系统框图
各模块端口及信号连接如图 28.4.2所示:
在这里插入图片描述

图 28.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       inout          dht11    ,  //DHT11总线
6       input          key      ,  //按键
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      
24      .dht11          (dht11),
25      .data_valid     (data_valid)
26      );
27  
28  
29  //按键消抖模块
30  key_debounce u_key_debounce(
31      .sys_clk        (sys_clk),
32      .sys_rst_n      (sys_rst_n),
33      
34      .key            (key),
35      .key_flag       (key_flag),
36      .key_value      (key_value)
37      );
38  
39  //按键控制温/湿度显示
40  dht11_key u_dht11_key(
41      .sys_clk        (sys_clk),
42      .sys_rst_n      (sys_rst_n),
43      
44      .key_flag       (key_flag),
45      .key_value      (key_value),
46      .data_valid     (data_valid),
47      
48      .data           (data),
49      .sign           (sign),
50      .en             (en),                     
51      .point          (point)
52      );
53  
54  //动态数码管显示模块
55  seg_led u_seg_led (
56      .clk            (sys_clk),
57      .rst_n          (sys_rst_n),
58      
59      .seg_sel        (sel),
60      .seg_led        (seg_led),
61      
62      .data           (data),
63      .point          (point),
64      .en             (en),
65      .sign           (sign)
66      );
67  
68  endmodule 

顶层模块完成对其他模块的例化,dht11_drivr模块输出的数据信号(data_valid)和key_debounce模块输出的按键信号(key_flag和key_value)连接至dht11_key模块,dht11_key模块输出的温度/湿度数据(data)以及数码管控制信号连接至seg_led模块。
由本章简介部分介绍的DHT11传输时序可以发现,DHT11的传输时序适合用状态机来编写。DHT11驱动模块状态跳转图如下所示。
在这里插入图片描述

图 28.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),延时两秒后重新发起开始信号。
图 28.4.4为SignalTap抓取的波形图,从图中可以清晰的看到DHT11驱动模块各个状态跳转的时序图。st_rec_data为解析数据的状态,数据解析完成之后,开始进入st_delay(延时)状态,延时状态结束之后,准备重新发起开始信号。
在这里插入图片描述

图 28.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       
9       output     [31:0] data,
10      output reg        sign,
11      output            en,                     
12      output     [ 5:0] point
13  );
14  
15  //reg define                            
16  reg       flag ; // 温/湿度标志信号
17  reg [7:0] data0; // 小数部分
18  reg [7:0] data1; // 整数部分
19  
20  //*****************************************************
21  //**                    main code
22  //*****************************************************
23  
24  //数码管使能信号
25  assign en    = 1'b1;
26  
27  //显示的数值为 (整数 + 0.1*小数)*100
28  assign data  = data1 * 100 + data0*10;
29  
30  //小数点左移两位
31  assign point = 6'b000100;
32  
33  //检测到按键按下时,切换温/湿度标志信号
34  always @ (posedge sys_clk or negedge sys_rst_n) begin 
35      if(!sys_rst_n)                                    
36          flag <= 1'b0;
37      else if (key_flag & (~key_value))
38          flag <= ~flag;
39  end 
40  
41  //flag为“0”时显示温度,为“1”时显示湿度
42  always @ (posedge sys_clk or negedge sys_rst_n) begin
43      if(!sys_rst_n) begin
44          data0 <= 8'd0;
45          data1 <= 8'd0;
46          sign  <= 1'b0;
47      end
48      else if(flag == 1'b0) begin
49          data0 <= data_valid[6:0];  //温度小数部分最高位为符号位
50          data1 <= data_valid[15:8];
51          if(data_valid[7])
52              sign <= 1'b1;          //bit7为1表示负温度
53          else
54              sign <= 1'b0;
55      end
56      else begin
57          data0 <= data_valid[23:16];
58          data1 <= data_valid[31:24];
59          sign  <= 1'b0;
60      end
61  end
62  
63  endmodule 

在代码的第34行开始的always中,每按下一次按键KEY0,flag的值会改变一次,当flag的值等于0时,输出的值为温度值;当flag的值等于1时,输出的值为湿度值。在代码的第28行中,我们将温湿度值的整数部分放大100倍,小数部分放大10倍,显示在数码管上。
按键消抖模块和数码管显示模块分别在“按键控制蜂鸣器试验”和“动态数码管显示试验”中作出了详细介绍,在此不再赘述。
28.5下载验证
首先将DHT11接插到新起点开发板上的单总线接口,如下图所示。然后将下载器一端连电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。
在这里插入图片描述

图 28.5.1 DHT11接插至新起点开发板
接下来我们下载程序,验证温湿度传感器数码管显示功能。下载完成后,数码管默认显示的是温度值,当按下KEY0按键后,数码管会显示湿度值,说明本次DHT11数字温湿度传感器实验验证成功。这里需要注意的是温度值的单位是:℃(摄氏度);湿度值的单位是:%RH(相对湿度),温度值和湿度值的单位在数码管上没有显示。
在这里插入图片描述

图 28.5.2 开发板KEY0按键