基于FPGA与DHT11的温湿度测量

开发板:max10开发板
1、DHT11简介
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。

**数据格式:**8位湿度整数数据+8位湿度小数数据+8位温度整数数据+8位温度小数数据+8bit校验和。数据传送正确时校验和数据等于“8位湿度整数数据+8位湿度小数数据+8位温度整数数据+8位温度小数数据”所得结果的末8位。温度小数数据的最高位为温度的符号位,小数的最高位为1,表示测量温度为负,温度小数最高位为0 ,则表示测量温度为正。注意在读取数据的时候注意传感器的精度,然后进行数据处理。

**数据时序:**传感器上电后,要等待 1s 以越过不稳定状态。在此期间无需发送任何指令。然后用户MCU发送一次开始信号后(>18ms的低电平),DHT11从低功耗模式转换到高速模式。等待主机开始信号结束后(拉高20~40us),DHT11发送响应信号(80us高电平),送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据。从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
基于FPGA与DHT11的温湿度测量_第1张图片
每一bit数据都以50us低电平时隙开始,高电平的长短决定了数据位是0还是1。格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
基于FPGA与DHT11的温湿度测量_第2张图片
基于FPGA与DHT11的温湿度测量_第3张图片
2、硬件电路
典型硬件电路见下图,和FPGA连接即可。
基于FPGA与DHT11的温湿度测量_第4张图片
3、DHT11驱动代码
``Verilog
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分频时钟
//系统时钟为50M通过偶分频得到1M时钟即50分频
//用计数器从0技术到50/2-1 偶分频N/2-1
//该项目使用1M分频是因为DHT11时序主要以us为单位
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 /0
begin
if(us_cnt < POWER_ON_NUM) //没有达到上电延迟时间1s
begin
dht11_buffer <= 1’bz; //空闲状态释放总线
us_cnt_clr <= 1’b0;
end
else
begin //进入下一状态,清零us计数器
next_state <= st_low_20ms;
us_cnt_clr <= 1’b1;
end
end
//FPGA发送起始信号(20ms的低电平)
st_low_20ms: //1
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://2
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: //3
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

你可能感兴趣的:(FPGA,fpga,verilog,串口通信)