独立按键消抖设计与验证
本实验主要是为了锻炼状态机的思维模式以及熟练掌握TB的写法
本节主要收获了:define的用法,另外就是,顶层的input在TB中是reg的真正含义,其实就是把激励当做寄存器来使了。
开发板:小梅哥AC620
软件:quartus 13.1
一般情况下抖动的总时间会持续20ms以内。
状态机的基本结构,下面是一个状态机的样例,我们只需要在以后模仿着写即可,下面程序不是我们的独立按键消抖程序,是我以前写的一个程序,基本上,我写状态机都是按照这种三段式的方法来写。
/* 有限状态机,三过程块建模风格 ***************************************** */
// 状态编码
localparam IDLE = 3'b001,
DELAY = 3'b010,
GENE = 3'b100;
reg [2:0] now_state = IDLE, next_state = IDLE;
// 1.实现状态转换
always @ (posedge clk) begin//: trans_state
if (rst)
now_state <= IDLE;
else
now_state <= next_state;
end//: trans_state
// 2.产生下一个状态
always @ (*) begin//: set_next_state
//next_state = now_state; // 下面分支的缺省状态
case (now_state)
IDLE : if (valid )
begin
if (delay=='b0)
next_state = GENE;
else
next_state = DELAY;
end
else
next_state = IDLE;
DELAY : if (delay_cnt >= delay)
next_state = GENE;
else
next_state = DELAY;
GENE : if (nop_cnt >= nop)
next_state = IDLE;
else
next_state = GENE;
default:
next_state=IDLE;
endcase
end//: set_next_state
// 3.产生状态机的输出值
always @ (posedge clk) begin//: set_out_proc
case (next_state)
IDLE : begin
nop_cnt <= 'b0;
delay_cnt <= 'b0;
end
DELAY : begin
delay_cnt <= delay_cnt + 1'b1;
end
GENE : begin
if (pulse_dec)
nop_cnt <= nop_cnt + 1'b1;
else
nop_cnt <= nop_cnt;
end
default: ;
endcase
end//: set_out_proc
独立按键消抖程序
module key_detect
(
input clk,
input rst_n,
input key,
output led
);
reg key_n;
reg key_n_n;
reg [23:0]cnt_fall=0;//
reg [23:0]cnt_rise=0;
/*--------边沿检测部分---------*/
wire dectect_fall,dectect_rise;
always @ (posedge clk)
begin
key_n<=key;
key_n_n<=key_n;
end
assign dectect_fall=key_n_n&!key;//下降沿检测
assign dectect_rise=!key_n_n&key;//上升沿检测
localparam idle=0;//不按时候的空闲状态
localparam fall_shake=1;//按下时候的抖动状态
localparam fall_stable=2;//按下时候的稳定状态
localparam rise_shake=3;//释放时候的抖动状态
/*--------状态机部分---------*/
reg[2:0] state_next=idle;
reg [2:0] state_now=idle;
//参数化设计
localparam fall_shake_time=24'd10;//20ms的下降延抖动延时 10*20=200ns
localparam rise_shake_time=24'd10;//20ms的上升延抖动延时
//状态转移
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
state_now<=idle;
else
state_now<=state_next;
end
//转移条件
always@ (*)
begin
case(state_now)
idle:
begin
if(dectect_fall)//如果检测到下降沿,那么就跳转到下降沿抖动状态
state_next=fall_shake;
else if(key)
state_next=idle;
else
state_next=fall_stable;
end
fall_shake:
begin
if(cnt_fall<fall_shake_time)
begin
if(dectect_rise)
state_next=idle;
else
state_next=fall_shake;
end
else if(cnt_fall==fall_shake_time)
state_next=fall_stable;
else
state_next=idle;
end
fall_stable:
begin
if(dectect_rise)//如果检测到下降沿,那么就跳转到下降沿抖动状态
state_next=rise_shake;
else if(!key)
state_next=fall_stable;
else
state_next=idle;
end
rise_shake:
begin
if(cnt_rise<rise_shake_time)
begin
if(dectect_fall)
state_next=fall_shake;
else
state_next=rise_shake;
end
else if(cnt_rise==rise_shake_time)
state_next=idle;
else
state_next=rise_shake;
end
default:;
endcase
end
//状态输出
always@ (posedge clk)
begin
case(state_next)
idle:
begin
cnt_fall<=0;
cnt_rise<=0;
end
fall_shake:
begin
cnt_fall<=cnt_fall+1;
end
fall_stable:;
rise_shake:
begin
cnt_rise<=cnt_rise+1;
end
endcase
end
//判断按键是否真的按下
assign led=(state_now==fall_stable|state_now==rise_shake)?0:1;
endmodule
测试文件:
注意:测试由于我们的抖动20ms,太长了,对于仿真不方便,我们就把顶层的20ms,改成了200ns的抖动时间
`define clock_period 0.02
`timescale 1 us/ 1 ns
module key_detect_vlg_tst();
reg clk;
reg key;
reg rst_n;
wire led;
// assign statements (if any)
key_detect i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.key(key),
.led(led),
.rst_n(rst_n)
);
initial
begin
clk=0;
rst_n=0;
key=1;
end
//定义时钟
always #(`clock_period/2) clk=~clk;
initial
begin
#(`clock_period*10);
rst_n=1;
#(`clock_period*10);
key=0;
#(`clock_period*500);
key=1;
#(`clock_period*10);
key=0;
#(`clock_period*5);
key=1;
#(`clock_period*500);
$stop;
end
endmodule
实际测试: