按键消抖例程讲解
1.需求解读
1.1 需求
每按一次按键KEY1,数码管上显示的数字加1,从0到9循环
1.2 知识背景
按键是最为常见的电子元器件之一,在电子设计中应用广泛。在FPGA的实验工程中,我们可以使用其作为系统复位信号或者控制信号的外部输入;在日常生活中,遥控器、玩具、计算器等等电子产品都使用按键。目前按键种类繁多,常见的有自锁按键、薄膜按键等等。我们开发板上使用的机械按键也是按键的一种,特点是:接触电阻小 ,手感好,按键按下或弹起时有“滴答”清脆声;但由于其构造和原理,在按键闭合及断开的瞬间均伴随有一连串的抖动。
本实验中,我们要根据机械按键的构造与原理,设计并实现按键消抖模块。以开发板上的物理按键作为输入信号,使用设计的按键消抖模块对输入的按键信号进行消抖处理,输出能够正常使用的按键触发信号,再利用这个信号作为计数器的加1的触发信号,按键每按一次生成一个触发信号,计数器加1,数码管上显示的数字也加1。
我们所使用的按键开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而做的措施就是按键消抖。
抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起一次按键被误读多次。为确保控制器对按键的一次闭合仅作一次处理,必须去除按键的抖动。在按键闭合稳定时读取按键的状态,并且必须判别到按键释放稳定后再作处理 。
消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响。按键的消抖,可用硬件或软件两种方法。在此我们只讨论软件消抖方法。
检测出按键闭合后执行一个延时程序,根据抖动的时间为5ms~10ms,我们产生一个20ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。
1.3 硬件设计
1.4 接口说明
信号名 | 方向 | FPGA管脚号 | 说明 |
---|---|---|---|
CLK_50M | 输入 | E1 | 系统时钟,50Mhz |
KEY1 | 输入 | M1 | 独立按键,按下低电平 |
KEY2 | 输入 | M2 | 独立按键,按下低电平 |
KEY3 | 输入 | K10 | 独立按键,按下低电平 |
KEY4 | 输入 | L10 | 独立按键,按下低电平 |
KEY5 | 输入 | M16 | 独立按键,按下低电平 |
KEY6 | 输入 | M15 | 独立按键,按下低电平 |
SMG_W0 | 输出 | A2 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W1 | 输出 | A3 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W2 | 输出 | A4 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W3 | 输出 | B5 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W4 | 输出 | A5 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 |
SMG_W5 | 输出 | 位选控制信号,低电平可导通三极管,使其给数码管位选供电 | |
SMG_A | 输出 | A9 | 数码管段选控制信号,低电平点亮该段 |
SMG_B | 输出 | K8 | 数码管段选控制信号,低电平点亮该段 |
SMG_C | 输出 | D8 | 数码管段选控制信号,低电平点亮该段 |
SMG_D | 输出 | A7 | 数码管段选控制信号,低电平点亮该段 |
SMG_E | 输出 | E7 | 数码管段选控制信号,低电平点亮该段 |
SMG_F | 输出 | B9 | 数码管段选控制信号,低电平点亮该段 |
SMG_G | 输出 | A10 | 数码管段选控制信号,低电平点亮该段 |
SMG_DP | 输出 | C8 | 数码管段选控制信号,低电平点亮该段 |
总结:通过上述说明,可以将需求解读成:按下KEY1,计数器加1,数码管显示计数器的值即可。 为了使程序结构更加清晰,该例程我们采用模块化设计方法,一个按键消抖模块(key_xd),一个数码管显示模块(smg_drv),一个顶层模块(key_xd_top)。
2. 绘制理论波形图
3.新建QuartusII 工程
为了让工程看起来整洁,同时方便工程移植。我们新建4个文件夹,分别是Project,Source,Sim,Doc。
Project — 工程文件夹,里面放的QuartusII工程
Source — 源代码文件夹,里面放的工程源码(.v文件或.vhd文件)
Sim — 仿真文件夹,里面放的仿真相关的文件
Doc — 存放相关资料,比如数据手册,需求文档等
4.编写代码
4.1 按键消抖
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:实现按键消抖功能
///
module key_xd(
input rst_n ,//复位信号,低电平有效
input key_in ,//按键输入信号
input clk ,//50MHZ,一个时钟周期20ns
output reg key_out //输出信号,检测到有效按键时,输出一个高脉冲
);
parameter IDLE =4'd0;
parameter ST0 =4'd1;
parameter ST1 =4'd2;
parameter ST2 =4'd3;
parameter time_20ms=1000000;//20MS的时钟周期
reg [3:0] curr_st;
reg [19:0] wait_cnt;
reg key_in_ff1;
reg key_in_ff2;
always@(posedge clk)key_in_ff1<=key_in;
always@(posedge clk)key_in_ff2<=key_in_ff1;
//状态机
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
curr_st<=IDLE;
else case(curr_st)
IDLE:begin
if(key_in_ff2==0)
curr_st<=ST0;
else
;//curr_st<=IDLE;
end
ST0:begin
if(wait_cnt==time_20ms)
curr_st<=ST1;
else;
end
ST1:curr_st<=ST2;
ST2:begin
if(key_in_ff2)
curr_st<=IDLE;
else
;
end
default:;
endcase
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
wait_cnt<=0;
else if(curr_st==ST0)
wait_cnt<=wait_cnt+1;
else
wait_cnt<=0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
key_out<=0;
else if(curr_st==ST1)
key_out<=~key_in_ff2;
else
key_out<=0;
end
endmodule
4.2 数码管显示模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:实现数码管显示
///
module smg_drv(
input clk ,//时钟信号,50MHZ
input rst_n ,//复位信号,低电平有效
input key_in ,//触发信号
output [7:0] smg_seg ,//数码管段选
output [5:0] smg_bit //数码管位选
);
reg [3:0] cnt;//0到9
reg [7:0] en_code;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt<=0;
else if(key_in&&cnt==9)
cnt<=0;
else if(key_in)
cnt<=cnt+1;
else ;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
en_code<=0;
else case(cnt)
0:en_code<=8'h03;//0的编码
1:en_code<=8'h9f;//1的编码
2:en_code<=8'h25;//2的编码
3:en_code<=8'h0d;//3的编码
4:en_code<=8'h99;//4的编码
5:en_code<=8'h49;//5的编码
6:en_code<=8'h41;//6的编码
7:en_code<=8'h1F;//7的编码
8:en_code<=8'h01;//8的编码
9:en_code<=8'h09;//9的编码
default:;
endcase
end
assign smg_seg=en_code;
assign smg_bit=6'h00;
endmodule
4.3 顶层模块
///
//QQ:3181961725
//TEL/WX:13540738439
//工程师:Mr Wang
//模块介绍:顶层模块,例化按键消抖模块和数码管显示模块
///
module key_xd_top(
input clk,
input rst_n,
input key_in,
output [7:0] smg_seg,
output [5:0] smg_bit
);
//例化按键消抖模块
key_xd key_xd(
.rst_n (rst_n),
.key_in (key_in),
.clk (clk),//50mHZ,一个时钟周期20ns
.key_out (key_out)
);
//例化数码管显示模块
smg_drv Usmg_drv(
.clk (clk),
.rst_n (rst_n),
.key_in (key_out),
.smg_seg (smg_seg),
.smg_bit (smg_bit)
);
endmodule
5.编写仿真测试激励文件
`timescale 1ns/1ns
module key_xd_top_tb;
reg clk ;
reg rst_n ;
reg key_in ;
reg [31:0] cnt=0;
parameter time_20ms=1000000;//20MS的时钟周期
initial
begin
clk = 0;
rst_n=0;
#1000
rst_n=1;
end
always #10 clk=~clk;
always@(posedge clk)cnt<=cnt+1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
key_in<=1;
else if(cnt>time_20ms/2+time_20ms)//30ms~~
key_in<=$random;
else if(cnt<time_20ms/2)//0~10MS
key_in<=$random;
else if(cnt>time_20ms/2&&cnt<(time_20ms/2+time_20ms))//10ms~30ms
key_in<=0;
else;
end
key_xd_top Ukey_xd_top(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.smg_seg (),
.smg_bit ()
);
endmodule
6.Modelsim仿真
这个例程非常简单,只用了一条语句,所以不需要仿真验证。但是为了给大家演示一个完整的开发流程,这个实验我们也新建一个仿真工程,从最简单的一个代码开始教大家如何编写仿真激励文件以及如何使用Modelsim软件进行仿真。将第三步编写的源码和第四步编写的仿真测试激励文件一起加入到Modelsim仿真工程中,即可进行仿真观察波形。
Modelsim仿真一般有两种方法:
-
图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。
-
批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。为了让大家所学的能够很快的应用到工程实践,仅仅第一个实验和第二个实验,采用图形化界面仿真,后面的实验均采用批处理方式仿真。为了更贴近工程实际,我们采用批处理方式仿真。仿真出的波形如下图所示:
7.对比波形图
将第二步绘制的理论波形图与第六步Modelsim仿真出来的波形图进行对比,结果一致,说明我们的逻辑设计是正确的。如果发现比对结果不一致,就需要找到不一致的原因,最终要保证对比结果一致。
通过对比,理论波形与仿真波形一致,说明功能符合设计要求。
8.编译综合
9.绑定管脚
当工程编译成功后,即可进行管脚分配(需要参考开发板的原理图)。我们店铺的开发板和模块在PCB板上均标注了信号名,在绑定管脚时也可以直接参照实物的连接关系。
管脚映射关系如下所示:
10.再次编译综合
11 下载SOF文件
再次编译综合成功后便可以将生成的SOF文件下载到开发板
下载成功后,便可以观察到开发板上的实验现象,如果实验现象与设计需求相符,那说明我们的设计是没有问题的,即可进行下一步固化JIC文件操作
12 生成并固化JIC文件
FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成JIC文件。SOF文件转换成JIC文件步骤如下:
12.1 file–>Convert Programming File…
12.2 选择JIC文件以及配置芯片的型号,FPGA的型号![在这里插入图片描述](https://img-blog.csdnimg.cn/7404d01a22ec4f419e167176d33b2154.png#pic_center)
标号1:选择生成文件的格式,我们选择JIC文件
标号2:选择配置芯片的型号,我们选择EPCS16
标号3:修改生成JIC文件的名字以及存放路径
标号4:鼠标左键点击Flash Loader
标号5:选择FPGA的型号,我们开发板用的是EP4CE6F17C8这款FPGA,所以我们也应该选这个型号,如下图所示:
12.3 选择SOF文件
标号1:鼠标左键点击SOF Data
标号2:添加SOF文件,选中我们工程生成的SOF文件,如下图:
12.3 生成JIC文件
到此,JIC文件生成好,可以进行固化操作了。
12.4 固化JIC文件
固化好以后,掉电程序也不会丢失了!
实验现象
与设计需求吻合,设计完成!