FPGA|CPLD|ASIC论坛
直播中

郝会轩

10年用户 144经验值
擅长:嵌入式技术
私信 关注
[资料]

【锆石A4 FPGA试用体验】按键消抖例程存在的问题

本帖最后由 hhxdianzi 于 2016-9-10 22:56 编辑

    这是上周帖子的链接:https://bbs.elecfans.com/forum.ph ... d&tid=936046&extra=里面提到了锆石A4自带按键消抖的例程存在一点问题,这里用Modelsim Altera进行了验证一下。首先贴上例程源码方便大家查看。
  1. //---------------------------------------------------------------------------
  2. //--        文件名                :        A4_Ked2.v
  3. //--        作者                :        ZIRCON
  4. //--        描述                :        按键消抖
  5. //--        修订历史        :        2014-1-1
  6. //---------------------------------------------------------------------------
  7. module A4_Key2
  8. (
  9.         //输入端口
  10.         CLK_50M,RST_N,KEY,
  11.         //输出端口
  12.         LED
  13. );  

  14. //---------------------------------------------------------------------------
  15. //--        外部端口声明
  16. //---------------------------------------------------------------------------
  17. input                  CLK_50M;          //时钟的端口,开发板用的50MHz晶振
  18. input                      RST_N;          //复位的端口,低电平复位
  19. input          [ 7:0]        KEY;          //对应开发板上的KEY
  20. output        [ 7:0]        LED;         //对应开发板上的LED

  21. //---------------------------------------------------------------------------
  22. //--        内部端口声明
  23. //---------------------------------------------------------------------------
  24. reg                [20:0]       time_cnt;                       //用来计数按键延迟的定时计数器
  25. reg                [20:0]       time_cnt_n;                    //time_cnt的下一个状态
  26. reg                [ 7:0]        key_in_r;                        //用来接收按键信号的寄存器
  27. reg                [ 7:0]        key_out;                        //消抖完成输出按键
  28. reg                [ 7:0]        key_out_n;                    //key_out的下一个状态
  29. wire                              key_press;                     //检测按键有没有变化

  30. //设置定时器的时间为20ms,计算方法为  (20*10^3)us / (1/50)us  50MHz为开发板晶振
  31. parameter SET_TIME_20MS = 21'd1_000_000;        

  32. //---------------------------------------------------------------------------
  33. //--        逻辑功能实现        
  34. //---------------------------------------------------------------------------
  35. //时序电路,用来key_in_r寄存器赋值
  36. always @ (posedge CLK_50M, negedge RST_N)
  37. begin
  38.         if(!RST_N)                                      //判断复位
  39.                 key_in_r <= 8'h00;                //初始化key_in_r值
  40.         else
  41.                 key_in_r <= KEY;                   //将按键的值赋值给key_in_r
  42. end

  43. assign key_press = key_in_r ^ KEY;        //检测按键有没有变化

  44. //时序电路,用来给time_cnt寄存器赋值
  45. always @ (posedge CLK_50M, negedge RST_N)
  46. begin
  47.         if(!RST_N)                                      //判断复位
  48.                 time_cnt <= 21'h0;                //初始化time_cnt值
  49.         else
  50.                 time_cnt <= time_cnt_n;       //用来给time_cnt赋值
  51. end

  52. //组合电路,实现20ms的定时计数器
  53. always @ (*)
  54. begin
  55.         if(time_cnt == SET_TIME_20MS || key_press) //判断按键有没有变化、时间有没有到
  56.                 time_cnt_n = 21'h0;     //如果到达20ms或者按键有了变化,那么定时计数器将会被清零
  57.         else
  58.                 time_cnt_n = time_cnt + 1'b1; //如果未到20ms或者按键没有变化,那么定时计数器将会继续累加
  59. end

  60. //时序电路,用来key_out寄存器赋值
  61. always @ (posedge CLK_50M, negedge RST_N)
  62. begin
  63.         if(!RST_N)                                            //判断复位
  64.                 key_out <= 8'h00;                       //初始化key_out值
  65.         else
  66.                 key_out <= key_out_n;                //用来给key_out赋值
  67. end

  68. //组合电路,每20ms接收一次按键的值
  69. always @ (*)
  70. begin
  71.         if(time_cnt == SET_TIME_20MS)     //判断20ms时间
  72.                 key_out_n = key_in_r;        //如果到达20ms,接收一次按键的值
  73.         else
  74.                 key_out_n = key_out;       //如果未到20ms,保持原状态不变
  75. end

  76. assign LED = key_out;               //将消抖的按键值赋值给LED

  77. endmodule
    仔细查看以上代码,难免会有疑问,8个按键共用一个消抖延时计数器,如果不同按键接连按下,优先级怎么排?按下一个按键,消抖还未完成,是否允许第二个按键按下?如果第二个按键按下会不会刷新定时器重新计数?带着这些问题,编写了一个简单的仿真文件对按键进行测试,以下代码为单个按键测试文件:
  1. `timescale 1ns/1ns

  2. `define clk_period 20

  3. module Key_TB;

  4. reg CLK_50M;
  5. reg RST_N;
  6. reg [7:0]KEY;
  7. wire [7:0]LED;

  8. A4_Key2 Key
  9. (
  10.         .CLK_50M(CLK_50M),
  11.         .RST_N(RST_N),
  12.         .KEY(KEY),
  13.         .LED(LED)
  14. );

  15. initial CLK_50M = 1;
  16. always#(`clk_period/2)CLK_50M = ~CLK_50M;

  17. initial
  18.         begin
  19.                 RST_N = 1'b0;
  20.                 KEY = 8'b0000_0000;
  21.                 #(`clk_period*10) RST_N = 1'b1;
  22.                 #(`clk_period*10+1);

  23.                 KEY = 8'b0000_0000;#10000000;
  24.                 KEY = 8'b0000_0001;#1000;
  25.                 KEY = 8'b0000_0000;#1200;
  26.                 KEY = 8'b0000_0001;#1300;
  27.                 KEY = 8'b0000_0000;#800;
  28.                 KEY = 8'b0000_0001;#1000;
  29.                 KEY = 8'b0000_0000;#1000;
  30.                 KEY = 8'b0000_0001;#20000100;
  31.                
  32.                 #60000000;
  33.                
  34.                 KEY = 8'b0000_0000;#1200;
  35.                 KEY = 8'b0000_0001;#1300;
  36.                 KEY = 8'b0000_0000;#800;
  37.                 KEY = 8'b0000_0001;#1000;
  38.                 KEY = 8'b0000_0000;#1000;
  39.                 KEY = 8'b0000_0001;#1200;
  40.                 KEY = 8'b0000_0000;#20000100;

  41.                 #30000000;

  42.         
  43.                 $stop;        
  44.         end               

  45. endmodule
