1. DHT11简介
DHT11是一款温湿度复合传感器,传感器包括一个电容式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。
1.1 数据格式
DHT11采用单总线通信,一次传送40位数据,高位先出。
校验位为前面四个字节之和的末8位:
00110101+00000000+00011000+00000100=01010001
温度小数部分的最高位为1,则表示负温度,否则表示正温度。
1.2 数据时序
用户主机(FPGA)发送一次信号后,DHT11从低功耗模式转换为高速模式,待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集。
注意:
1、DHT11上电后要等待1s以跳过不稳定状态,在此期间不能发送任何指令。
2、结束信号后,DHT11内部重测环境温度数据,并记录数据,等待外部信号的到来。
3、主机从DHT11读取的温度湿度数据总是前一次的测量值,如两次测量时间很长,请连续读两次,以第二次获得的值为实时温度湿度值。
4、不建议连续多次读取传感器,每次读取传感器间隔大于2秒即可获得较为准确的数据。
2. 程序设计
使用FPGA开发板完成对DHT11温湿度数据的采集,并通过按键控制温度和湿度在数码管上切换显示。
系统框图
代码
module top_dht11(
input sys_clk , //系统时钟
input sys_rst_n, //系统复位
inout dht11 , //DHT11总线
input key , //按键
output [5:0] sel , //数码管位选信号
output [7:0] seg_led //数码管段选信号
);
//wire define
wire [31:0] data_valid;
wire [19:0] data ;
wire [5:0] point ;
//*****************************************************
//** main code
//*****************************************************
//dht11驱动模块
dht11_drive u_dht11_drive (
.sys_clk (sys_clk),
.rst_n (sys_rst_n),
.dht11 (dht11),
.data_valid (data_valid)
);
//按键消抖模块
key_debounce u_key_debounce(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key (key),
.key_flag (key_flag),
.key_value (key_value)
);
//按键控制温/湿度显示
dht11_key u_dht11_key(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_flag (key_flag),
.key_value (key_value),
.data_valid (data_valid),
.data (data),
.sign (sign),
.en (en),
.point (point)
);
//动态数码管显示模块
seg_led u_seg_led (
.clk (sys_clk),
.rst_n (sys_rst_n),
.seg_sel (sel),
.seg_led (seg_led),
.data (data),
.point (point),
.en (en),
.sign (sign)
);
endmodule
module dht11_drive (
input sys_clk , //系统时钟
input rst_n , //系统复位
inout dht11 , //dht11温湿度传感器单总线
output reg [31:0] data_valid //有效输出数据
);
//parameter define
parameter POWER_ON_NUM = 1000_000; //上电延时等待时间,单位us
//状态机各个状态
parameter st_power_on_wait = 3'd0; //上电延时等待
parameter st_low_20ms = 3'd1; //主机发送20ms低电平
parameter st_high_13us = 3'd2; //主机释放总线13us
parameter st_rec_low_83us = 3'd3; //接收83us低电平响应
parameter st_rec_high_87us = 3'd4; //等待87us高电平(准备接收数据)
parameter st_rec_data = 3'd5; //接收40位数据
parameter st_delay = 3'd6; //延时等待,延时完成后重新操作DHT11
//reg define
reg [2:0] cur_state ; //当前状态
reg [2:0] next_state ; //下一个状态
reg [4:0] clk_cnt ; //分频计数器
reg clk_1m ; //1Mhz时钟
reg [20:0] us_cnt ; //1微秒计数器
reg us_cnt_clr ; //1微秒计数器清零信号
reg [39:0] data_temp ; //缓存接收到的数据
reg step ; //数据采集状态
reg [5:0] data_cnt ; //接收数据用计数器
reg dht11_buffer; //DHT11输出信号
reg dht11_d0 ; //DHT11输入信号寄存器0
reg dht11_d1 ; //DHT11输入信号寄存器1
//wire define
wire dht11_pos ; //DHT11上升沿
wire dht11_neg ; //DHT11下降沿
//*****************************************************
//** main code
//*****************************************************
assign dht11 = dht11_buffer;
assign dht11_pos = ~dht11_d1 & dht11_d0; //采集上升沿
assign dht11_neg = dht11_d1 & ~dht11_d0; //采集下降沿
//得到1Mhz分频时钟
always @ (posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
clk_cnt <= 5'd0;
clk_1m <= 1'b0;
end
else if (clk_cnt < 5'd24)
clk_cnt <= clk_cnt + 1'b1;
else begin
clk_cnt <= 5'd0;
clk_1m <= ~ clk_1m;
end
end
//对DHT11输入信号连续寄存两次,用于边沿检测
always @ (posedge clk_1m or negedge rst_n) begin
if (!rst_n) begin
dht11_d0 <= 1'b1;
dht11_d1 <= 1'b1;
end
else begin
dht11_d0 <= dht11;
dht11_d1 <= dht11_d0;
end
end
//1微秒计数器
always @ (posedge clk_1m or negedge rst_n) begin
if (!rst_n)
us_cnt <= 21'd0;
else if (us_cnt_clr)
us_cnt <= 21'd0;
else
us_cnt <= us_cnt + 1'b1;
end
//状态跳转
always @ (posedge clk_1m or negedge rst_n) begin
if (!rst_n)
cur_state <= st_power_on_wait;
else
cur_state <= next_state;
end
//状态机读取DHT11数据
always @ (posedge clk_1m or negedge rst_n) begin
if(!rst_n) begin
next_state <= st_power_on_wait;
data_temp <= 40'd0;
step <= 1'b0;
us_cnt_clr <= 1'b0;
data_cnt <= 6'd0;
dht11_buffer <= 1'bz;
end
else begin
case (cur_state)
//上电后延时1秒等待DHT11稳定
st_power_on_wait : begin
if(us_cnt < POWER_ON_NUM) begin
dht11_buffer <= 1'bz; //空闲状态释放总线
us_cnt_clr <= 1'b0;
end
else begin
next_state <= st_low_20ms;
us_cnt_clr <= 1'b1;
end
end
//FPGA发送起始信号(20ms的低电平)
st_low_20ms: begin
if(us_cnt < 20000) begin
dht11_buffer <= 1'b0; //起始信号为低电平
us_cnt_clr <= 1'b0;
end
else begin
dht11_buffer <= 1'bz; //起始信号结束后释放总线
next_state <= st_high_13us;
us_cnt_clr <= 1'b1;
end
end
//等待DHT11的响应信号(等待10~20us)
st_high_13us:begin
if (us_cnt < 20) begin
us_cnt_clr <= 1'b0;
if(dht11_neg) begin //检测到DHT11响应信号
next_state <= st_rec_low_83us;
us_cnt_clr <= 1'b1;
end
end
else //超过20us未响应
next_state <= st_delay;
end
//等待DHT11的83us低电平响应信号结束
st_rec_low_83us: begin
if(dht11_pos)
next_state <= st_rec_high_87us;
end
//DHT11拉高87us通知FPGA准备接收数据
st_rec_high_87us: begin
if(dht11_neg) begin //准备时间结束
next_state <= st_rec_data;
us_cnt_clr <= 1'b1;
end
else begin //高电平准备接收数据
data_cnt <= 6'd0;
data_temp <= 40'd0;
step <= 1'b0;
end
end
//连续接收40位数据
st_rec_data: begin
case(step)
0: begin //接收数据低电平
if(dht11_pos) begin
step <= 1'b1;
us_cnt_clr <= 1'b1;
end
else //等待数据低电平结束
us_cnt_clr <= 1'b0;
end
1: begin //接收数据高电平
if(dht11_neg) begin
data_cnt <= data_cnt + 1'b1;
//判断接收数据为0/1
if(us_cnt < 60)
data_temp <= {
data_temp[38:0],1'b0};
else
data_temp <= {
data_temp[38:0],1'b1};
step <= 1'b0;
us_cnt_clr <= 1'b1;
end
else //等待数据高电平结束
us_cnt_clr <= 1'b0;
end
endcase
if(data_cnt == 40) begin //数据传输结束,验证校验位
next_state <= st_delay;
if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24]
+ data_temp[23:16] + data_temp[15:8])
data_valid <= data_temp[39:8];
end
end
//完成一次数据采集后延时2s
st_delay:begin
if(us_cnt < 2000_000)
us_cnt_clr <= 1'b0;
else begin //延时结束后重新发送起始信号
next_state <= st_low_20ms;
us_cnt_clr <= 1'b1;
end
end
default : ;
endcase
end
end
endmodule
module dht11_key(
input sys_clk,
input sys_rst_n,
input key_flag,
input key_value,
input [31:0] data_valid,
output [31:0] data,
output reg sign,
output en,
output [ 5:0] point
);
//reg define
reg flag ; // 温/湿度标志信号
reg [7:0] data0; // 小数部分
reg [7:0] data1; // 整数部分
//*****************************************************
//** main code
//*****************************************************
//数码管使能信号
assign en = 1'b1;
//显示的数值为 (整数 + 0.1*小数)*100
assign data = data1 * 100 + data0*10;
//小数点左移两位
assign point = 6'b000100;
//检测到按键按下时,切换温/湿度标志信号
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
flag <= 1'b0;
else if (key_flag & (~key_value))
flag <= ~flag;
end
//flag为“0”时显示温度,为“1”时显示湿度
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
data0 <= 8'd0;
data1 <= 8'd0;
sign <= 1'b0;
end
else if(flag == 1'b0) begin
data0 <= data_valid[6:0]; //温度小数部分最高位为符号位
data1 <= data_valid[15:8];
if(data_valid[7])
sign <= 1'b1; //bit7为1表示负温度
else
sign <= 1'b0;
end
else begin
data0 <= data_valid[23:16];
data1 <= data_valid[31:24];
sign <= 1'b0;
end
end
endmodule