Fpga 学习笔记7(状态机设计实例):因内容比较简单,而且在这篇日志中也有相关的知识点,就不写了。
该集主要知识点:
1、利用状态机实现滤除物理按键所产生的抖动波形。
2、非阻塞赋值的巧妙运用
3、将状态机与计数器功能组合使用
4、在仿真代码中利用随机数产生延时随机的时间。
5、task 运用方式、以及将仿真测试代码模块化
按键抖动的现象与状态机对应的状态:
一、源程序
/* 实验名称:按键消抖模块设计与验证
* 功能实现:滤除按键抖动的波形
*/
`define DEC_TIME_CNT ((20 * 1000 * 1000) / 20 - 1)
module mytest(clk, rst_n, key_in, key_flag, key_state);
input clk, rst_n, key_in;
output reg key_flag, key_state;
reg[3:0] state; // 状态机状态
reg key_old, key_cur; // key状态
wire pedge, nedge; // 边沿状态
// 50MHz的时钟 = 1/50M = 0.02us = 20ns
// 20ms = 20_000_000ns / 20ns = 1000_000
reg[19:0] time_cnt; // 计数器计数值
reg time_full; // 计数器已经达到指定的时间
reg time_en; // 计数器使能
localparam // 状态机几种状态的标志
IDLE = 4'b0001, // 空闲,即高电平状态,为按下状态
DING = 4'b0010, // 按下时抖动状态
DOWN = 4'b0100, // 可以确定是处于按下状态而不是抖动状态
UING = 4'b1000; // 弹起时抖动状态
// 边沿检测
always@(posedge clk, negedge rst_n)
if(!rst_n)begin
key_cur <= 1'b0;
key_old <= 1'b0;
end
else
begin
key_cur <= key_in; // 这里由于采用了非阻塞赋值
key_old <= key_cur; // 所以在同一个时钟内 key_old 采样 key_curr 的是旧的值
end
// 判断上升沿、下降沿
assign pedge = !key_old & key_cur; // 原来为低电平,现在为高电平,则表示检测到上升沿
assign nedge = key_old & !key_cur; // 原来为高电平,现在为低电平,则表示检测到下降沿
// 计数功能
always@(posedge clk, negedge rst_n)
if(!rst_n)begin
time_cnt <= 20'd0;
// time_en <= 1'b0; // 这里不能再次赋值,因为在状态机的程序块中需要对该信号赋值
// 一个信号不能在多个 always 块中赋值
end
else if(time_en)
time_cnt <= time_cnt + 1'b1;
else
time_cnt <= 20'd0;
// 检测时间是否已经到了 这里指定 20ms 的时间
always@(posedge clk, negedge rst_n)
if(!rst_n)
time_full <= 1'b0;
else if(time_cnt == `DEC_TIME_CNT)
time_full <= 1'b1;
else
time_full <= 1'b0;
// 状态机
always@(posedge clk, negedge rst_n)
if(!rst_n)begin
state <= IDLE;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDLE: begin // 空闲状态:按键没有被按下
key_flag <= 1'b0; // 在空闲状态 按键需要清零
if(nedge) begin // 检测到下降沿
state <= DING; // 设置状态为 DING,下个时钟上升沿将会进入另外一个分支
time_en <= 1'b1; // 启动定时器
end
else
state <= IDLE; // 依据是高电平,设置状态为 DING
end
DING: begin // 滤波抖动,按下时产生的抖动状态
if(time_full) begin // 如果指定的时间内没有上升沿
key_flag <= 1'b1; // 则表示处于稳定状态,进入按下状态
key_state <= 1'b0; // 表示按下
state <= DOWN; // 设置状态为按下
time_en <= 1'b0; // 关闭定时器
end
else if(pedge) begin // 如果指定的时间内出现上升沿说明是处于抖动状态
state <= IDLE; // 重新设置为空闲状态
time_en = 1'b0; // 并且关闭定时器
end