当前位置:   article > 正文

SWJTU 数电实验报告——按键防抖动_数电实验按键防抖动设计

数电实验按键防抖动设计

实验内容:

一、 实验目的

1. 学习有限状态机的设计。 2. 学习信号边沿抖动的消除方法。

二、 基本实验内容

由于金属弹性形变的原因,按键/开关在状态切换 过程中总是会有或多或少的抖动情况,有时这种抖动 会导致电路误动作,甚至无法正常工作。比如在设置 参数时,按一下“加 1”可能会加 4~5 个数甚至更多; 生活中常见的情况是鼠标单击变成了双击等。因此, 在很多时候,输入电路的信号需要经过防抖动处理之 后才会送到后级电路。

按键抖动的电压波形如图 1 如示,tjitter是抖动时间,通常在 1ms~30ms 之间。

按键抖动消除有多种方法: 1. 积分法(模拟电路); 2. 施密特触发器(模拟电路); 3. 延迟再次判断法; 4. 持续稳定判断法。

实验要求:

用 Verilog HDL 设计一个按键防抖动电路,要求用有限状态机实现。防抖动电路的输入 接实验箱的按键/开关(SW0),输出接实验五 1 位计数译码显示电路的时钟输入,实现每按 一次按键(或拨一次开关)计数器加 1,多次测试不出现抖动乱加现象,电路对按键的响应 无明显滞后。

三、 提高性实验

自拟实验方法,测试实验箱的按键抖动时间,精确到 0.1ms。

四、 预习要求

1. 简要写出电路设计思路,列出电路所需状态,画出状态转移图。 2. 分析、计算电路需要多高的时钟频率。 3. 自行查阅 Verilog HDL 编写有限状态机资料,完成实验程序代码编写。 4. 用 ModelSim 对实验电路进行功能仿真,并将仿真结果截图、打印。 5. 列出引脚锁定分配表(信号名->主板器件名->引脚号)。

五、 实验报告要求

1. 列出程序代码(有详细注释)。 2. 附 Quartus 生成的状态图 3. 附仿真测试波形。 4. 列出实验过程出现的问题及解决措施。

设计思路:

本次实验要求实现了一个用于防抖动的按键状态机,可以处理由于按键在状态切换时产生的抖动情况,并输出稳定的按键状态信号和状态机状态。以下是代码的主要设计思路和功能:

1.状态定义:定义了四个状态:`key0`、`key1`、`key2`、`key3`,分别代表按键的不同状态。这些状态用于表示按键的松开和按下过程中的不同状态情况。

2. 计数器和状态机更新:使用 `always` 块结合时钟信号和复位信号,在时钟的上升沿和复位信号变化时,更新状态机的状态和计数器的值。根据状态的变化和计数器的值,控制状态机状态的转换和计数器的计数过程。

3. 按键状态机逻辑: 在 `always` 块中通过 `case` 语句实现按键状态机的逻辑。根据当前状态和输入信号(按键是否按下),切换状态以及控制按键状态的输出。通过状态转换和计数器的变化,实现对按键状态的稳定检测和切换,避免了按键抖动带来的误触发问题。

4. 输出:输出了防抖后的按键状态 `key_state` 和进位信号 `CO`。`key_state` 表示稳定的按键状态,`CO` 则用于其他逻辑或模块的触发或控制。

状态机在按键输入的过程中会经历松开状态、松开毛刺状态、按下毛刺状态和按下状态,并根据输入信号和计数器的状态来稳定地输出按键状态,并在稳定状态下输出进位信号。整个设计的核心思想是通过状态机的状态切换和计数器的计数,实现对按键状态的稳定检测和输出。

代码:

