首页 > 其他分享 >07 FPGA按钮去抖实验

07 FPGA按钮去抖实验

时间:2023-12-29 11:12:28浏览次数:40  
标签:sysclk KEY FPGA key2 key1 按钮 key 按键 07

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录米联客(MILIANKE)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!

1 概述

按键的消抖,是指按键在闭合或松开的瞬间伴随着一连串的抖动,这样的抖动将直接影响设计系统的稳定性,降低响应灵敏度。因此,必须对抖动进行处理,即消除抖动的影响。实际工程中,有很多消抖方案,如RS触发器消抖,电容充放电消抖,软件消抖。本章利用FPGA内部来设计消抖,即采取软件消抖。

按键的机械特性,决定着按键的抖动时间,一般抖动时间在5ms~10ms。消抖,也意味着,每次在按键闭合或松开期间,跳过这段抖动时间,再检测按键的状态。只要通过简单的延时就可实现按键的消抖动。

2 硬件电路分析

硬件接口和子卡模块请阅读"附录 1"

配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。

3 系统框图

10ms定时器模块用于触发去抖状态机每间隔10ms判断一次按键状态。

寄存去亚稳态模块是对key进行异步多次寄存,消除亚稳态

去抖状态机每间隔10ms判断一次按键状态,分别判断按键的按下,按键的弹开

之后根据去抖状态机的状态切换判断按键是有效按下和有效弹开。

4 状态机流程图

状态机每间隔10ms完成一次按键检测,首先判断按键是否按下,如果按下再次判断按键是否按下,如果按下,检测按键是否松开,也是判断两次。如果按键按下后第二次判断按键没有按下,那么就代表按键按下的不稳定,继续回到开始状态机。

5 key模块的设计

由于按键滤波是比较比较通用的一个程序,因此我们可以把一个通用的程序设置为一个模块,方便后面重复使用。

`timescale 1ns / 1ns

 

module key #

(

parameter REF_CLK = 32'd50_000_000        //设置时钟为参数,方便上层调用修改

)

(

input  I_sysclk,

input  I_rstn,

input  I_key,

output O_key_down,

output O_key_up

);

 

parameter  T10MS = (REF_CLK/50 - 1'b1);                      //设置10MS的时钟分频计数

parameter  KEY_S0 = 2'd0;                                      //设置按键状态机的状态

parameter  KEY_S1 = 2'd1;

parameter  KEY_S2 = 2'd2;

parameter  KEY_S3 = 2'd3;

 

reg [32:0] t10ms_cnt = 25'd0;

reg [3:0] key_r = 4'd0;

//reg [1:0] key_s = 2'b0;

//reg [1:0] key_s_r = 2'b0;

//wire t10ms_done ;

assign t10ms_done = (t10ms_cnt == T10MS);

assign O_key_down   = (key_s == KEY_S2)&&( key_s_r == KEY_S1);    //设置判断按键按下时的条件

assign O_key_up     = (key_s == KEY_S0)&&( key_s_r == KEY_S3);     //设置判断按键松开时的条件

 

(*mark_debug = "true"*) reg [1:0] key_s = 2'b0;                   //mark_debug 语句可以观察信号波形

(*mark_debug = "true"*) reg [1:0] key_s_r = 2'b0;

(*mark_debug = "true"*) wire t10ms_done ;

 

//10ms timer counter

always @(posedge I_sysclk or negedge I_rstn)begin                 //系统时钟的上升沿以及复位的下降沿触发

    if(I_rstn == 1'b0)begin

        t10ms_cnt <= 25'd0;                                          //系统复位

    end

    else if(t10ms_cnt < T10MS)                                        //10ms计数,目标值是 T10MS

        t10ms_cnt <= t10ms_cnt + 1'b1;                                //未达到目标t10ms_cnt+1

    else

        t10ms_cnt <= 25'd0;                                             //达到目标值复位

end

always @(posedge I_sysclk)begin                                     //将key_s的状态缓存一拍

    key_s_r <= key_s;

end

always @(posedge I_sysclk)begin                                     //将I_key的状态缓存一拍

    key_r <= {key_r[2:0],I_key};

end

always @(posedge I_sysclk or negedge I_rstn)begin                //设置状态机,设定按键的4种状态

    if(I_rstn == 1'b0)begin

        key_s <= KEY_S0;

    end

    else if(t10ms_done)begin     //触发条件为t10ms_done,说明下列所有的状态转移都是每10ms

        case(key_s)                                                     //触发一次

        KEY_S0:begin

           if(!key_r[3])                          //收到第一个按键的低电平信号,不能判断是否为毛刺

               key_s <= KEY_S1;                                        //转到状态S1

        end  

        KEY_S1:begin//recheck key done                               //第二次判断按键是否按下

           if(!key_r[3])

               key_s <= KEY_S2;                                        //按下转入S2状态

            else

               key_s <= KEY_S0;                                  //没按下,判断为毛刺,转入S0状态,等待触发

        end

        KEY_S2:begin//wait key up                                     //确定按键按下后

           if(key_r[3])                                            //等待按键松开,接收到按键的高电平信号,

               key_s <= KEY_S3;                                        //不能确定是否为毛刺

        end                                                              //转入状态S3

        KEY_S3:begin//recheck key up                                  

           if(key_r[3])                                                 //第二次判断案件是否松开

              key_s <= KEY_S0;                        //依然检测到按键是松开的状态,转入S0状态,等待触发

        end

        endcase                  

    end

   

以上代码中,首先把系统时钟做分频,产生10ms 的分配时钟使能信号。在设计的状态机中,分4个状态

KEY_S0:判断按键是否按下,如果是,转移到状态KEY_S1;

KEY_S1:10ms后再次判断按键是否按下,如果是,转移状态到KEY_S2,否则继续回到KEY_S0;

KEY_S2:判断按键是否抬起,如果是,转移状态到KEY_S3;

KEY_S3:10ms后再次判断按键是否抬起,如果是,转移状态到KEY_S0,否则继续回到KEY_S2;

当状态从KEY_S1 转到KEY_S2代表依次按钮按下key_cap输出一次高电平。

6 调用key模块

以下代码中调用了key模块,并且通过两个按钮来分别控制LED等的向左跟向右偏移。

`timescale 1ns / 1ns

 

