09A-独立按键消抖实验01——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线——普利斯队长精心奉献

?

实验目的: 1.复习状态机的设计思想并以此为基础实现按键消抖

2.单bit异步信号同步化以及边沿检测

3.在激励文件中学会使用随机数发生函数$random

4.仿真模型的概念

实验平台:芯航线FPGA核心板

实验原理:

????按键在电子设计中使用的最多,从复位到控制设置均可以看到其身影。现在按键的功能也种类也越来越多,例如多向按键、自锁按键、薄膜按键等。普通按键其硬件示意图如图9-1所示。

图9-1 按键示意图

芯航线开发板所载的为两脚贴片按键,分别位于开发板正面的左下角以及右下角,原理图如图9-2所示。按键不按下时IO口为高电平,按键按下时则变为电平,因此系统即可通过检测IO的电平来判断按键的状态。

图9-2 按键原理图

????从图9-1中可以看到按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,然而物理抖动便会产生电平的抖动,总的来说,按键从按下再到松开的过程,若检测其电平变化,即如图9-4所示。

图9-4 按键从按下到松开的电平变化

图9-4中产生的抖动次数以及间隔时间均是不可预期的,这就需要滤波来消除抖动过程中可能造成的影响。一般情况抖动的总时间会持续20ms以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,其中硬件电路消除的电路图之一如图9-5所示。

图9-5 利用RS触发器进行硬件消抖

图9-5中两个"与非"门构成一个RS触发器。此时即使B点的电压波形是抖动的,但经双稳态电路之后,其输出为正规的矩形波。当按键未按下时,输出为1;当键按下时,输出为0。

此外还可以通过利用电容器和电阻对噪声波形积分,吸收因触点跳动产生的噪声。也可以通过555定时器组成的单稳态触发器同样可以消除开关的抖动。这两种电路此处不再详述。

????对于FPGA通常使用状态机来进行消抖设计,在图9-4中可看出若按照第08讲的状态机概念对其进行状态编码即存在以下状态:未按下时空闲状态IDLE、按下抖动滤除状态FILTER0、按下稳定状态DOWN以及释放抖动滤除状态FILTER1。若对其进行独热码编码分别为0001,0010,0100以及1000。其状态转换图如图9-6所示。因此其为一个米利型状态机,输出与输入相关。

图9-6 状态转换图

其状态转移条件如表9-1所示。


当前状态


下一状态


转移条件


IDLE


FILTER0


nedge


FILTER0


IDLE


pedge


FILTER0


DOWN


Cnt_full


DOWN


FILTER1


Pedge


FILTER1


DOWN


nedge


FILTER1


IDLE


Cnt_full

表9-1 状态转移条件

实验步骤:

新建一个以名为key_filter的工程保存在prj下,并在本工程目录的rtl文件夹下新建verilog file文件并以key_filter.v保存。

????其模块接口如图9-7所示,即可得出如下的接口声明。

图9-7 按键消抖模块接口


input Clk;

input Rst_n;

input key_in;

?

output
reg key_flag;

output
reg key_state;

?

这里的按键输入信号key_in相对于FPGA内部信号来说是一个异步信号,这里就需要对其使用两级同步D触发器进行进行异步信号的同步化,同步后的信号为key_in_sb。????


reg key_in_sa,key_in_sb;

always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)begin

key_in_sa <=
1‘b0;

key_in_sb <=
1‘b0;

end

else
begin

key_in_sa <= key_in;

key_in_sb <= key_in_sa;

end

????

由实验原理中的状态转移表可以看出其转换条件中需要检测到下降沿以及上升沿,而边沿检测其原理就是利用两级寄存器寄存的不同值来进行比较判断,如图9-8所示。

图9-8 边沿检测原理图

其检测过程,可以假设data_in从0变1,也就是上升沿:

第一个时钟到来第一个寄存器regc的输出为0;

