温湿度复合传感器

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 

猜你喜欢

转载自blog.csdn.net/gemengxia/article/details/115428596