module key_top #

(

parameter REF_CLK = 64'd50_000_000                                     //设置时间参数,方便上层调用

)

(

input  I_sysclk,

input  I_rstn,

input  I_key1,

input  I_key2,

output [3:0]O_led

);

 

reg [3:0] led_r;                                                       //存储LED灯的状态

 

wire key1_down,key2_down;

 

assign O_led = led_r;                                                 //将存储的LED灯的状态输出

 

always @(posedge I_sysclk or negedge I_rstn)begin

    if(I_rstn == 1'b0)

       led_r <= 4'b1110;                                           //设置LED的初始状态

    else if(key1_down)

       led_r<={led_r[2:0],led_r[3]};                              //将LED灯左移动

    else if(key2_down)

       led_r<={led_r[0],led_r[3:1]};                                 //将LED灯右移动

end

 

key#(                                                //例化了两个KEY按键,一个负责左移一个负责右移

.REF_CLK(REF_CLK)

)

key_u1

(

.I_sysclk(I_sysclk),

.I_rstn(I_rstn),

.I_key(I_key1),

.O_key_down(key1_down),

.O_key_up()

);

 

key#(

.REF_CLK(REF_CLK)

)

key_u2

(

.I_sysclk(I_sysclk),

.I_rstn(I_rstn),

.I_key(I_key2),

.O_key_down(key2_down),

.O_key_up()

);

 

endmodule

7 综合布线前仿真时序

1、新建仿真文件,仿真文件源码如下所示。