第二个时钟沿到来后第一个寄存器输出为1,第二个寄存器输出此时为0,这样对两个寄存器输出进行相关组合逻辑运算则可检测出,

同理data_in从1变为0,也就是下降沿:

第一个时钟到来第一个寄存器regc的输出为1;

第二个时钟沿到来后第一个寄存器输出为0,第二个寄存器输出此时为1。

?

本部分逻辑设计如下,这样就实现了当有上升沿时信号pedge就会产生一个时钟周期的高电平,当有下降沿时信号nedge也会产生一个时钟周期的高电平,没有上升沿或者下降沿变化时pedge以及nedge保持低电平状态。 这里使用的"!"是逻辑非运算,对0110逻辑非运算后是0000;而"~"是按位取反,对0110按位取反后是1001。


reg key_tmpa,key_tmpb;

wire pedge,nedge;

always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)begin

key_tmpa <=
1‘b0;

key_tmpb <=
1‘b0;

end

else
begin

key_tmpa <= key_in_sb;

key_tmpb <= key_tmpa;

end

?

assign nedge =
!key_tmpa & key_tmpb;

assign pedge = key_tmpa &
(!key_tmpb);

????

还应有20ms计数器模块以及计数器使能模块,这里也可以合并成一个always模块。一般还是推荐一个always块只对一个信号进行操作。


reg
[19:0]cnt;

reg en_cnt;
//使能计数寄存器

//计数使能模块

always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)

cnt <=
20‘d0;

else
if(en_cnt)

cnt <= cnt +
1‘b1;

else

cnt <=
20‘d0;

//计数模块

always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)

cnt_full <=
1‘b0;

else
if(cnt ==
999_999)

cnt_full <=
1‘b1;

else

cnt_full <=
1‘b0;

????

现在开始状态机设计,首先用本地参数化定义来定义其状态机。


localparam

IDEL =
4‘b0001,

FILTER0 =
4‘b0010,

DOWN =
4‘b0100,

FILTER1 =
4‘b1000;

????由于状态以及判断条件较少,此处先用一段式状态机来进行描述。当复位时候将计数器清零,状态回到IDLE,key_flag与key_state也回到初始态。


reg
[3:0]state;

always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)begin

en_cnt <=
1‘b0;

state <= IDEL;

key_flag <=
1‘b0;

key_state <=
1‘b1;

end

else
begin

case(state)

//。。。。。。。。

default:

begin

state <= IDEL;

en_cnt <=
1‘b0;

key_flag <=
1‘b0;

key_state <=
1‘b1;

end

?

endcase

end

????

在未按下时空闲状态IDLE时,如果检测到下降沿则状态进入按下抖动滤除状态FILTER0并使能计数器,否则继续保持IDLE状态。


IDEL :

begin

key_flag <=
1‘b0;

if(nedge)begin

state <= FILTER0;

en_cnt <=
1‘b1;

end

else

state <= IDEL;

end

????当在FILTER0状态时,如果20ms尚未计时结束就有上升沿到来,则认为此时还是按键按下抖动过程,状态回到IDLE并清0计数器。按下过程中当最后一次抖动后不会存在上升沿,计数器则可以一直计数,计数满后则将key_flag置1、key_state置0、状态进入按下稳定状态DOWN并将计数器清0。这样就可以通过判断key_flag && !key_state来确定按键的状态,为1则按下。

?

?


FILTER0:

if(cnt_full)begin

key_flag <=
1‘b1;

key_state <=
1‘b0;

en_cnt <=
1‘b0;

state <= DOWN;

end

else
if(pedge)begin

state <= IDEL;

en_cnt <=
1‘b0;

end

else

state <= FILTER0;

?

????进入DOWM状态后将key_flag清0,如果检测到上升沿则进入释放抖动滤除状态FILTER1,否则保持当前态。


DOWN:

begin

key_flag <=
1‘b0;

if(pedge)begin

