【原创】FPGA应用(三)——按键控制led
0赞一、设计需求
设计一个按键控制led的功能模块,即一个按键控制一个led。随着每一次按键的按下,led就按照亮、灭交替显示。
二、设计思路
本次使用的按键为机械弹性开关按键,但由于机械触点的弹性作用,按键闭合时并不能马上接通,同样按键断开时也不能马上断开,故在按键闭合和断开的瞬间均伴随着一连串的抖动,如图1所示。而抖动的时间由按键的机械特性决定,一般为5ms~10ms。为了避免由于按键抖动而引起的误操作,需要对按键进行消抖。
图1 按键抖动
按键消抖包括硬件消抖和软件消抖。当按键数目较少时,可采用硬件消抖如RS触发器,否则将占大面积的PCB;当按键数目较多时,采用软件消抖,利用程序避开按键的抖动期。
本设计采用软件消抖的方案,即检测按键是否按下,当检测到按键按下时,延时10ms,再检测按键是否真的按下了,若不是真的按下则返回继续检测按键是否按下,若是真的按下了则进行按键按下处理,接着检测按键是否松开,当检测到按键松开时,延时10ms,再检测按键是否真的松开了,若不是真的松开则返回继续检测按键是否松开,若是真的松开了则进行按键松开处理,接着再返回检测按键是否按下,就这样不断的判断按键按下与否和松开与否。根据这种思路,给出按键检测和消抖流程图,如图2所示。
图2 按键检测和消抖流程图
图3所示为红色飓风E45开发板上按键的电路原理图。从原理图可以看出,按键按下时为低电平,按键断开时为高电平。
图3 E45中按键电路原理图
图4所示为本设计按键控制led的总体框架,由三个模块组成,分别为子模块KEY_Decounce、led_ctrl和顶层模块key_ctrl_led。这里重点介绍按键检测和消抖。图5所示为KEY_Decounce模块中所用到的按键检测与消抖状态机。状态机中有四个状态,分别为IDLE、JITTER1、DEAL和JITTER2。在IDLE状态中检测按键是否按下,若按下即data!=1则跳转到JITTER1状态中延时10ms进行消抖后再判断按键是否真的按下了,若为真即data!=1则跳转到DELA状态中进行按键按下处理,否则即data==1则返回IDLE状态中继续检测按键是否按下,在DEAL状态中除了按键按下处理外还要检测按键是否松开,若检测到按键松开即data==1则跳转到JITTER2状态中延时10ms进行消抖后再判断按键是否真的松开了,若为真即data==1则跳转到IDLE状态继续检测按键是否按下,否则返回DEAL状态中继续判断按键是否松开。
图4 按键控制led
图5 按键检测与消抖状态机
三、设计实现
key_ctrl_led顶层模块:
/**********************************************版权申明************************************************* ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: key_ctrl_led.v ** 创建者: CrazyBird ** 创建日期: 2015-7-12 ** 版本号: v1.0 ** 功能描述: 顶层模块将按键消抖模块和led控制模块连接起来,完成按键控制led的功能 ** ********************************************************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module key_ctrl_led( rst_n, clk, key_data, led_data ); //****************************************************************************** // 参数定义 //****************************************************************************** // 修改以下参数以满足需求 parameter CLK_CYCLE = 20; // 时钟周期,单位ns // 修改以上参数以满足需求 //****************************************************************************** // 输入/输出端口定义 //****************************************************************************** input rst_n; // 全局复位信号,低电平有效 input clk; // 全局时钟信号,50MHz input key_data; // 键值输入 output led_data; // led状态 //****************************************************************************** // 变量定义 //****************************************************************************** wire key_flag; //****************************************************************************** // 模块的连接 //****************************************************************************** // 例化KEY_Debounce模块 KEY_Debounce #( .CLK_CYCLE(CLK_CYCLE) ) u_KEY_Debounce ( .rst_n ( rst_n ), .clk ( clk ), .key_data ( key_data ), .key_flag ( key_flag ) ); // 例化led_ctrl模块 led_ctrl u_led_ctrl ( .rst_n ( rst_n ), .clk ( clk ), .led_en ( key_flag ), .led_data ( led_data ) ); //****************************************************************************** endmodule //*********************************************文件结束*****************************************************
KEY_Debounce模块:
/**********************************************版权申明*************************************************
** 电子技术应用网站, CrazyBird
** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: KEY_Debounce.v
** 创建者: CrazyBird
** 创建日期: 2015-7-12
** 版本号: v1.0
** 功能描述: 该模块主要负责完成按键的检测和消抖
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module KEY_Debounce(
rst_n,
clk,
key_data,
key_flag
);
//******************************************************************************
// 参数定义
//******************************************************************************
// 修改以下参数以满足需求
parameter CLK_CYCLE = 20; // 时钟周期,单位ns
parameter T0 = 10_000_000; // 10ms,按键消抖时间
// parameter T0 = 1000; // 测试用
parameter DELAY = 19; // 10ms计数器位宽
// 修改以上参数以满足需求
// 以下参数不要修改
parameter T0_VAL = (T0/CLK_CYCLE)-1; // 10ms,按键消抖时间
parameter IDLE = 2'b00,
JITTER1= 2'b01,
DEAL = 2'b10,
JITTER2= 2'b11;
// 以上参数不要修改
//******************************************************************************
// 输入/输出端口定义
//******************************************************************************
input rst_n; // 全局复位信号,低电平有效
input clk; // 全局时钟信号,50MHz
input key_data; // 键值输入
output reg key_flag; // 按键按下标志位
//******************************************************************************
// 变量定义
//******************************************************************************
wire delay_cnt_done;
//******************************************************************************
// 实现按键检测和消抖的状态机
//******************************************************************************
reg [1:0] state;
reg delay_cnt_en;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
state <= IDLE;
delay_cnt_en <= 1'b0;
key_flag <= 1'b0;
end
else
begin
case(state)
IDLE :
begin
if(key_data!=1'b1)
begin
state <= JITTER1;
delay_cnt_en <= 1'b1;
end
else
state <= IDLE;
end
JITTER1 :
begin
if(delay_cnt_done==1'b1)
begin
delay_cnt_en <= 1'b0;
if(key_data!=1'b1)
begin
state <= DEAL;
key_flag <= 1'b1;
end
else
state <= IDLE;
end
else
state <= JITTER1;
end
DEAL :
begin
key_flag <= 1'b0;
if(key_data==1'b1)
begin
state <= JITTER2;
delay_cnt_en <= 1'b1;
end
else
state <= DEAL;
end
JITTER2 :
begin
if(delay_cnt_done==1'b1)
begin
delay_cnt_en <= 1'b0;
if(key_data==1'b1)
state <= IDLE;
else
state <= DEAL;
end
else
state <= JITTER2;
end
endcase
end
end
//******************************************************************************
// 计数器的实现
//******************************************************************************
reg [DELAY-1:0] delay_cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
delay_cnt <= {(DELAY){1'b0}};
else if(delay_cnt_en==1'b1)
begin
if(delay_cnt_done==1'b1)
delay_cnt <= {(DELAY){1'b0}};
else
delay_cnt <= delay_cnt + 1'b1;
end
else
delay_cnt <= {(DELAY){1'b0}};
end
assign delay_cnt_done = (delay_cnt==T0_VAL);
//******************************************************************************
endmodule
//*********************************************文件结束*****************************************************led_ctrl模块:
/**********************************************版权申明************************************************* ** 电子技术应用网站, CrazyBird ** http://www.chinaaet.com, http://blog.chinaaet.com/crazybird ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: led_ctrl.v ** 创建者: CrazyBird ** 创建日期: 2015-7-12 ** 版本号: v1.0 ** 功能描述: 该模块根据按键每次按下时将led的状态取反 ** ********************************************************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module led_ctrl( rst_n, clk, led_en, led_data ); //****************************************************************************** // 输入/输出端口定义 //****************************************************************************** input rst_n; // 全局复位信号,低电平有效 input clk; // 全局时钟信号,50MHz input led_en; // led状态取反使能信号 output reg led_data; // led状态 //****************************************************************************** // led状态控制 //****************************************************************************** always @(posedge clk or negedge rst_n) begin if(rst_n==1'b0) led_data <= 1'b0; else if(led_en==1'b1) led_data <= ~led_data; else led_data <= led_data; end //****************************************************************************** endmodule //*********************************************文件结束*****************************************************
modelsim仿真结果:
从仿真结果看,本设计已实现了按键控制led的功能,同时也达到了按键消抖的效果。
最后对设计进行综合、布局布线、生成bit流文件、下载在红色飓风板子上,然后通过控制led灯的亮与灭。






