我犯下的错误:
1,在START状态跳转到REPLAY状态,sda会由高电平跳转到低电平。这个下降沿是传感器产生低电平响应而产生的。而不是传感器准备高电平结束而产生的。
REPLAYtoWAIT_2S = ( state_c == REPLAY ) && (nege && flag0 == 1'b0 && flag1 == 1'b0 && cnt_us >= 30) ; // 这里错过,cnt_us >= 30 是后加上去的;
2,RD_DATAtoWAIT_2S = ( state_c == RD_DATA ) && (pose && data_done == 1); // 写错了,应该用data_done 。之前把flag_base 与了上去。这就导致状态机卡在了RD_DATA状态。因为pose是随机的。虽然做了同步打拍同步,和flag_base是同一时钟域但是他俩几乎不可能同时满足。
总结:在时序比较简单的工程,容易出错的地方大概率是状态机,而状态机容易出错的地方就是状态转移条件描述了。所以以后设计状态机时,要格外慎重。
3,在编译时的一个警告:
这个是状态机,状态参数定义时,忘记改值,导致的。
4,被quartus优化的信号。
在Quartus中,使用/*synthesis noprune*/,/*synthesis preserve*/等语句,注意,这些语句如果是用于信号的定义时,需要放在定义的语句尾部。
在Vivado中,使用(* keep="true" *),(* keep_hierarchy="yes" *)语句。这些语句是放在信号定义之前的
module dht11(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
inout wire sda ,
output reg [19:0] data_out ,
output reg sign
);
// localparam
localparam WAIT_2S = 4'b0001 ,
START = 4'b0010 ,
REPLAY = 4'b0100 ,
RD_DATA = 4'b1000 ;
// wire signal define
wire nege ;
wire pose ;
wire flag_base ;
wire WAIT_2StoSTART ;
wire STARTtoREPLAY ;
wire REPLAYtoRD_DATA ;
wire REPLAYtoWAIT_2S ;
wire RD_DATAtoWAIT_2S ;
// reg signal define
reg sda_reg0 ;
reg sda_reg1 ;
reg sda_reg2 ;
reg sda_en ;
reg sda_out ;
reg flag0 ;
reg flag1 ;
reg key_flag ;
reg data_done ;
reg [ 5:0] cnt_base ;
reg [ 3:0] state_c /*synthesis preserve*/;
reg [ 3:0] state_n ;
reg [ 5:0] cnt_bit ;
reg [20:0] cnt_us ;
reg [39:0] data_temp ;
/***********************************************************************/
// 1us 标志信号的产生
// reg [5:0] cnt_base ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_base <= 6'd0 ;
end else begin
if(flag_base) begin
cnt_base <= 6'd0 ;
end else begin
cnt_base <= cnt_base + 1'b1 ;
end
end
end
// wire flag_base ;
assign flag_base = (cnt_base == 49) ? 1'b1 : 1'b0 ;
// 同步与打两拍
// reg sda_reg0 // reg sda_reg1 // reg sda_reg2 ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
sda_reg0 <= 1'b1 ;
sda_reg1 <= 1'b1 ;
sda_reg2 <= 1'b1 ;
end else begin
sda_reg0 <= sda ;
sda_reg1 <= sda_reg0 ;
sda_reg2 <= sda_reg1 ;
end
end
// sda总线上升沿与下降沿的检测
// wire nege ;
// wire pose ;
assign nege = ~sda_reg1 && sda_reg2 ;
assign pose = sda_reg1 && ~sda_reg2 ;
// 三段式状态机 状态转移描述 状态转移条件描述 输出描述(与状态转移有关量描述)
// reg [ 4:0] state_c ;
// reg [ 4:0] state_n ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
state_c <= WAIT_2S ;
end else begin
state_c <= state_n;
end
end
always @(*) begin
case (state_c)
WAIT_2S:begin
if(WAIT_2StoSTART) begin
state_n <= START ;
end else begin
state_n <= WAIT_2S ;
end
end
START :begin
if(STARTtoREPLAY) begin
state_n <= REPLAY ;
end else begin
state_n <= START ;
end
end
REPLAY :begin
if(REPLAYtoRD_DATA) begin
state_n <= RD_DATA ;
end else begin
if(REPLAYtoWAIT_2S) begin
state_n <= WAIT_2S ;
end else begin
state_n <= REPLAY ;
end
end
end
RD_DATA:begin
if(RD_DATAtoWAIT_2S) begin
state_n <= WAIT_2S ;
end else begin
state_n <= RD_DATA ;
end
end
default: state_n <= WAIT_2S ;
endcase
end
assign WAIT_2StoSTART = ( state_c == WAIT_2S ) && (flag_base && cnt_us == 1_999_999) ;
assign STARTtoREPLAY = ( state_c == START ) && (flag_base && cnt_us == 20_012 ) ;
assign REPLAYtoRD_DATA = ( state_c == REPLAY ) && (nege && flag0 && flag1) ;
assign REPLAYtoWAIT_2S = ( state_c == REPLAY ) && (nege && flag0 == 1'b0 && flag1 == 1'b0 && cnt_us >= 30) ; // 这里错过一次,cnt_us >= 30 是后加上去的;因为第一次的低电平是传感器响应低电平拉低的,并不是响应高电平结束拉低的。
assign RD_DATAtoWAIT_2S = ( state_c == RD_DATA ) && (pose && data_done == 1); // 写错了,应该用data_done 。而且pose 和 flag_base 是两个时钟域下的,根本不能同时满足。逻辑错误。
// reg [20:0] cnt_us ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_us <= 21'd0 ;
end else begin
case (state_c)
WAIT_2S:begin
if(flag_base && cnt_us == 1_999_999) begin
cnt_us <= 21'd0 ;
end else begin
if(flag_base) begin
cnt_us <= cnt_us + 1'b1 ;
end else begin
cnt_us <= cnt_us ;
end
end
end
START :begin
if(flag_base && cnt_us == 20_012) begin
cnt_us <= 21'd0 ;
end else begin
if(flag_base) begin
cnt_us <= cnt_us + 1'b1 ;
end else begin
cnt_us <= cnt_us ;
end
end
end
REPLAY :begin
if(nege || pose) begin
cnt_us <= 21'd0 ;
end else begin
if(flag_base) begin
cnt_us <= cnt_us + 1'b1 ;
end else begin
cnt_us <= cnt_us ;
end
end
end
RD_DATA:begin
if(nege || pose) begin
cnt_us <= 21'd0 ;
end else begin
if(flag_base) begin
cnt_us <= cnt_us + 1'b1 ;
end else begin
cnt_us <= cnt_us ;
end
end
end
default: cnt_us <= 21'd0 ;
endcase
end
end
// reg flag0 ;
// reg flag1 ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
flag0 <= 1'b0 ;
flag1 <= 1'b0 ;
end else begin
if(state_c == REPLAY) begin
if(cnt_us >= 81 && sda == 0) begin // 这里其实可能有一个小问题,就是响应时序的低电平不足81拉高了,但是响应时序的高电平满足。那么也会判定为满足响应。
flag0 <= 1'b1 ;
end else begin
flag0 <= flag0 ; // 所以,在设计flag0信号的时候,还得加上一条,sda == 0
end
if(cnt_us >= 85 && sda == 1) begin
flag1 <= 1'b1 ;
end else begin
flag1 <= flag1 ;
end
end else begin
flag0 <= 1'b0 ;
flag1 <= 1'b0 ;
end
end
end
// reg [5:0] cnt_bit ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
cnt_bit <= 6'd0 ;
end else begin
if(state_c == RD_DATA) begin
if(nege) begin // 判断cnt_us 的值 是给data_temp 赋值时要考虑的事情。这里只是cnt_bit + 1
cnt_bit <= cnt_bit + 1'b1 ;
end else begin
if(pose && cnt_bit == 40) begin
cnt_bit <= 6'd0 ;
end else begin
cnt_bit <= cnt_bit ;
end
end
end else begin
cnt_bit <= 6'd0 ;
end
end
end
// reg data_done ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
data_done <= 1'b0 ;
end else begin
if(state_c == RD_DATA && cnt_bit == 40) begin
data_done <= 1'b1 ;
end else begin
data_done <= 0 ;
end
end
end
// reg [39:0] data_temp ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
data_temp <= 40'd0 ;
end else begin
if(state_c == RD_DATA) begin
if(nege) begin
if(cnt_us >= 23 && cnt_us <= 27) begin
data_temp[39 - cnt_bit] <= 1'b0 ;
end else begin
if(cnt_us >= 68 && cnt_us <= 74) begin
data_temp[39 - cnt_bit] <= 1'b1 ;
end else begin
data_temp[39 - cnt_bit] <= 1'b0 ;
end
end
end else begin
data_temp <= data_temp ;
end
end
end
end
// reg key_flag ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
key_flag <= 1'b0 ;
end else begin
if(key_in) begin
key_flag <= ~key_flag ;
end else begin
key_flag <= key_flag ;
end
end
end
// 三态使能与输出
// reg sda_en ;
// reg sda_out ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
sda_en <= 1'b1 ;
sda_out <= 1'b1 ;
end begin
case (state_c)
WAIT_2S : begin
sda_en <= 1'b1 ;
sda_out <= 1'b1 ;
end // 由于使用reg 类型,这里在start状态会多拉高20ns的高电平。
START : begin
sda_en <= 1'b1 ;
if(cnt_us <= 19_999) begin
sda_out <= 1'b0 ;
end else begin
sda_out <= 1'b1 ;
end
end
REPLAY : begin
sda_en <= 1'b0 ;
sda_out <= 1'b1 ;
end
RD_DATA : begin
sda_en <= 1'b0 ;
sda_out <= 1'b1 ;
end
default: begin
sda_en <= 1'b0 ;
sda_out <= 1'b1 ;
end
endcase
end
end
/**********************************output signal*****************************************/
// wire sda ,
assign sda = (sda_en == 1'b1) ? sda_out : 1'bz ; // 注意三态输出赋值z
// reg [19:0] data_out ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
data_out <= 20'd0 ;
end else begin
if(key_flag == 1'b0) begin // 显示湿度 前8位是整数,后8位是小数。
data_out <= (data_temp[39:24] >> 8) * 20'd10 ; // 因为数码管的最后一位是显示小数的,所以×10。
end else begin
data_out <= data_temp[23:16] * 20'd10 + data_temp[11:8] ;
end
end
end
// 目前 DHT11 温度只能精确到 0.1℃,所以温度 8bit 小数数据的值是小于 10 的,
// 我们应用时用小数数据的低四位来表示
// 温度的小数值,最高位表示温度的正负即可。
// reg sign
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
sign <= 1'b0 ;
end else begin
if(data_done && (data_temp[7:0] == (data_temp[15:8] + data_temp[23:16] + data_temp[31:24] + data_temp[39:32]))) begin
if(data_temp[15] == 1'b1) begin
sign <= 1'b1 ;
end else begin
sign <= 1'b0 ;
end
end
end
end
endmodule
module top(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key ,
inout wire dht11 ,
output wire ds ,
output wire oe ,
output wire shcp ,
output wire stcp
);
// 例化连线
wire key_out_w ;
wire [19:00] data_w ;
wire sign_w ;
wire [ 5: 0] point_w ;
wire en_w ;
assign point_w = 6'b000_010 ;
assign en_w = 1'b1 ;
key_filter key_filter_insert(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.key_in ( key ) ,
.key_out ( key_out_w )
);
dht11 dht11_insert(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.key_in ( key_out_w ) ,
.sda ( dht11 ) ,
.data_out ( data_w ) ,
.sign ( sign_w )
);
seg_595_dynamic seg_595_dynamic_insert(
.sys_clk ( sys_clk ) ,
.sys_rst_n ( sys_rst_n ) ,
.data ( data_w ) ,
.point ( point_w ) ,
.sign ( sign_w ) ,
.seg_en ( en_w ) ,
.ds ( ds ) ,
.oe ( oe ) ,
.shcp ( shcp ) ,
.stcp ( stcp )
);
endmodule
木有仿真,只有signaltap抓取的波形,但我忘记截屏了。