`timescale 1ns / 1ns

 

module tb_key();

 

reg I_sysclk,I_rstn,I_key1,I_key2;

wire  [3:0]O_led;

 

key_top#

(

.REF_CLK(100_000)

)

key_top_inst

(

.I_sysclk(I_sysclk),

.I_rstn(I_rstn),

.I_key1(I_key1),

.I_key2(I_key2),

.O_led(O_led)

);

 

initial

   begin

      // Initialize Inputs

      I_sysclk_p = 0;

      I_rstn = 0;

      #100;

      I_rstn =1;

      I_key1 = 1;

      I_key2 = 1;

      #10000;

      forever

         begin

            I_key1 = 0;

            // Wait 100 ns for global reset to finish

            #100;

            I_key1=1; #1000;                       //100个时钟周期,频繁的翻转,模拟的是毛刺状态

            I_key1=0; #1000;

            I_key1=1; #2000;

            I_key1=0; #5000;

            #20000000;                             //20000000个时钟周期的KEY变化,确认为真实的按键操作

            I_key1=1;

            I_key1=0; #1000;

                I_key1=1; #2000;

                I_key1=0; #1000;

                I_key1=1; #2000;      

            #20000000;                         //20000000个时钟周期的KEY变化,确认为真实的按键操作

            I_key2=1; #1000;

            I_key2=0; #1000;

            I_key2=1; #2000;

            I_key2=0; #5000;

            #20000000;                            //20000000个时钟周期的KEY变化,确认为真实的按键操作

            I_key2=1;

            I_key2=0; #1000;

                I_key2=1; #2000;

                I_key2=0; #1000;

                I_key2=1; #2000;

            I_key2=0; #1000;  

            #20000000;                                  //20000000个时钟周期的KEY变化,确认为真实的按键操作

            I_key2=1; #1000;  

         end

   end

always #10 I_sysclk_p=~I_sysclk_p;

 

endmodule


2、进入仿真界面:SIMULATION->单击 Run Simulation->单击Run Behavioral Simulation。

Setp1:设置断点

之后再点击下图箭头所指

读者可以以这种方法去观察自己想看的内部信号

Setp2:取消断点,添加想观察的信号

Setp3:观察仿真波形

可以看到每次key1的按下,key1_down信号都会拉高表示捕捉到key1的按下,并且led灯进行偏移给我们反馈。

Setp4:放大信号观察毛刺

可以看到我们仿真模拟的按键抖动并不会触发我们的led灯的偏移,同时我们的key2_down信号也不会拉高反馈,说明按键抖动被很好的滤除。

8 Chipscope在线逻辑分析仪仿真

很多时候软件仿真后的代码也不一定完全执行正确,这个时候我们可以通过XILINX 自带的在线逻辑分析,在板子上运行并且查看关键信号。

1、将(*mark_debug = "true"*) 添加到需要观察的信号前面。

(*mark_debug = "true"*) reg [1:0] key_s = 2'b0;

(*mark_debug = "true"*) reg [1:0] key_s_r = 2'b0;

(*mark_debug = "true"*) wire t10ms_done ;

2、为了观察到信号,先点击Run Synthesis

点击ok

3、单击Set Up Debug 设置需要观察的信号

以下是我们要观察的信号

 

以下是这只在线逻辑分析仪的采样深度,使用的是FPGA的 BRAM,以及设置Captrue control,对于这种超慢信号,XILINX 的在线逻辑分析低于20M采样速度的,波形窗口就不会显示波形,这个XILINX也没有特别说明过,但是通过设置Captrue control,可以用我们这里的t10ms_done来作为扑捉控制,而采样时钟依然用系统时钟。

单击Finish后会出来下面的原理图设计,可以看到FPGA编程的本质还是回归电路设计。现在我们使用代码去设计电路。记得保存,否则无法观察到调试信号。下面的小蚂蚁,就是已经添加调试标记的信号。

4、编译程序

5、下载程序

6、设置触发,以及设置Capture 信号

Captrue mode 一定要设置为BASIC

Window data depth 为采样深度设置为2048最大

Trigger position inwindow 设置为1024

以上参数都可以根据需要用户自行设置

 

以信号t10ms_done作为Capture信号

以信号key_s以及key_s_r作为触发信号,条件是key_s == KEY_S2以及 key_s_r == KEY_S1,也就是key_s =2且key_s_r=1。因为我们代码中定义:

assign key_down   = (key_s == KEY_S2)&&( key_s_r == KEY_S1);

7、启动采集并且按下按键,可以连续多次按下按键

可以看到正确观察到了按键程序的状态机信号。

 

9 输出结果

(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)

请确保下载器和开发板已经正确连接,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

 

将程序下载。按键KEY1每按一次,LED灯进行向右偏移;按键KEY2每按一次,LED灯进行向左偏移;键KEY3每按一次,LED灯复位,LED灯响应无差错。为清晰的表示消抖的效果,可将延时参数设置很小,可以发现,按键有时候明明已经按下去了,LED却无响应。

标签:sysclk,KEY,FPGA,key2,key1,按钮,key,按键,07
From: https://www.cnblogs.com/milianke/p/17934345.html

相关文章

  • 08 FPGA多路分频器实验
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MILIANKE)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述在FPGA中,时钟分频是经常用到的。本节课讲解2分频、3分频、4分频和8分频的Verilog实现并且学习generate......
  • 05 FPGA流水灯实验
    软件版本:VIVADO2021.1操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1概述本章课程以大家熟悉的流水灯为例子,详细讲解了VIVADO软件的使用,包括创建FPGA工程,编写Verilog代码,添加管......
  • 2023-2024-1 20231307《计算机基础与程序设计》第十四周学习总结
    作业信息所属课程2023-2024-1-计算机基础与程序设计作业要求 第十四周作业(必学,选做)作业目标自学教材《C语言程序设计》第13章并完成实验作业正文https://www.cnblogs.com/lzt-/p/17933997.html教材学习内容总结13.1二进制文件和文本文件文本文件(也称ASCII......
  • numpy、scipy、pandas、matplotlib的读书报告07
    numpy库常用的创建数组(ndarray类型)使用实例:importnumpyasnpa=np.ones((4,5))#创建一个4行5列全是1的数组print(a)输出为:[[1.1.1.1.1.][1.1.1.1.1.][1.1.1.1.1.][1.1.1.1.1.]] matplotlib.pyplot为matplotlib的子库引用如下:importma......
  • Spring Cloud动力节点-07Alibaba简介、注册、配置中心
    1.项目简介SpringCloudAlibaba致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过SpringCloud编程模型轻松使用这些组件来开发分布式应用服务。依托SpringCloudAlibaba,您只需要添加一些注解和少量配置,就可以将SpringClo......
  • 《FPGA原理和结构》——读书笔记
    最近做了一个关于FPGA的项目后,读了《FPGA原理和结构》这本书。主要梗概内容和想法如下。第一章:理解FPGA所需要的基础知识理解FPGA我们需要数电的组合逻辑、时序逻辑等内容的知识。FPGA(20世纪70年度发展起来的,因为其具有通过组合使用器件内大量的逻辑块来实现所需的电路,比以往侠......
  • FPGA处理编码信号进行毛刺滤波
    目录一、前言二、滤波算法三、代码设计一、前言在利用处理编码信号时,一般在较为理想的环境下可以很方便进行计算,判断等。但是由于有时候受到电磁干扰等环境因素,会导致编码信号产生毛刺等,这时候如果不对编码信号进行预处理而是直接进行边缘判断等操作则极容易导致错误,所以需要提......
  • DAC转化——FPGA驱动LTC1446
    目录一、前言二、结合LTC1446芯片手册分析三、Verilog代码与仿真四、总结一、前言最近在学习利用FPGA结合DAC芯片实现数模转换,在实验中选择的LTC1446这款芯片。接下来自己将结合芯片手册进行分析,并编写Verilog代码并进行仿真验证。二、结合LTC1446芯片手册分析首先从上述第......
  • FPGA驱动AD9240实现AD转换
    目录一、前言二、时序原理三、代码设计四、结果验证一、前言在做项目中,经常会用到AD转换模块。前段时间做毕业设计的时候需要用到FPGA驱动AD9240模块实现模拟数据的采集和转换,尽管相对来说AD9240算比较简单的驱动模块,但是也想记录下分析和设计过程。二、时序原理首先通过芯片......
  • 移动端手写板 + 模态框 + 弹框,前端监听移动端返回按钮
    今天的需求是把全屏的手写板改为同一个页面只占半屏的手写板,本来用的iframe,后面发现笔触和屏幕按下的位置不一样,然后用了jQuery的$.load(),发现用$.load会导致文件中的js不执行,后面还是重新开始,在同文件重新写了一个canvas手写板,然后发现了,canvas在全屏的时候没问题,在容器......