ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【FPGA】状态机写按键消抖

2021-12-07 09:58:47  阅读:200  来源: 互联网

标签:wire FPGA 20ms 消抖 状态机 state KEY 按键


文章目录

一、状态机原理

状态(FSM),又称有限状态机
一段式状态机
一段式状态机似乎是一锅端,把所有逻辑(包括输入,输出,状态)都在一个always里解决了,这种写法看上去好像很简洁,但是往往不利于维护,这种写法不太推荐,但是在一些简单的状态机中还是可以使用的。
两段式状态机
两段式状态机是一种常用的写法,他把时序逻辑和组合逻辑划分开来,时序逻辑里进行当前逻辑和下一逻辑的切换,组合逻辑里实现各个输入输出及状态判断,这种写法相对容易维护,不过组合逻辑输出较容易出现毛刺等常见问题。
三段式状态机
三段式状态机的写法是一种比较推荐的写法,代码容易维护,时序逻辑的输出解决了两段式写法中组合逻辑的毛刺问题,但是从资源消耗上来讲,三段式消耗的资源会多一些,另外,三段式输入从输入到输出比一段式会延迟一个时钟周期。三段式状态机将时序逻辑和组合逻辑分开,状态和输出分开,清晰容易理解。

我推荐就是写三段式状态机

二、设计思路

按键消抖算是一个比较简单的模块,四个状态
1.IDLE(初始状态)
2.K_D(按键按下的状态)
3.H_D(按键按住稳定的值的状态)
4.K_U(按键弹起的状态)(这个可要可不要)

状态图:

在这里插入图片描述

状态转移图:

在这里插入图片描述

然后去想想这几个状态的跳转条件,无非就是边沿的检测和按键消抖的20ms的延时

assign idle2down = (state_c == IDLE) && nedge;//检测到下降沿
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//计时到20ms时没有出现上升沿标志按键按下后保持稳定
assign hold2up   = (state_c == HOLD) && (pedge);//检测到上升沿跳转到上升态
assign up2idle   = (state_c == UP)   && end_cnt_20ms;//计数器计数到20ms跳转到初始态

其余的逻辑就跟平常的按键消抖无异,可以看我之前的博客的代码

【FPGA】实战之按键消抖

三、代码部分

这里代码是借鉴同学的代码,我的代码写的写完的时候太乱了,反正这一个按键消抖的模块,要用的时候代码直接照搬,例化就完事了,用里面的key_out信号就行了。

module fsm_key_debounce # (parameter KEY_W = 3,TIME_20MS = 1_000_000)(
    input 			            clk		,
    input 			            rst_n	,
    input 		[KEY_W - 1:0]	key_in	,

    output 		[KEY_W - 1:0]	key_out	 
);
//参数定义
localparam IDLE  = 4'b0001;//初始状态 
localparam DOWN  = 4'b0010;//按键按下抖动
localparam HOLD  = 4'b0100;//按键按下后稳定
localparam UP    = 4'b1000;//按键上升抖动
//信号定义
reg [3:0] state_c;//现态
reg [3:0] state_n;//次态

//状态转移条件定义
wire idle2down;
wire down2idle;
wire down2hold;
wire hold2up  ;
wire up2idle  ;

reg [KEY_W - 1:0] key_r0;//同步
reg [KEY_W - 1:0] key_r1;//打拍
wire [KEY_W - 1:0] nedge;//下降沿
wire [KEY_W - 1:0] pedge;//上升沿

//20ms计数器
reg [19:0] cnt_20ms;
wire add_cnt_20ms;
wire end_cnt_20ms;

reg [KEY_W - 1:0] key_out_r;//输出寄存

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

always@(*)begin
    case(state_c)
        IDLE:begin
            if(idle2down)begin
                state_n = DOWN;
            end
            else begin
                state_n = state_c;
            end
        end
        DOWN:begin
            if(down2idle)begin
                state_n = IDLE;
            end
            else if(down2hold)begin
                state_n = HOLD;
            end
            else begin
                state_n = state_c;
            end
        end
        HOLD:begin
            if(hold2up)begin
                state_n = UP;
            end
            else begin
                state_n = state_c;
            end
        end
        UP:begin
            if(up2idle)begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end
        default:state_n = state_c;
    endcase
end

assign idle2down = (state_c == IDLE) && nedge;//检测到下降沿
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//计时到20ms时没有出现上升沿标志按键按下后保持稳定
assign hold2up   = (state_c == HOLD) && (pedge);//检测到上升沿跳转到上升态
assign up2idle   = (state_c == UP)   && end_cnt_20ms;//计数器计数到20ms跳转到初始态
//20ms计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <= 0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'b1;
        end
    end
end
assign add_cnt_20ms = state_c == DOWN || state_c == UP;//当按键按下或上弹时开始计数
assign end_cnt_20ms = add_cnt_20ms && ((cnt_20ms == TIME_20MS - 1) || pedge);//当计数到最大值或检测到上升沿计数器清零



//同步打拍
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= {KEY_W{1'b1}};
        key_r1 <= {KEY_W{1'b1}};
    end
    else begin
        key_r0 <= key_in;
        key_r1 <= key_r0;
    end
end

assign nedge = ~key_r0 &  key_r1;//检测下降沿
assign pedge =  key_r0 & ~key_r1;//检测上升沿

//按键赋值
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_out_r <= {KEY_W{1'b0}};
    end
    else if(state_c == HOLD && hold2up)begin
        key_out_r <= ~key_r1;
    end
    else begin
        key_out_r <= {KEY_W{1'b0}};
    end
end
assign key_out = key_out_r;

endmodule

四、仿真验证

这里就不验证了,还是可以去看我之前的博客仿真,无非是多加了4个状态和4个状态转移的条件。

【FPGA】实战之按键消抖

标签:wire,FPGA,20ms,消抖,状态机,state,KEY,按键
来源: https://blog.csdn.net/weixin_45888898/article/details/121711187

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有