英特尔FPGA实训day6

今天学习了按键的消抖。上午用了简单的延时和标志位进行消抖。 其实现相对快捷,但实则逻辑,和有用程度没有下午的有限状态机清晰,更加适合临时开发,从后期维护和长期稳定,以及复用能力来看,还是有限状态机更胜一筹。

产生疑惑及解决:

        今天的疑惑产生倒是几乎没什么,如果有,那就是后面实现有限状态机的时候,一些代码没看懂,但后来静下心来理清实现的逻辑,一步步看懂了。一会儿会贴出相关收获以及实现逻辑的思维导图。

接下来做的事: 

进一步回顾,理清一些程序的实现逻辑,以及模块化架构思维,学会复用,对于后期学习的进一步调整,相关侧重点的增强。

今天收获:

对于延时消抖,收获不多。但对于有限状态机,收获很多,因为一方面其比较重要,另一方面,其逻辑很清晰,而且前几天有接触,有一定总结,更有兴趣去进一步专研总结。

下面我将从宏观,逐步进行具体,实例化。(也相当于对状态机实现的进一步回顾总结)

有限状态机实现 的大体框架:        

程序实现包括三大板块:状态转移——状态描述(重点)——状态输出

        提到有限状态机,则 必有两个变量。 当前状态,下一个状态。

然后再是对于状态转移规律:即几个状态的切换关系。

下面分别是四个状态:空闲状态——按下消抖状态——按下稳定状态——弹起消抖状态

 明白几个状态后。 再理清各个状态之间的条件转移关系。

tips:在实现的时候,直接宏观的一步步写,然后再逐步细化,补充,学会 层次化,模块化的思维。如下方判断条件的书写:(即类似伪代码一样。后续一步步扩充,补全就行)

//第二段,描述状态转移规律
always@(*)begin
	case(state_c)
		IDLE:begin
			if(idle2filter_down_start)
			state_n = FILTER_DOWN;
			else
			state_n = state_c;
		end

具体过程不再详述,以下为实现的导图:

(可能不太清晰,后续会贴代码,或有需要清晰图的,可以找我要Q:1354087583)

在用状态机实现消抖过程中,还学到个很巧妙的东西。就是上下升沿的判断(边沿检测)

当然,其实也很简单,但对于我是从 之前一直是静态状态判断(高,低电平),到动态状态(升降电平)的判断的跳变,还是觉得蛮有意思。

//对按键输入信号打两拍,从而实现边沿检测
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_in_r0 <= 1'b1;
        key_in_r1 <= 1'b1;
    end
    else begin
        key_in_r0 <= key_in;
        key_in_r1 <= key_in_r0;
    end
end

assign  n_edge = ~key_in_r0 & key_in_r1;//下降沿
assign  p_edge = ~key_in_r1 & key_in_r0;//上升沿

其很巧妙的利用了fpga的非阻塞赋值 ,即 第8,9行代码。

解释:当这两条语句进行后,key_in_r1实至是获取的key_in_r0 执行第8行之前的值,而key_in_r0获取的是现在(此时此刻)传入的key_in的值(key_in是动态的外部输入的实时电平)。

今天差不多就这些,接下来就是今天相关的代码。

(注:以下代码均以抬起按键认为有效 ,即 不具有连续输入功能, 按一下 松开,变化一下)

普通延时消抖

顶层:

module top_key_beep(
    input clk,
    input rst_n,
    input key,
    
    output beep
);
wire flag;
wire key_value;

key_debounce u_key_debounce(
.clk(clk),
.rst_n(rst_n),
.key_in(key),

.flag(flag),
.key_value(key_value)
);

beep_control u_beep_control(
.clk(clk),
.rst_n(rst_n),
.flag(flag),
.key_value(key_value),

.beep(beep)
);

endmodule

消抖:

module key_debounce(
    input clk,
    input rst_n,
    input key_in,

    output reg flag,//按键消抖完成
    output reg key_value//消抖后的按键值

);
parameter DELAY = 20'd100_0000;
reg [19:0] delay_cnt ;

reg key_reg;//保存上一时刻按键的值

