实验展示
DHT11温湿度数码管显示.MP4 download:https://cdn.jsdelivr.net/gh/zikwq/Blog_Pic/4c7607b1dc9ab6b2cfc246a2fffc6624.mp4
DHT11相关文档
DHT11数据格式
时序交互
信号持续时间
程序框图
状态机部分的状态转移图
整体波形图绘制
时钟的绘制,采用clk_us
程序部分
顶层模块:
```verilog dht11.v module dht11( input wire sys_clk, input wire sys_rst_n, inout wire dht11, input wire key,
output wire [7:0]segdata,
output wire [2:0]segcs
); wire key_flag; wire [19:0] data_out;
key_filter
(
.CNT_MAX(20'd999_999)
) key_filter_inst ( .sys_clk (sys_clk) , .sys_rst_n (sys_rst_n) , .key_in (key) , .led_out(), .key_flag(key_flag) ); dht11_ctrl dht11_ctrl_inst ( .sys_clk(sys_clk), .sys_rst_n(sys_rst_n), .key_flag(key_flag), .dht11(dht11), .data_out(data_out), .sign() );
seg seg_inst ( .clk(sys_clk), .rst_n(sys_rst_n), .tenvalue(data_out), // 这里连接没有问题 .segdata(segdata), .segcs(segcs) );
endmodule
6位数码管驱动模块:
```verilog seg.v
/*
数码管显示驱动,目前是直接设置固定数据
通过将tenvalue设置到输入端口,可以作为子模块,通过传入数据显示在数码管上面
*/
module seg(
input clk,
input rst_n,
input [19:0]tenvalue,
output reg [7:0]segdata,
output reg [2:0]segcs
);
reg[31:0] count;
reg[24:0]count1ms;
reg[2:0]number;
reg clk1ms;
parameter sample=2'b00,
display=2'b01;
//-----------数码管驱动时钟-----------
always@(posedge clk)
begin
if(count1ms >= 25'd50000) // 1ms的计数器
begin
clk1ms <= ~clk1ms;
count1ms <= 1'd0;
end
else
count1ms <= count1ms + 1'd1;
end
//-----------从0开始显示数字-----------
//assign tenvalue = 20'd99999; //固定显示999
//-----------数码管译码-----------
function [7:0] leddata;
input [3:0] datain;
begin
case(datain)
4'd0: leddata = 8'b11000000; // 0
4'd1: leddata = 8'b11111001; // 1
4'd2: leddata = 8'b10100100; // 2
4'd3: leddata = 8'b10110000; // 3
4'd4: leddata = 8'b10011001; // 4
4'd5: leddata = 8'b10010010; // 5
4'd6: leddata = 8'b10000010; // 6
4'd7: leddata = 8'b11111000; // 7
4'd8: leddata = 8'b10000000; // 8
4'd9: leddata = 8'b10010000; // 9
4'd10: leddata = 8'b10111111; // -
4'd11: leddata = 8'b01111111; // .
default: leddata = 8'b11111111;
endcase
end
endfunction
//-----------数码管扫描-----------
always@(posedge clk1ms or negedge rst_n)
begin
if(!rst_n)
number <= 3'd0;
else if(number == 3'd5)
number <= 3'd0;
else
number <= number + 1;
case(number)
3'd0:
begin
segdata <= leddata(tenvalue % 10); // 个位
segcs <= 3'b101;
end
3'd1:
begin
segdata <= leddata((tenvalue / 10) % 10); // 十位
segcs <= 3'b100;
end
3'd2:
begin
segdata <= leddata((tenvalue / 100) % 10); // 百位
segcs <= 3'b011;
end
4'd3:
begin
segdata<=leddata((tenvalue/1000)%10);//千位
segcs<=3'b010;
end
4'd4:
begin
segdata<=leddata((tenvalue/10000)%10);//万位
segcs<=3'b001;
end
4'd5:
begin
segdata<=leddata((tenvalue/100000)%10);//十万位
segcs<=3'b000;
end
default:
begin
segdata <= 8'b11111111; // 或者其他默认值
segcs <= 3'b111; // 或者其他默认值
end
endcase
end
endmodule
dht11驱动模块
```verilog dht11_ctrl.v module dht11_ctrl( input wire sys_clk, input wire sys_rst_n, input wire key_flag, inout wire dht11,
output reg [19:0] data_out,
output reg sign
); // 状态转移变量的赋值 parameter S_WAIT_1S = 3'd1 , //上电等待1s状态 S_LOW_18MS = 3'd2 , //主机拉低18ms,发送开始信号状态 S_DLY1 = 3'd3 , //等待20-40us状态 S_REPLY = 3'd4 , //DHT11响应80us状态 S_DLY2 = 3'd5 , //拉高等待80us状态 S_RD_DATA = 3'd6 ; //接收数据状态
parameter T_1S_DATA = 999999 ; //1s时间计数值 parameter T_18MS_DATA = 17999 ; //18ms时间计数值 // 内部参数的定义 reg clk_us; reg [4:0] cnt; reg [5:0] state; // 状态机变量 reg [19:0] cnt_us ; // 计数器 reg [19:0] cnt_low; // reg dht11_reg1; // reg dht11_reg2; // wire dht11_rise; // wire dht11_fall; // reg [5:0] bit_cnt; // 发送的bit计数,总共发送40bit的数据 reg [39:0] data_temp; reg [31:0] data; reg data_flag; // 数据发送的标志信号 reg dht11_en; // dht11使能信号 wire dht11_out; //
// 生成us时钟信号 always @(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) cnt <= 5'd0; else if(cnt == 5'd24) cnt <= 5'd0; else cnt <= cnt+1'd1; end always @(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) clk_us <= 1'd0; else if(cnt == 5'd24) clk_us <= ~clk_us; else clk_us <= clk_us; end
//状态机状态跳转 always@(posedge clk_us or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= S_WAIT_1S ; else case(state) S_WAIT_1S: if(cnt_us == T_1S_DATA) //上电1s后跳入起始状态 state <= S_LOW_18MS ; else state <= S_WAIT_1S ; S_LOW_18MS: if(cnt_us == T_18MS_DATA) state <= S_DLY1 ; else state <= S_LOW_18MS ; S_DLY1: if(cnt_us == 10) //等待10us后进入下一状态 state <= S_REPLY ; else state <= S_DLY1 ; S_REPLY: //上升沿到来且低电平保持时间大于70us,则跳转到下一状态 if(dht11_rise == 1'b1 && cnt_low >= 70) state <= S_DLY2 ; //若1ms后,dht11还没响应,则回去继续发送起始信号 else if(cnt_us >= 1000) state <= S_LOW_18MS ; else state <= S_REPLY ; S_DLY2: //下降沿到来且计数器值大于70us,则跳转到下一状态 if(dht11_fall == 1'b1 && cnt_us >= 70) state <= S_RD_DATA ; else state <= S_DLY2 ; S_RD_DATA: //读完数据后,回到起始状态 if(bit_cnt == 40 && dht11_rise == 1'b1) state <= S_LOW_18MS ; else state <= S_RD_DATA ; default: state <= S_WAIT_1S ; endcase
//各状态下的计数器赋值 //cnt_us:每到一个新的状态就让该计数器重新计数 always@(posedge clk_us or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin cnt_low <= 7'd0 ; cnt_us <= 21'd0 ; end else case(state) S_WAIT_1S: if(cnt_us == T_1S_DATA) cnt_us <= 21'd0 ; else cnt_us <= cnt_us + 1'b1; S_LOW_18MS: if(cnt_us == T_18MS_DATA) cnt_us <= 21'd0 ; else cnt_us <= cnt_us + 1'b1; S_DLY1: if(cnt_us == 10) cnt_us <= 21'd0 ; else cnt_us <= cnt_us + 1'b1; S_REPLY: if(dht11_rise == 1'b1 && cnt_low >= 70) begin cnt_low <= 7'd0 ; cnt_us <= 21'd0 ; end //当dht11发送低电平回应时,计算其低电平的持续时间 else if(dht11 == 1'b0) begin cnt_low <= cnt_low + 1'b1 ; cnt_us <= cnt_us + 1'b1 ; end //若1ms后,dht11还没响应,则回去继续发送起始信号 else if(cnt_us >= 1000) begin cnt_low <= 7'd0 ; cnt_us <= 21'd0 ; end else begin cnt_low <= cnt_low ; cnt_us <= cnt_us + 1'b1 ; end S_DLY2: if(dht11_fall == 1'b1 && cnt_us >= 70) cnt_us <= 21'd0 ; else cnt_us <= cnt_us + 1'b1; S_RD_DATA: if(dht11_fall == 1'b1 || dht11_rise == 1'b1) cnt_us <= 21'd0 ; else cnt_us <= cnt_us + 1'b1; default: begin cnt_low <= 7'd0 ; cnt_us <= 21'd0 ; end endcase
// 打两拍,提取dht11的输入信号的上升沿和下降沿 always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) begin dht11_reg1 <= 1'd1; dht11_reg2 <= 1'd1; end else begin dht11_reg1 <= dht11; dht11_reg2 <= dht11_reg1; end end assign dht11_rise = (~dht11_reg2) && (dht11_reg1) ; assign dht11_fall = (dht11_reg2) && (~dht11_reg1) ;
// bit_cnt的搭建 always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) bit_cnt <= 6'd0; else if(bit_cnt == 6'd40 && dht11_rise == 1'd1) bit_cnt <= 6'd0; else if(state == S_RD_DATA && dht11_fall == 1'd1) bit_cnt <= bit_cnt + 1'd1; else bit_cnt <= bit_cnt; end
// data_temp always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) data_temp <= 40'd0; else if(state == S_RD_DATA && dht11_fall == 1'd1 && cnt_us <=50) data_temp[39-bit_cnt] <= 1'd0; else if(state == S_RD_DATA && dht11_fall == 1'd1 && cnt_us >50) data_temp[39-bit_cnt] <= 1'd1; else data_temp <= data_temp; end //数据校验和赋值 always@(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) data <= 32'd0; else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8]) data <= data_temp[39:8]; else data <= data; end
// dht11_en 的赋值 always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) dht11_en <= 1'd0; else if(state == S_LOW_18MS) dht11_en <= 1'd1; else dht11_en <= 1'd0; end
// dht11_out 的赋值 assign dht11_out = 1'd0 ;
// data_flag 的赋值 always @(posedge sys_clk or negedge sys_rst_n) begin if(!sys_rst_n) data_flag <= 1'd0; else if(key_flag == 1'd0) data_flag <= ~data_flag; else data_flag <= data_flag; end
// 输出信号 always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) data_out <= 20'd0; else if(data_flag == 1'd0) data_out <= data[31:24]10; else if(data_flag == 1'd1) data_out <= (data[15:8]10) + data[3:0]; end
// 符号位 always @(posedge clk_us or negedge sys_rst_n) begin if(!sys_rst_n) sign <= 1'd0; else if(data_flag == 1'd1 && data[7] == 1'd1) sign <= 1'd1; else sign <= 1'd0; end
// 如果使能是高电平,则吧dht11_out赋值给dht11输出端口。否则dht11端口作为输入端口为高阻态状态 assign dht11 = (dht11_en == 1'd1) ? dht11_out : 1'bz;
endmodule
按键消抖模块
```verilog key_filter.v
module key_filter
#(
parameter CNT_MAX = 32'd99999
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
output reg key_flag ,
output reg led_out
);
reg [31:0] cnt_20ms;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_20ms<=20'd0;
else if(key_in == 1'b1)
cnt_20ms<=20'd0;
else if(cnt_20ms == CNT_MAX)
cnt_20ms<=CNT_MAX;
else
cnt_20ms<=cnt_20ms+20'd1;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag<=1'b0;
else if(cnt_20ms == (CNT_MAX-20'd1))
key_flag<=1'b1;
else
key_flag<=1'b0;
end
always @(*) begin
if(!sys_rst_n)
led_out<=1'b0;
else if(cnt_20ms == CNT_MAX)
led_out<=1'b0;
else
led_out<=1'b1;
end
endmodule
引脚配置
参考链接
[1] ChatGPT3.5