state <= FILTER1;

en_cnt <=
1‘b1;

end

else

state <= DOWN;

end

?

????进入FILTER1状态后,如果20ms计数尚未结束就检测到下降沿,则认为此时还是按键释放抖动过程,状态回到DOWN并清0计数器。释放过程中当最后一次抖动后不会存在下降沿,计数器则可以一直计数,计数满后则将key_flag与key_state均置1、状态进入IDLE并将计数器清0等待下一次按键被按下。


FILTER1:

if(cnt_full)begin

key_flag <=
1‘b1;

key_state <=
1‘b1;

state <= IDEL;

en_cnt <=
1‘b0;

end

else
if(nedge)begin

en_cnt <=
1‘b0;

state <= DOWN;

end

else

state <= FILTER1;

?

这里如果改写为两段式,则如下所示,状态转移部分省略。


always@(posedge Clk or
negedge Rst_n)

if(!Rst_n)
begin

state <= IDLE;end

else

state <= nx_state;

?

always@(state or key_in or key_flag or key_state)

begin

en_cnt =
1‘b0;

key_flag =
1‘b0;

key_state =
1‘b1;

case(state)

…………….

end

?

进行分析和综合直至没有错误以及警告。

????为了测试仿真编写测试激励文件,新建key_filter_tb.v文件保存到testbench文件夹下,本激励文件除产生正常的时钟以及复位信号外,还人为模拟了按键从按下到松手释放的过程,人为产生的抖动之一如下所示,复制几次并在合理范围内修改参数。再次进行分析和综合直至没有错误以及警告。


//模拟按下抖动20ms内

key_in =
0;#1000;

key_in =
1;#2000;

key_in =
0;#1400;

key_in =
1;#2600;

key_in =
0;#1300;

key_in =
1;#200;

//产生一个低电平大于20ms

key_in =
0;#20_000_100;

#50_000_000;

//模拟释放抖动20ms内

key_in =
1;#2000;

key_in =
0;#1000;

key_in =
1;#2000;

key_in =
0;#1400;

key_in =
1;#2600;

key_in =
0;#1300;

key_in =
1;#200;

//产生一个高电平大于20ms

key_in =
1;#50_000_100;

#30000;

????设置好仿真脚本后进行功能仿真,可以看到产生的按下抖动以及松开抖动如图9-9所示。

图9-9-1按下抖动

图9-9-2释放抖动

????其局部仿真图如图9-10所示,可以看出当key_in从正常态到按下稳定后状态经历了0001、 0010、0100三个状态,从按下到释放经历了0100、1000、0001三个状态,且按下稳定以及释放稳定均会产生一个时钟周期的key_flag高电平信号,key_state也会正常变化。

图9-10按键消抖仿真波形图

从图9-9可以看出是人为的设计了一些抖动,不具有随机性且编写出来的文件太长,因此继续采用随机数发生函数来产生抖动。

$random这一系统函数可以产生一个有符号的32bit随机整数。一般的用法是$random%b,其中b>0。这样就会生成一个范围在(-b+1):(b-1)中的随机数。如果只想要正数的随机数即可采用{$random}%b来产生。在工程中需要产生在20ms的按下与松手抖动,道理上应该产生20_000_000以内随机数的抖动,这里为了节约仿真时间,只产生一个16位的随机数也就是0到65535。

????在人为产生抖动时复制多次抖动后可以看出激励文件比较长,这里采用使用任务task,其语法如下:


task
<任务名>;

<端口及数据型声明语句>

<语句1>

<语句2>

<语句n>

endtask

????

任务的调用的语法如下:


<任务名>(端口1,端口2,...端口n);

????综合以上两点,编写出以下设计文件,这里实现了五十次的0-65535ns按下抖动然后key_in赋固定值0且延时50ms(大于20ms即为稳定),同时也实现了释放抖动后key_in赋固定值1且延时50ms。