//延时消抖 逻辑代码
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        delay_cnt <= 20'd0;
        key_reg <= 1'b1;
    end  
    else begin 
        key_reg <= key_in;
        if (key_reg != key_in) begin
            delay_cnt <= DELAY;
        end
        else begin
            if (delay_cnt > 20'd0) begin
                delay_cnt <= delay_cnt -1'd1;
            end
            else begin
                delay_cnt <= 20'd0;

            end
        end
    end 
end

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        flag <= 1'b0;
        key_value <= 1'b1;
    end 
    else if(delay_cnt == 1'd1)begin //20ms没有抖动,输出当前按键值
        flag <= 1'b0;
        key_value <= key_in;

    end 
    else begin 
        flag <= 1'b0;
        key_value <= key_value;
        
    end 
end

endmodule

蜂鸣器:

module beep_control(
    input clk,
    input rst_n,
    input key_value,
    input flag,

    output reg beep
);

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        beep <= 1'b1;
    end 
    else begin
        if(!key_value && flag) begin 
        beep <= ~beep;//低电平有效
    end 
    else begin
        beep <= beep;
    end
    end
end




endmodule

有限状态机实现消抖

顶层文件:top_key_beep.v

module top_key_beep(
    input clk,
    input rst_n,
    input [1:0]key,
    
    output [1:0]led
);

wire [1:0]key_value;

key_keep u_key_keep(
.clk(clk),
.rst_n(rst_n),
.key_in(key[0]),


.key_down(key_value[0])
);
key_keep u_key1_keep(
.clk(clk),
.rst_n(rst_n),
.key_in(key[1]),

.key_down(key_value[1])
);

beep_control u_beep_control(
.clk(clk),
.rst_n(rst_n),
.key_value(key_value),

.led(led)
);

endmodule

响应文件(led):

module beep_control(
    input clk,
    input rst_n,
    input [1:0]key_value,


    output reg [1:0]led
);

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        led <= 2'b11;
    end 
    else if(key_value[0]) begin 
        led[0] <= ~led[0];//低电平有效
    end 
    else if(key_value[1]) begin 
        led[1] <= ~led[1];//低电平有效
    end 
    else begin
        led <= led;
    end
    
end




endmodule

按键消抖文件

module key_keep(//消抖模块
    input clk,
    input rst_n,
    input key_in,//未消抖的按键

    output reg key_down//消抖后的按键值
);

/****************************************************************
						main
				按键消抖用状态机方法实现
***************************************************************/


//定义按键按下的四个 状态
localparam IDLE = 4'b0001;
localparam FILTER_DOWN = 4'b0010;//按键按下过程
localparam HOLD_DOWN = 4'b0100 ;
localparam FILTER_UP = 4'b1000;  //按键松开过程

parameter TIME_DELAY = 20'd100_0000;

wire add_delay_cnt;//开始计时的标志
wire end_delay_cnt;//结束及时的标志

reg [19:0] cnt;
reg [3:0] state_c;//现态
reg [3:0] state_n;//次态

reg key_in_r0;
reg key_in_r1;

wire n_edge;//按键按下
wire p_edge;//按键松开 
wire idle2filter_down_start;
wire filter_down2hold_down_start;
wire filter_down2idle_start;
wire hold_down2filter_up_start;
wire filter_up2idle_start;

//接收按键信号
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
         key_in_r0 <= 1'b1;
         key_in_r1 <= 1'b1;
    end 
    else begin 
        key_in_r0 <= key_in;
        key_in_r1 <= key_in_r0;
    end 
end

assign n_edge = ~key_in_r0 & key_in_r1;
assign p_edge = ~key_in_r1 & key_in_r0;

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 
     if(!rst_n)begin
      state_n = IDLE;
     end 
     else begin 
        case (state_c)
            IDLE:begin
                if(idle2filter_down_start)
                state_n <=FILTER_DOWN;
                else
                state_n <=state_c;
            end 
            FILTER_DOWN:begin
                if(filter_down2hold_down_start)begin
                    state_n <= HOLD_DOWN;
                end
                else if (filter_down2idle_start) begin
                    state_n <=IDLE;
                end
                else
                    state_n <= state_c;
            end
            HOLD_DOWN: begin
                if (hold_down2filter_up_start) begin
                    state_n <= FILTER_UP;
                end
                else 
                    state_n <= state_c;
            end
            FILTER_UP: begin
                if (filter_up2idle_start)begin
                    state_n <= IDLE;
                end
                else 
                    state_n <= state_c;
            end
            default: ;
        endcase
     end 
end

always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_down <= 0;
    end 
    else if(hold_down2filter_up_start)begin 
        key_down <= ~key_in_r1;//如果不取反,按键按下就输出0,取反,按键按下就输出1
    end 
    else begin 
        key_down <= 1'b0;
    end 
end

assign idle2filter_down_start = (state_c == IDLE)&&(n_edge);
assign filter_down2idle_start = (state_c == FILTER_DOWN)&&(add_delay_cnt && p_edge);
assign filter_down2hold_down_start = (state_c == FILTER_DOWN)&&(end_delay_cnt && !p_edge);
assign hold_down2filter_up_start = (state_c == HOLD_DOWN)&&(p_edge);
assign filter_up2idle_start = (state_c == FILTER_UP)&&(end_delay_cnt);

/*计数条件与结束条件*/
assign  add_delay_cnt = (state_c==FILTER_DOWN) || (state_c==FILTER_UP);
assign  end_delay_cnt = add_delay_cnt && (cnt == TIME_DELAY-1);
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cnt <= 20'd0;
    end 
    else if(add_delay_cnt)begin 
        if (end_delay_cnt) begin
            cnt <= 20'd0;
        end
        else begin
            cnt <= cnt +1'b1;
        end
    end 
    else begin 
        cnt <= cnt;
    end 
end

endmodule

(这里再贴个老师的,因为也附有自己的添的一些注释,好理解写,同时也工整不少,但注意 这个文件的变量名和上面的并不通用)

状态机实现消抖:

module key_debounce (
input 				clk,
input 				rst_n,
input      		 	key_in,
output reg 		 	key_down 
);

//独热码方式定义状态机状态参数
localparam		IDLE			=4'b0001;
localparam		FILTER_DOWN     =4'b0010;
localparam		HOLD_DOWN       =4'b0100;
localparam		FILTER_UP       =4'b1000;


parameter TIME_DELAY = 20'd100_0000;


reg	[3:0]		state_c;//现态
reg	[3:0]		state_n;//次态
reg 	[19:0] 	counter;//计数器
reg 			 	key_in_r0;
reg  			 	key_in_r1;

wire			n_edge;//下降沿检测信号
wire 		 	p_edge;//上升沿检测信号
wire 			add_delay_flag;
wire 			end_delay_flag;

wire			idle2filter_down_start;     //空闲状态进入按下消抖状态标志,即检测到下降沿
wire			filter_down2idle_start;
wire			filter_down2hold_down_start;
wire			hold_down2filter_up_start;
wire			filter_up2idle_start;

/***************************************************************
						main
				按键消抖用状态机方法实现
***************************************************************/

//对按键输入信号打两拍,从而实现边沿检测
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		key_in_r0 <= 1'b1;
		key_in_r1 <= 1'b1;
	end
	else begin
		key_in_r0 <= key_in;
		key_in_r1 <= key_in_r0;
	end
end
//更简便方法
assign  n_edge = ~key_in_r0 & key_in_r1;//下降沿
assign  p_edge = ~key_in_r1 & key_in_r0;//上升沿


	
//三段式状态机实现

//第一段,描述状态转移
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(idle2filter_down_start)
			state_n = FILTER_DOWN;
			else
			state_n = state_c;
		end
		FILTER_DOWN:begin
			if(filter_down2idle_start)
			state_n = IDLE;
			else if(filter_down2hold_down_start)
			state_n = HOLD_DOWN;
			else 
			state_n = state_c;
		end
		HOLD_DOWN:begin
			if(hold_down2filter_up_start)
			state_n = FILTER_UP;
			else
			state_n = state_c;
		end
		FILTER_UP:begin
			if(filter_up2idle_start)
			state_n = IDLE;
			else
			state_n = state_c;
		end
		default:state_n = IDLE;
	endcase
end

//第三段,描述各个状态的输出
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		key_down <= 1'b0;
	end
	else if( hold_down2filter_up_start ) begin//按键抬起的时候切换输出
		key_down <= ~key_in_r1;//把按下时候的0 变成1(为了便于后续判断,这里输出这个 就相当于是 输出的flag。输出1表示有效,0表示无效)
	end
	else
		key_down <= 1'b0;
end

assign	idle2filter_down_start 		= (state_c == IDLE) && (n_edge);     
assign	filter_down2idle_start 		= (state_c == FILTER_DOWN) && (add_delay_flag && p_edge);
assign	filter_down2hold_down_start = (state_c == FILTER_DOWN) && (end_delay_flag && (!p_edge));
assign	hold_down2filter_up_start 	= (state_c == HOLD_DOWN) && (p_edge);
assign	filter_up2idle_start 		= (state_c == FILTER_UP)&&(end_delay_flag);

/*计数条件与结束条件*/
assign  add_delay_flag 				= (state_c==FILTER_DOWN) || (state_c==FILTER_UP);
assign  end_delay_flag 				= add_delay_flag && (counter == TIME_DELAY-1);
//延时计时模块
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		counter<=20'd0;
	end
	else if(add_delay_flag)begin
			if(end_delay_flag)begin
			counter<=20'd0;
			end
			else begin
			counter <= counter + 1'b1;
			end
		end
	else begin
	counter <= counter;
	end
end

endmodule

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值