• 【小月电子】FPGA开发板(XLOGIC_V1)系统学习教程-LESSON6


    按键消抖例程讲解

    若要观看该博客配套的视频教程,可点击此链接

    开发板实物图

    在这里插入图片描述
    在这里插入图片描述
    根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤。比如非常简单的项目,我们可以省去虚线框里面的步骤,但是我们的入门级课程,即使再简单,也按照这12个步骤来进行讲解。

    1. 需求解读

    1.1 需求

    每按一次按键KEY1,数码管上显示的数字加1,从0到9循环

    1.2 知识背景

    按键是最为常见的电子元器件之一,在电子设计中应用广泛。在FPGA的实验工程中,我们可以使用其作为系统复位信号或者控制信号的外部输入;在日常生活中,遥控器、玩具、计算器等等电子产品都使用按键。目前按键种类繁多,常见的有自锁按键、薄膜按键等等。我们开发板上使用的机械按键也是按键的一种,特点是:接触电阻小 ,手感好,按键按下或弹起时有“滴答”清脆声;但由于其构造和原理,在按键闭合及断开的瞬间均伴随有一连串的抖动。
    本实验中,我们要根据机械按键的构造与原理,设计并实现按键消抖模块。以开发板上的物理按键作为输入信号,使用设计的按键消抖模块对输入的按键信号进行消抖处理,输出能够正常使用的按键触发信号,再利用这个信号作为计数器的加1的触发信号,按键每按一次生成一个触发信号,计数器加1,数码管上显示的数字也加1。
    我们所使用的按键开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而做的措施就是按键消抖。
    在这里插入图片描述

    开发板上使用的按键外观图

    在这里插入图片描述

    机械按键抖动原理

        抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起一次按键被误读多次。为确保控制器对按键的一次闭合仅作一次处理,必须去除按键的抖动。在按键闭合稳定时读取按键的状态,并且必须判别到按键释放稳定后再作处理 。
        消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响。按键的消抖,可用硬件或软件两种方法。在此我们只讨论软件消抖方法。
        检测出按键闭合后执行一个延时程序,根据抖动的时间为5ms~10ms,我们产生一个20ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。

    1.3 硬件设计

    在这里插入图片描述

    图5.有源晶振

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    以KEY1为例,当按键未按下时,网络KEY1被上拉至3.3V,即为高电平;当按键按下时,网络KEY1即直接与地连接,那为低电平。

    1.4 接口说明

    信号名方向FPGA管脚号说明
    CLK50M输入B10时钟信号,50MHZ
    SMG_W0输出E12位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_W1输出B15位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_W2输出E15位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_W3输出H11位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_W4输出K16位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_W5输出K14位选控制信号,低电平可导通三极管,使其给数码管位选供电
    SMG_A输出F13数码管段选控制信号,低电平点亮该段
    SMG_B输出B16数码管段选控制信号,低电平点亮该段
    SMG_C输出J16数码管段选控制信号,低电平点亮该段
    SMG_D输出J13数码管段选控制信号,低电平点亮该段
    SMG_E输出G14数码管段选控制信号,低电平点亮该段
    SMG_F输出E13数码管段选控制信号,低电平点亮该段
    SMG_G输出G12数码管段选控制信号,低电平点亮该段
    SMG_DP输出J14数码管段选控制信号,低电平点亮该段
    KEY1输出E4独立按键,按下低电平

        总结:通过上述说明,可以将需求解读成:按下KEY1,计数器加1,数码管显示计数器的值即可。 为了使程序结构更加清晰,该例程我们采用模块化设计方法,一个按键消抖模块(key_xd),一个数码管显示模块(smg_drv),一个顶层模块(key_xd_top)。

    2. 绘制理论波形图

    在这里插入图片描述

    程序框图

    在这里插入图片描述

    按键消抖模块理论波形图

    3.新建ISE工程

    为了让工程看起来整洁,同时方便工程移植。我们新建4个文件夹,分别是Project,Source,Sim,Doc。
    Project — 工程文件夹,里面放的ISE工程
    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	ST3		=4'd4;
    	parameter	time_20ms=1000000;//20MS的时钟周期
    	
    	reg	[3:0] curr_st;
    	reg	[20: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:begin
    				if(key_in_ff2)
    					curr_st<=IDLE;
    				else
    					curr_st<=ST2;
    			end
    			ST2:curr_st<=ST3;
    			ST3: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==ST2)
    			key_out<=1;
    		else
    			key_out<=0;
    	end
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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(cnttime_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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    6.Modelsim仿真

    Modelsim仿真一般有两种方法

    1. 图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。

    2. 批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。前两讲采用的是图形化界面仿真的方式;为了更贴近工程实际,从第三讲开始,我们就采用批处理方式仿真。具体操作步骤可参考我们的视频教程
      仿真出的波形如下图所示:
      在这里插入图片描述

    7.对比波形图

    将第二步绘制的理论波形图与第六步Modelsim仿真出来的波形图进行对比,结果一致,说明我们的逻辑设计是正确的。如果发现比对结果不一致,就需要找到不一致的原因,最终要保证对比结果一致。通过对比,理论波形与仿真波形一致,说明功能符合设计要求。

    8.绑定管脚(编写UCF文件)

    NET "clk" TNM_NET = "clk";
    TIMESPEC TS_sys_clk_i = PERIOD "clk" 20 ns HIGH 50 %;
    NET "clk" 			LOC = B10	| IOSTANDARD = LVCMOS33 ;
    
    NET "rst_n" 		LOC = B3	| IOSTANDARD = LVCMOS33;
    NET "key_in" 		LOC = E4	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[7]" 	LOC = F13	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[6]" 	LOC = B16	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[5]" 	LOC = J16	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[4]" 	LOC = J13	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[3]" 	LOC = G14	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[2]" 	LOC = E13	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[1]" 	LOC = G12	| IOSTANDARD = LVCMOS33;
    NET "smg_seg[0]" 	LOC = J14	| IOSTANDARD = LVCMOS33;
    
    NET "smg_bit[5]" 	LOC = E12	| IOSTANDARD = LVCMOS33;
    NET "smg_bit[4]" 	LOC = B15	| IOSTANDARD = LVCMOS33;
    NET "smg_bit[3]" 	LOC = E15	| IOSTANDARD = LVCMOS33;
    NET "smg_bit[2]" 	LOC = H11	| IOSTANDARD = LVCMOS33;
    NET "smg_bit[1]" 	LOC = K16	| IOSTANDARD = LVCMOS33;
    NET "smg_bit[0]" 	LOC = K14	| IOSTANDARD = LVCMOS33;
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    9.添加.v和.ucf文件

    在这里插入图片描述

    10.编译综合,同时将未使用管脚设置为悬空状态

    1.设置未使用管脚为悬空状态
    在这里插入图片描述
    在这里插入图片描述

    2.编译综合
    在这里插入图片描述

    11.下载BIT文件

    编译综合成功后便可以将生成的BIT文件下载到开发板(记得插上下载器,同时开发板上电)
    1.打开IMPACT
    在这里插入图片描述

    2.搜索器件

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.选择bit文件

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    下载成功后,便可以观察到开发板上的实验现象,如果实验现象与设计需求相符,那说明我们的设计是没有问题的,即可进行下一步生成MCS文件

    12.生成MCS文件,同时固化到配置芯片中

    FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成MCS文件。
    BIT文件转换成MCS文件步骤:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    固化MCS文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    固化成功后,开发板断电再重新上电,可以观察到开发板仍然可以执行刚刚的功能。

  • 相关阅读:
    地图可视化:基于 Echarts + 百度地图bmap + 时间轴timeline + 多边形(multi)polygon + 点scatter 的可视化案例
    HarmonyOS ArkTS基础知识
    ffmpeg-go库的介绍
    多层感知机 MLP
    js中减少if-else的几个小技巧
    化合物应用 | 动物实验溶剂选择
    【GD32F427开发板试用】+demo的正确打开方式(一)
    中兴路由器、小米路由器无线信号强度对比
    2021第7届中国大学生程序设计竞赛CCPC桂林站, 签到题5题
    计算机内功内功修炼:信息的表示与和处理
  • 原文地址:https://blog.csdn.net/Moon_3181961725/article/details/126706273