顶层文件:
  1. module top(clk,reset_n,key,key_state,Q,codeout,CO,state,cnt);
  2. input clk,reset_n,key;// 输入时钟信号、复位信号和按键信号
  3. output [3:0] Q ; // 计数器
  4. output [6:0] codeout; // 七段数码管
  5. output CO; // 进位信号
  6. output key_state; // 防抖状态
  7. output [3:0]state; // 临时状态输出
  8. output [20:0] cnt; // 计数器状态输出
  9. son1 fun1(clk,reset_n,key,key_state,state,cnt);
  10. son2 fun2(key_state,reset_n,Q,CO);
  11. son3 fun3(Q,codeout);
  12. endmodule
子文件:
状态机代码:
  1. module son1(clk,reset_n,key,key_state,state,cnt);
  2. input clk,reset_n,key;// 输入时钟信号、复位信号和按键信号
  3. output reg key_state; // 防抖状态
  4. reg cnt_en; // 计数使能信号
  5. output reg [20:0] cnt; // 计数器,用于计算时间
  6. output reg [3:0] state; // 状态寄存器,用于记录按键状态机的状态
  7. parameter key0 = 4'b0001; //松开状态 // 按键状态机的状态参数
  8. parameter key1 = 4'b0010; //松开毛刺状态
  9. parameter key2 = 4'b0100; //按下毛刺状态
  10. parameter key3 = 4'b1000; //按下状态
  11. // 计数器和状态机的更新
  12. always @ (posedge clk, negedge reset_n) begin
  13. if (!reset_n)
  14. cnt <= 0; // 复位时将计数器清零
  15. else if (cnt_en)
  16. cnt <= cnt + 1'b1; // 根据计数使能信号更新计数器值
  17. else
  18. cnt <= 0; // 当计数使能信号为0时,将计数器清零
  19. end
  20. // 按键状态机逻辑
  21. always @ (posedge clk, negedge reset_n) begin
  22. if (!reset_n) begin
  23. state <= key0; // 复位时,将状态设置为初始状态key0
  24. cnt_en <= 0; // 复位时,将计数使能信号清零
  25. key_state <= 0; // 复位时,将按键状态清零
  26. end
  27. else begin
  28. case(state)
  29. key0: begin
  30. key_state <= 1'b0; // 设置按键状态为低电平
  31. if (key == 1 && cnt_en == 0) begin
  32. state <= key1; // 如果按键被按下且计数使能信号为0,则切换到状态key1
  33. cnt_en <= 1'b1; // 启动计数使能信号
  34. end
  35. else state <= key0; // 否则保持当前状态
  36. end
  37. key1: begin
  38. key_state <= 1'b0; // 设置按键状态为低电平
  39. if (cnt == 21'd100000 && key == 1) begin
  40. state <= key2; // 如果计数器达到阈值且按键仍被按下,则切换到状态key2
  41. cnt_en <= 1'b0; // 关闭计数使能信号
  42. end
  43. else if (cnt == 21'd100000 && key == 0) begin
  44. state <= key0;
  45. cnt_en <= 1'b0;
  46. end
  47. else state <= key1; // 否则保持当前状态
  48. end
  49. key2: begin
  50. key_state <= 1'b1; // 设置按键状态为高电平
  51. if (key == 0 && cnt_en == 0) begin
  52. state <= key3; // 如果按键被释放且计数使能信号为0,则切换到状态key3
  53. cnt_en <= 1'b1; // 启动计数使能信号
  54. end
  55. else state <= key2; // 否则保持当前状态
  56. end
  57. key3: begin
  58. key_state <= 1'b1; // 按键状态返回初始状态key0
  59. if (cnt == 21'd100000 && key == 0) begin
  60. state <= key0; // 如果计数器达到阈值且按键已释放,则切换到空闲状态
  61. cnt_en <= 1'b0; // 关闭计数使能信号
  62. end
  63. else if (cnt == 21'd100000 && key == 1) begin
  64. state <= key2;
  65. cnt_en = 1'b0;
  66. end
  67. else state <= key3; // 否则保持当前状态
  68. end
  69. default: state <= key0; // 默认情况下将状态设置为初始状态key0
  70. endcase
  71. end
  72. end
  73. endmodule
计数器代码: 
  1. module son2(clk, clr, Q, CO);
  2. input clk, clr; // 输入时钟信号和清零信号
  3. output reg [3:0] Q; // 输出4位计数值
  4. output CO; // 输出进位信号
  5. always @ (posedge clk, negedge clr) begin
  6. if (!clr) // 当清零信号为低电平时
  7. Q <= 4'd0; // 将计数器值清零
  8. else begin
  9. if (Q == 4'd9) // 如果计数器值为9
  10. Q <= 4'd0; // 将计数器值重置为0
  11. else
  12. Q <= Q + 1'd1; // 否则计数器值加1
  13. end
  14. end
  15. assign CO = (clk & Q == 4'd9); // 判断是否达到9,输出进位信号
  16. endmodule
控制LED灯数值显示代码: 
  1. module son3(indec,codeout);
  2. input[3: 0] indec; // 说明输入信号的数组宽度为4
  3. output[6: 0] codeout; // 说明输出信号的数组宽度为7
  4. reg[6: 0] codeout; // 定义为寄存器
  5. always@(indec)
  6. begin
  7. case(indec) // indec作为判别常量
  8. // 根据输入信号indec,输出相应的codeout
  9. 4'd0: codeout = 7'b1111110;
  10. 4'd1: codeout = 7'b0110000;
  11. 4'd2: codeout = 7'b1101101;
  12. 4'd3: codeout = 7'b1111001;
  13. 4'd4: codeout = 7'b0110011;
  14. 4'd5: codeout = 7'b1011011;
  15. 4'd6: codeout = 7'b1011111;
  16. 4'd7: codeout = 7'b1110000;
  17. 4'd8: codeout = 7'b1111111;
  18. 4'd9: codeout = 7'b1111011;
  19. default: codeout = 7'b0000001; // 将大于9的数显示为-负号
  20. endcase
  21. end
  22. endmodule
modelsim代码:
  1. `timescale 1ns / 1ns
  2. module test_7();
  3. reg clk;
  4. reg reset_n;
  5. reg key;
  6. wire key_state;
  7. wire [3:0] Q;
  8. wire [6:0] codeout;
  9. wire CO;
  10. wire [3:0] state;
  11. wire [20:0] cnt;
  12. top fun(
  13. .clk(clk),
  14. .reset_n(reset_n),
  15. .key(key),
  16. .key_state(key_state),
  17. .Q(Q),
  18. .codeout(codeout),
  19. .CO(CO),
  20. .state(state),
  21. .cnt(cnt)
  22. );
  23. initial begin
  24. clk = 1'b0;
  25. reset_n = 1'b0;
  26. key = 1'b1;
  27. #200 reset_n = 1'b1; //200 时间单位后,将复位信号置为高电平
  28. end
  29. always begin // 无限循环块
  30. // 1ms变化一次 一共变化10ms
  31. key = 1'b1;
  32. #100000 key = 1'b0;
  33. #100000 key = 1'b1;
  34. #100000 key = 1'b0;
  35. #100000 key = 1'b1;
  36. #100000 key = 1'b0;
  37. #100000 key = 1'b1;
  38. #100000 key = 1'b0;
  39. #100000 key = 1'b1;
  40. #100000 key = 1'b0;
  41. // 保持按键信号为高电平,然后再置为低电平
  42. #(50*100000) key = 1'b1;
  43. // 继续10ms变化
  44. #100000 key = 1'b0;
  45. #100000 key = 1'b1;
  46. #100000 key = 1'b0;
  47. #100000 key = 1'b1;
  48. #100000 key = 1'b0;
  49. #100000 key = 1'b1;
  50. #100000 key = 1'b0;
  51. #100000 key = 1'b1;
  52. #100000 key = 1'b0;
  53. #100000 key = 1'b1;
  54. // 保持低电平50ms
  55. #(50*100000) key = 1'b0;
  56. end
  57. always #10 clk = ~clk; // 模拟50MHZ时钟
  58. endmodule

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/704993
推荐阅读
  

闽ICP备14008679号