本帖最后由 正点原子运营 于 2023-11-30 15:32 编辑
第二十八章 DHT11数字温湿度传感器实验
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
6)FPGA技术交流QQ群:435699340
DHT11是奥松(AoSong)公司生产的一款数字温湿度复合传感器。该传感器用途广泛、抗干扰能力强、可靠性高,在家电、汽车、医疗等方面有广泛的应用。本章我们将使用FPGA开发板实现对DHT11数据的采集,并将温湿度数据显示在数码管上。 本章包括以下几个部分: 1.1简介 1.2实验任务 1.3硬件设计 1.4程序设计 1.5下载验证
1.1 简介DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它使用专用的数字模块采集技术和温湿度传感技术,具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC(负温度系数热敏电阻器)测温元件,并与一个高性能8位MCU相连接。每个DHT11传感器都在湿度校验室中校准过,校准系数以程序的形式储存在OTP(一次性可编程)内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。DHT11使用单线制串行接口,4针单排引脚封装,信号传输距离可达20米以上,在各类应用甚至很苛刻的环境中都能正常工作。 上图为DHT11的内部原理图,可以看出感湿原件、感温元件和OTP内存直接连接在内部一个八位MCU上,该MCU通过计算得出测量数值。 DATA用于FPGA与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分为整数部分和小数部分,数据格式如下: 一次完整的数据传输为40bit,高位在前。 数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和数据。 数据传送正确时校验和数据等于“8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据”所得结果的末8位)。 接下来我们介绍一下DHT11的传输时序,DHT11的数据发送流程如图 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所示。 需要注意的是,DHT11的温度和湿度转换较慢,如果读取速度过快会导致DHT11无法响应的情况。本次实验每100ms读取一次,如果DHT11长时间未响应,则重新发起开始信号。
1.2 实验任务本节实验任务是使用ATK-DFPGL22G开发板完成对DHT11温湿度数据的采集,并通过按键KEY0控制温度和湿度在数码管上的切换显示。
1.3 硬件设计我们的ATK-DFPGL22G开发板上有一个DS18B20/DTH11的扩展接口,该接口可以用来连接DS18B20或DHT11。其原理图如图 28.3.1所示。 图 28.3.1 DHT11/DS18B20扩展接口原理图 DHT11通过4个排针与外部连接,如图 28.3.2所示,将DHT11直接插在开发板上即可,接插之前注意正负极的方向,以免短路。 本次实验的管脚分配如下表所示:
1.4 程序设计根据实验任务,我们可以大致规划出系统的工作流程:FPGA控制DHT11采集温度和湿度,并将收到的温度和湿度数据转换成十进制显示在数码管上。本次实验使用按键来控制数码管显示温度和湿度,所以还需要添加消抖模块来对按键进行消抖,以及按键控制模块控制数码管切换显示温度和湿度。由此画出的系统框图如下图所示。 图 28.4.1 DHT11数字温湿度传感器系统框图 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驱动模块状态跳转图如下所示: 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
复制代码代码第143行FPGA发送20ms的起始信号,使dht11起始信号为低电平,起始信号结束后释放总线;进入st_high_13us状态(等待DHT11的响应信号),从代码的第156行可知,在st_high_13us状态下,当DHT11长时间未响应时(超时20us),则进入延时状态(st_delay),延时两秒后重新发起开始信号。检测到DHT11响应信号,则进入st_rec_low_83us。 代码第167行为st_rec_low_83us的状态为等待相应的时间,拉低数据线,作为响应信号。响应结束后,进入st_rec_high_87us(准备接收数据)状态,主要是为接收数据做准备。 代码第185行st_rec_data为解析数据的状态,数据解析完成之后,开始进入st_delay(延时)状态,延时状态结束之后,准备重新发起开始信号。 按键控制模块的代码如下所示: - 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倍,显示在数码管上。 按键消抖模块和数码管显示模块分别在“按键控制蜂鸣器实验”和“动态数码管显示实验”中有详细的介绍,如果需要了解可以参考上述两个相关的实验进行学习。
1.5 下载验证首先将DHT11接插到ATK-DFPGL22G开发板上的单总线接口,如下图所示。然后将下载器一端连电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。 图 28.5.1 DHT11接插至ATK-DFPGL22G开发板 接下来我们下载程序,验证温湿度传感器数码管显示功能。下载完成后,数码管默认显示的是温度值,当按下KEY0按键后,数码管会显示湿度值,说明本次DHT11数字温湿度传感器实验验证成功。这里需要注意的是温度值的单位是:℃(摄氏度);湿度值的单位是:%RH(相对湿度),温度值和湿度值的单位在数码管上没有显示。 |