Veriog之DHT11温湿度,拿来就用模块

背景

拿来主义者,复用主义者,然而找了一圈没找到合适的,这个模块一搜能搜到很多关于该模块的代码,但是不同项目,设计的FPGA系统时钟可能不一样,换一个时钟频率就要改代码,麻烦。假设DH11模块使用的系统时钟频率为50MHz,而当前FPGA设计的主时钟为33MH在,而除了DH11之外,其它部分没有使用到50MHz的时钟,如果为了使用DH11的现有代码,就要PLL倍频时钟产生一个50MHz的时钟,这样就浪费了一个PLL资源,不划算;如果DH11模块设计的时钟频率低于系统时钟频率,可以分频实现,但是分频后,假设这个分频得到的时钟也就只有DH11需要使用,为了确保时序能过,很可能需要对分频得到的时钟加一个GBUF,也浪费了资源。

于是为了能适应不住时钟,自己写了这个模块,对时钟频率没有明确要求。

代码

`timescale 1ns / 1ps
/*--------------------------------------------------------------------*\
FileName        : dht11.v
Author          :hpy
Email           :yuan_hp@qq.com
Date            :2022年07月29日
Description     :
1.DH11温湿度检测实现,只需系统时钟和多长时间检测一次就可以使用该代码
2.当读取的数据校验不通过时,温湿度的数据所有bit全为1
3.双向端口已在该模块内部处理,外部不要进行三态处理

4.调用示例
	dht11 #(
    	.fclk(100000000),  //系统时钟频率Hz
        .delay_s( 3)      //多少秒采集一次数据
	)dht11_u1 (
        .clk( clk),
        .rst_n(rst_n),

        .temp(temp), //高8位为整数 低8位为小数  温度 
        .humi(humi), //高8位为整数 低8位为小数  湿度
        //数据线
        .dat(dat_io) 
	);
\*--------------------------------------------------------------------*/
module dht11 #(
    parameter fclk = 50000000 , //系统时钟频率
    parameter delay_s = 3  //多少秒进行一次采集数据,至少为1s
)(
        input clk,
        input rst_n,

        output reg [15:0]temp, //高8位为整数 低8位为小数  温度 
        output reg [15:0]humi, //高8位为整数 低8位为小数  湿度
        //数据线
        inout dat 
);


function integer clog2(input [63:0] i);
    for(clog2=0;i>0 ; clog2=clog2+1)
        i=i>>1; 
endfunction 
// ----------------- ------ 参数计算 -------------------
localparam [63:0] clk10us = fclk*10/1000000;
localparam [63:0] clk20us = fclk*20/1000000;
localparam [63:0] clk26us = fclk*26/1000000;
localparam [63:0] clk30us = fclk*30/1000000;
localparam [63:0] clk40us = fclk*40/1000000;
localparam [63:0] clk50us = fclk*50/1000000;
localparam [63:0] clk68us = fclk*65/1000000;
localparam [63:0] clk80us = fclk*80/1000000;
localparam [63:0] clk20ms = fclk*2/100;

localparam cntmax = fclk*delay_s; //多长时间采样读取一次数据,该值必须大于1s,因为启动时等待也是由其决定

//----------------------- reg --------------------------
reg [clog2(clk20ms+255) : 0 ] clkcnt ;

wire clk10us_flag ; //作为门限,避免时钟太快,器件没有及时响应而卡死
wire clk20us_flag , clk80us_flag , clk26us_flag , clk30us_flag,clk50us_flag , clk40us_flag , clk65us_flag , clk20ms_flag ;
reg [1:0]dat_in_f;
reg dat_in_syn ;
reg [2:0] state ; 
reg [39:0] rdat ; 
reg [5:0]bitcnt ;
reg dat_r ; 
reg [clog2(cntmax-1) - 1 : 0 ] startcnt ; //
reg startupok ; 

wire dat_in;
assign dat_in = dat;
assign dat = (dat_r==1'b0)? 1'b0 : 1'hz ; 

assign clk10us_flag = (clkcnt>= clk10us-1'b1)? 1'b1  : 1'b0;
assign clk20us_flag = (clkcnt>= clk20us-1'b1)? 1'b1  : 1'b0;
assign clk26us_flag = (clkcnt>= clk26us-1'b1)? 1'b1  : 1'b0;
assign clk30us_flag = (clkcnt>= clk30us-1'b1)? 1'b1  : 1'b0;
assign clk40us_flag = (clkcnt>= clk40us-1'b1)? 1'b1  : 1'b0;
assign clk50us_flag = (clkcnt>= clk50us-1'b1)? 1'b1  : 1'b0;
assign clk65us_flag = (clkcnt>= clk68us-1'b1)? 1'b1  : 1'b0;
assign clk80us_flag = (clkcnt>= clk80us-1'b1)? 1'b1  : 1'b0;
assign clk20ms_flag = (clkcnt>= clk20ms-1'b1)? 1'b1  : 1'b0;

always @(posedge clk) begin
    if(~rst_n) begin 
        dat_in_syn <= 1'b1 ; 
        dat_in_f <=3'b111;
    end else begin
        dat_in_syn  <= dat_in;
        dat_in_f    <= {dat_in_f[0] , dat_in_syn} ;  
    end
end

always @(posedge clk) begin
    if(~rst_n) begin 
        startupok <= 1'b0;
        startcnt <= cntmax - 1'b1 ;
    end else begin 
        startcnt <=  (~|state)? startcnt - 1'b1 :  cntmax - 1'b1 ;
        startupok <= (~|startcnt) ? 1'b1 : 1'b0;
    end
end

//
localparam 
    idel = 3'b000 ,
    startreq = 3'b001 ,
    startwait = 3'b011,
    startack  = 3'b010,
    trbit     =3'b110,
    getbit    =3'b100,
    stopt      =3'b101;


always@(posedge clk)
begin
    if(!rst_n)begin
        state <= idel ; 
        clkcnt <= 0 ;
        dat_r <= 1'b1 ; 
        bitcnt <= 6'h0;
        rdat <= 40'hfffffffff;
    end
    else begin
        clkcnt <= ( &clkcnt ) ? clkcnt : clkcnt + 1'b1;
        case(state)
            idel: 
                begin
                    clkcnt <= 0 ;
                    dat_r <= 1'b1 ; 
                    bitcnt <= 6'h27 ; 
                    if(startupok)begin
                        state <= startreq ;  
                        dat_r <= 1'b0 ; 
                    end
                end
            startreq :
                begin
                    if(&clkcnt) begin 
                        state <= idel ; 
                        dat_r <= 1'b1;
                    end else begin 
                        if( clk20ms_flag & ~dat_r) begin //拉低20ms,释放总线
                            // state <= idel ; 
                            dat_r <=1'b1;
                            clkcnt <= 0 ; 
                        end else if( dat_r & (dat_in_f==2'b10)) begin
                            clkcnt <= 0;
                            state  <= startwait ;
                        end
                    end
                end
            startwait : 
                begin
                    if(&clkcnt ) begin 
                        state <= idel ; 
                    end else begin 
                        if( (dat_in_f==2'b01) & clk80us_flag ) begin //收到响应信号, 低电平83us
                            state <= startack ; 
                            clkcnt <= 0 ;
                        end
                    end                    
                end 
            startack :
                begin
                    if(&clkcnt ) begin 
                        state <= idel ; 
                    end else begin 
                        if( (dat_in_f==2'b10) &clk80us_flag ) begin //DHT 1->0,准备输出数据 1持续87us
                            state <= trbit ; 
                            clkcnt <= 0 ;
                            bitcnt <= 6'h27 ; 
                        end
                    end                    
                end
            trbit : //传输数据
                begin
                    if(&clkcnt ) begin 
                        state <= idel ; 
                    end else begin 
                        if( (dat_in_f==2'b01) & clk50us_flag) begin 
                            state <= getbit ; 
                            clkcnt <= 0 ;
                        end
                    end                    
                end 
            getbit : //判断数据的为1 or 0
                begin
                    if(&clkcnt ) begin 
                        state <= idel ; 
                    end else begin 
                        if((dat_in_f==2'b10) & clk20us_flag ) begin //
                            bitcnt <=bitcnt - 1'b1 ; 
                            if( clk65us_flag)
                                rdat <= {rdat[38:0],1'b1} ; 
                            else 
                                rdat <= {rdat[38:0],1'b0} ;

                            if(~|bitcnt) begin
                                state <= stopt ; 
                            end else begin
                                state <= trbit ; 
                            end
                            clkcnt <= 0 ;
                        end
                    end                    
                end 
            stopt:  
                begin
                    if(rdat[7:0] == rdat[15:8] + rdat[23:16] + rdat[31:24] + rdat[39:32]) begin 
                        temp <= rdat[23:8] ;
                        humi <= rdat[39:24] ;
                    end else begin
                        temp <= 16'hffff ;
                        humi <= 16'hffff ;                        
                    end 

                    if(&clkcnt) begin 
                        state <= idel ; 
                        dat_r <= 1'b1;
                    end else begin
                        if( (dat_in_f==2'b01) & clk10us_flag ) begin
                            state <= idel;
                        end                          
                    end
                end 
            default : begin
                state <= idel ; 
                dat_r <=1'b1;
            end                         
        endcase 
    end
end
endmodule

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值