reg
[15:0]myrand;

?

task press_key;

begin

repeat(50)
begin

myrand =
{$random}%65536;

#myrand key_in =
~key_in;

end

key_in =
0;

#50_000_000;

?

repeat(50)
begin

myrand =
{$random}%65536;

#myrand key_in =
~key_in;

end

key_in =
1;

#50_000_000;

end

endtask

????这样在key_filter_tb.v中除了产生时钟以及复位信号后只需调用这个任务即可。


initial
begin

Rst_n =
1‘b0;

key_in =
1‘b1;

#(`clk_period*10) Rst_n =
1‘b1;

#30000;

press_key;

#10000;

?

press_key;

#10000;

?

press_key;

#10000;

$stop;

end

????

编译无误后再次启动仿真,可以看到如图9-11所示的仿真波形图,按下稳定以及释放稳定均会产生一个时钟周期的key_flag高电平信号,key_state也会正常变化。这里放大产生的抖动过程可以看到每一个抖动时间均不一样,这样就成功模拟了随机抖动过程。

图9-11-1引入task后的仿真波形图

图9-11-2 随机抖动过程

????这里再提出一个仿真模型的概念,新建key_module.v保存到testbench文件夹下并输入以下内容。


`timescale
1ns/1ns

?

module key_model(key);

?

output
reg key;

?

reg
[15:0]myrand;

?

initial
begin

key =
1‘b1;

press_key;

#10000;

press_key;

#10000;

press_key;

$stop;

end

?

task press_key;

begin

*******

end

endtask

?

endmodule

????

这样在key_filter_tb中只调用这个仿真模型以及产生复位以及时钟信号即可,更简化了激励文件。这样key_in就是一个内部信号了,需要将reg型改为wire型。


key_model key_model(.key(key_in));

????

这样整个激励文件的内部结构即为图9-12所示:

图9-12 激励文件内部结构

这里的仿真模型也是不可综合,需要在设置脚本时需要额外添加进来,如图9-13所示。

?

图9-13 将仿真模型加入激励文件

再次运行仿真,同样可以看到图9-14的仿真波形图。

图9-14 引入按键仿真模型后的仿真波形图

????这样就完成了单个按键的消抖设计与验证,且复习了状态机相关设计思路以及仿真模型的概念与应用。

?

?

?

时间: 2024-10-13 01:07:18

09A-独立按键消抖实验01——小梅哥FPGA设计思想与验证方法视频教程配套文档的相关文章

09B-独立按键消抖实验02——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯队长精心奉献 ? 实验目的: 1.复习按键的设计 2.用模块化设计的方式实现每次按下按键0,4个LED显示状态以二进制加法格式加1,每次按下按键1,4个LED显示状态以二进制加法格式减1 实验平台:芯航线FPGA核心板 实验原理:???? ????在上一讲中设计并验证了独立按键的消抖,这里基于上一讲的按键消抖模块来实现一个加减法计数器,并以此学习模块化的设计方式. ????在设计过程中,相对大一点的工程经常通常不会写在一个设计文件中,通常会针对不同的功能设计出不同的子文件,最后在

08-FPGA状态机设计实例——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯队长精心奉献 ? 实验目的:1.学习状态机的相关概念 2.理解一段式.两段式以及三段式状态机的区别以及优缺点 实验平台:芯航线FPGA核心板 实验原理: 状态机全称是有限状态机(finite-state machine,缩写:FSM)是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型. 状态机分为摩尔(Moore)型有限状态机与米利(Mealy)型有限状态机.摩尔状态机输出是只由输入确定的有限状态机(不直接依赖于当前状态).米利有限状态机的输出不止与其输入有关还于它的

02-FPGA设计流程介绍——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线——普利斯队长精心奉献 课程目标: 1.了解并学会FPGA开发设计的整体流程 2.设计一个二选一选择器并进行功能仿真.时序仿真以及板级验证 实验平台:芯航线FPGA开发板.杜邦线 实验内容: 良好的文件夹设置以及工程管理是一个好的FPGA设计的基础,在学习之初就建立俩良好的习惯,会少走一些弯路.因此我们首先在新建的工程文件夹下面,分别建立如图2-1所示的子文件夹. 图2-1 FPGA工程子文件夹 上图中,prj为工程文件存放目录:rtl为verilog可综合代码存放目录:testbench

06-BCD计数器设计与应用——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯队长精心奉献 ? 实验目的:1.掌握BCD码的原理.分类以及优缺点 ???????? 2.设计一个多位的8421码计数器并进行验证 ???????? 3.学会基本的错误定位以及修改能力 ???? 实验平台:无 实验原理: ????BCD码(Binary-Coded Decimal)又被称为二进码十进数.二-十进制代码是一种十进制的数字编码,用4位二进制数来表示十进制数中的0~9个十个数之一.BCD编码又可以分成有权码和无权码两种,其中有权码如:8421码.2421码以及5421等

05-IP核应用之计数器——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯顿队长精心奉献 ? 实验目的:了解FPGA的IP核相关知识并以计数器IP核为例学会基本IP使用的流程 实验平台:无 实验原理: ????IP核(Intellectual Property core),也被称为知识产权核,其分为软核.硬核和固核.软核通常是与工艺无关.具有寄存器传输级硬件描述语言描述的设计代码,可以进行后续设计:硬核是前者通过逻辑综合.布局.布线之后的一系列工艺文件,具有特定的工艺形式.物理实现方式:固核则通常介于上面两者之间,它已经通过功能验证.时序分析等过程,设

10-8位7段数码管驱动实验——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯队长精心奉献 ? 实验目的: 1.实现FPGA驱动数码管动态显示: 2.使用In system sources and probes editor工具,输入需要显示在数码管上的的数据,数码管显示对应数值. 实验平台:芯航线FPGA核心板.数码管_VGA_PS2模块 实验原理: ????电子设计系统中常用的显示设备有数码管.LCD液晶以及VGA显示器等.其中数码管又可分为段式显示(7段.米字型等)以及点阵显示(8*8.16*16等),LCD液晶的应用可以分为字符式液晶(1602.1

07-阻塞赋值与非阻塞赋值原理分析——小梅哥FPGA设计思想与验证方法视频教程配套文档

芯航线--普利斯队长精心奉献 ? 实验目的:掌握阻塞赋值与非阻塞赋值的区别 实验平台:无 实验原理: ????阻塞赋值,操作符为"=","阻塞"是指在进程语句(initial和always)中,当前的赋值语句阻断了其后的语句,也就是说后面的语句必须等到当前的赋值语句执行完毕才能执行.而且阻塞赋值可以看成是一步完成的,即:计算等号右边的值并同时赋给左边变量. ????非阻塞赋值,操作符为"<=","非阻塞"是指在进程语句(

关于按键消抖实验

对于特权同学按键消抖程序的理解:(程序源码见<深入浅出玩转FPGA>P191) 第一个always块中,在每个时钟周期(clk)对按键值进行采样 第二个always块中,利用边沿脉冲检测法,当key_rst有下降沿时,key-an将输出一个周期的高脉冲 第三个always块中,利用cnt进行循环计数(计数时间周期大约20ms),当keg-an为1时,cnt将清零,然后从零开始计数(由此产生一个20ms,从而消除抖动部分的影响) 第四个always块中,每当cnt从0计数到20'hfffff(即

单片机第7课:独立按键消抖程序

JP3连接P0口,数码管的VCC接+5V电源,JP5的8.1分别接P2.0和P2.1. #include<reg51.h> /* * 功能:独立按键消除抖动程序,按下KEY1,数值加1,按下KEY8,数值减去1 * */ #define uchar unsigned char #define uint unsigned int int num; uchar code table[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80,