背景
拿来主义者,复用主义者,然而找了一圈没找到合适的,这个模块一搜能搜到很多关于该模块的代码,但是不同项目,设计的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