下面是模拟单个按键按下的波形:
单个按键测试.png
单个按键的没有问题,下面进行多个按键接连按下的测试,以下是测试文件:
  1. `timescale 1ns/1ns

  2. `define clk_period 20

  3. module Key_TB;

  4. reg CLK_50M;
  5. reg RST_N;
  6. reg [7:0]KEY;
  7. wire [7:0]LED;

  8. A4_Key2 Key
  9. (
  10.         .CLK_50M(CLK_50M),
  11.         .RST_N(RST_N),
  12.         .KEY(KEY),
  13.         .LED(LED)
  14. );

  15. initial CLK_50M = 1;
  16. always#(`clk_period/2)CLK_50M = ~CLK_50M;

  17. initial
  18.         begin
  19.                 RST_N = 1'b0;
  20.                 KEY = 8'b0000_0000;
  21.                 #(`clk_period*10) RST_N = 1'b1;
  22.                 #(`clk_period*10+1);

  23.       KEY = 8'b0000_0000;#30000000;
  24.       KEY = 8'b0000_0001;#10000000;
  25.                 KEY = 8'b0000_0011;#10000000;
  26.                 KEY = 8'b0000_0111;#10000000;
  27.                 KEY = 8'b0000_1111;#60000000;
  28.                 KEY = 8'b0000_0111;#10000000;
  29.                 KEY = 8'b0000_0011;#10000000;
  30.                 KEY = 8'b0000_0001;#10000000;
  31.                 KEY = 8'b0000_0000;#30000000;
  32.         
  33.                 $stop;        
  34.         end               

  35. endmodule
从下面的波形,可以明显看出,多个按键按下,如果定时器被占用的时候,后按下的按键并没有完成消抖时间就输出了,因此这里存在一些问题,个人目前的解决办法就是每个按键使用一个计数器。如果大家有好的解决办法,欢迎指导。
按键变傻了.png
Quartus II可以按列删除或修改的功能,批量修改比其他编译器方便很多,方法是ctrl + 鼠标左键,下面贴张图:
批量加点.png



回帖(1)

郝会轩

2016-9-10 19:54:43
留个沙发、、、
举报

更多回帖

发帖
×
20
完善资料,
赚取积分