当前位置:   article > 正文

FPGA拾忆_(10):按键控制蜂鸣器_边沿检测_按键消抖_按键式蜂鸣器 verilog

按键式蜂鸣器 verilog
1.硬件特征:

轻触式(回弹式)按键

        略

蜂鸣器:

分为蜂鸣器按照结构原理不同可分为压电式蜂鸣器和电磁式蜂鸣器。 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、 阻抗匹配器及共鸣箱、外壳等组成; 电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。

其他资料:1
 

发声原理:压电式蜂鸣器是利用压电效应原理工作的,当对其施加交变电压时它会产生机械振动发声; 电磁式蜂鸣器是接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场, 振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
压电式蜂鸣器和电磁式蜂鸣器由于发音原理不同, 产生的声音信号也不一样。 压电式结构简单耐用但音调单一,适用于报警器等设备; 而电磁式由于音质好,所以多用于语音、音乐等设备。 本次实验使用的蜂鸣器为电磁式蜂鸣器。
蜂鸣器按照驱动方式不同又可分为有源蜂鸣器和无源蜂鸣器,其主要区别为蜂鸣器内部是否含有震荡源。一般的有源蜂鸣器内部自带了震荡源,只要通电就会发声。而无源蜂鸣器由于不含内部震荡源,需要外接震荡信号才能发声

2.原理图:
3.端口以及模块结构:

其中,key_debounce是将输入的key信号消抖后输出,这个模块的输出信号是key_filter,key_beep则是简单的控制逻辑,检测到有下降沿则判断为一次按键按下,beep信号翻转,最后是顶层模块将两个模块例化到一块,顶层模块中连接的信号key_filter为wire型,这样的结构清晰且富有条例,而我自己写的模块的结构应该如下面这个一样的 

关键模块:

消抖处理模块:电平跳变就进行计数,计数时间超过20ms说明此信号已经平稳,抖动信号已经过滤掉,将key信号直接赋值给key_filter即可。(我写的较为麻烦)

边沿检测模块:key信号是异步信号,所以要有个同步打拍子过程,然后进行边沿检测,

buf_res != buffer2 ,就说明有边沿出现。

4.代码技巧: 

人家的代码和我的代码对比:

  1. module top_key_beep(
  2. input sys_clk , //系统时钟
  3. input sys_rst_n , //系统复位,低电平有效
  4. input key , //按键
  5. output beep //蜂鸣器
  6. );
  7. //parameter define
  8. parameter CNT_MAX = 20'd100_0000; //消抖时间20ms
  9. //wire define
  10. wire key_filter ; //按键消抖后的值
  11. //*****************************************************
  12. //** main code
  13. //*****************************************************
  14. //例化按键消抖模块
  15. key_debounce #(
  16. .CNT_MAX (CNT_MAX)
  17. )u_key_debounce(
  18. .sys_clk (sys_clk),
  19. .sys_rst_n (sys_rst_n),
  20. .key (key),
  21. .key_filter (key_filter)
  22. );
  23. //例化按键控制蜂鸣器模块
  24. key_beep u_key_beep(
  25. .sys_clk (sys_clk),
  26. .sys_rst_n (sys_rst_n),
  27. .key_filter (key_filter),
  28. .beep (beep)
  29. );
  30. endmodule
  31. /
  32. 按键消抖模块
  33. module key_debounce(
  34. input sys_clk ,
  35. input sys_rst_n ,
  36. input key , //外部输入的按键值
  37. output reg key_filter //按键消抖后的值
  38. );
  39. //parameter define
  40. parameter CNT_MAX = 20'd100_0000; //消抖时间20ms
  41. //reg define
  42. reg [19:0] cnt ;
  43. reg key_d0; //将按键信号延迟一个时钟周期
  44. reg key_d1; //将按键信号延迟两个时钟周期
  45. //*****************************************************
  46. //** main code
  47. //*****************************************************
  48. //对按键端口的数据延迟两个时钟周期
  49. always @ (posedge sys_clk or negedge sys_rst_n) begin
  50. if(!sys_rst_n) begin
  51. key_d0 <= 1'b1;
  52. key_d1 <= 1'b1;
  53. end
  54. else begin
  55. key_d0 <= key;
  56. key_d1 <= key_d0;
  57. end
  58. end
  59. //按键值消抖
  60. always @ (posedge sys_clk or negedge sys_rst_n) begin
  61. if(!sys_rst_n)
  62. cnt <= 20'd0;
  63. else begin
  64. if(key_d1 != key_d0) //检测到按键状态发生变化
  65. cnt <= CNT_MAX; //则将计数器置为20'd100_0000,
  66. //即延时100_0000 * 20ns(1s/50MHz) = 20ms
  67. else begin //如果当前按键值和前一个按键值一样,即按键没有发生变化
  68. if(cnt > 20'd0) //则计数器递减到0
  69. cnt <= cnt - 1'b1;
  70. else
  71. cnt <= 20'd0;
  72. end
  73. end
  74. end
  75. //将消抖后的最终的按键值送出去
  76. always @ (posedge sys_clk or negedge sys_rst_n) begin
  77. if(!sys_rst_n)
  78. key_filter <= 1'b1;
  79. //在计数器递减到1时送出按键值
  80. else if(cnt == 20'd1)
  81. key_filter <= key_d1;
  82. else
  83. key_filter <= key_filter;
  84. end
  85. endmodule
  86. 按键控制逻辑
  87. module key_beep(
  88. input sys_clk,
  89. input sys_rst_n,
  90. input key_filter, //消抖后按键值
  91. output reg beep //蜂鸣器
  92. );
  93. //reg define
  94. reg key_filter_d0; //将消抖后的按键值延迟一个时钟周期
  95. //wire define
  96. wire neg_key_filter; //按键有效脉信号
  97. //*****************************************************
  98. //** main code
  99. //*****************************************************
  100. //捕获按键端口的下降沿,得到一个时钟周期的脉冲信号
  101. assign neg_key_filter = (~key_filter) & key_filter_d0;
  102. //对按键端口的数据延迟一个时钟周期
  103. always @ (posedge sys_clk or negedge sys_rst_n) begin
  104. if(!sys_rst_n)
  105. key_filter_d0 <= 1'b1;
  106. else
  107. key_filter_d0 <= key_filter;
  108. end
  109. //每次按键按下时,就翻转蜂鸣器的状态
  110. always @ (posedge sys_clk or negedge sys_rst_n) begin
  111. if(!sys_rst_n)
  112. beep <= 1'b1;
  113. else if(neg_key_filter) //有效的一次按键被按下
  114. beep <= ~beep;
  115. else
  116. beep <= beep;
  117. end
  118. endmodule

我的代码:

  1. module key_beep(
  2. input sys_clk,
  3. input sys_rst_n,
  4. input key,
  5. output reg beep
  6. );
  7. wire key_filter;
  8. reg buffer2, beep_flag;
  9. //例化按键消抖模块
  10. key_filter
  11. key_filter_1(
  12. .key(key),
  13. .sys_clk(sys_clk),
  14. .sys_rst_n(sys_rst_n),
  15. .key_filter(key_filter)
  16. );
  17. //边沿检测电路
  18. always @(posedge sys_clk or negedge sys_rst_n) begin
  19. if(!sys_rst_n)
  20. buffer2 <= 1'b0;
  21. else
  22. buffer2 <= key_filter;
  23. end
  24. always@(posedge sys_clk or negedge sys_rst_n)begin
  25. if(!sys_rst_n)
  26. beep_flag <= 1'b0;
  27. else if((key_filter == 1'b1)&&(buffer2 == 1'b0))
  28. beep_flag <= 1'b1; //一个脉冲的按键标志信号
  29. else
  30. beep_flag <= 1'b0;
  31. end
  32. always @(posedge sys_clk or negedge sys_rst_n) begin
  33. if(!sys_rst_n)
  34. beep <= 1'b0;
  35. else if(beep_flag == 1'b1) //检测到一次按键
  36. beep <= ~beep;
  37. else
  38. beep <= beep; //保持
  39. end
  40. endmodule
  41. 按键消抖
  42. module key_filter (
  43. input key,
  44. input sys_clk,
  45. input sys_rst_n,
  46. output reg key_filter
  47. );
  48. parameter CNT_MAX = 20'd1_000_000; //20ms
  49. reg [19:0] cnt;
  50. reg buffer1,buf_res;
  51. reg pose_flag,nege_flag;
  52. //ĺć­Ľććĺ­?
  53. always@(posedge sys_clk or negedge sys_rst_n)begin
  54. if(!sys_rst_n)
  55. buffer1 <= 1'b0;
  56. else
  57. buffer1 <= key;
  58. end
  59. always@(posedge sys_clk or negedge sys_rst_n)begin
  60. if(!sys_rst_n)
  61. buf_res <= 1'b0;
  62. else
  63. buf_res <= buffer1;
  64. end
  65. //ä¸čžšć˛żćŁćľçľčˇ?
  66. always@(posedge sys_clk or negedge sys_rst_n)begin
  67. if(!sys_rst_n)begin
  68. pose_flag <= 1'b0;
  69. nege_flag <= 1'b0;
  70. end
  71. else if((buf_res == 1'b1)&&(buffer1 == 1'b0))begin
  72. pose_flag <= 1'b0;
  73. nege_flag <= 1'b1;
  74. end
  75. else if((buf_res == 1'b0)&&(buffer1 == 1'b1))begin
  76. pose_flag <= 1'b1;
  77. nege_flag <= 1'b0;
  78. end
  79. else
  80. begin
  81. pose_flag <= pose_flag;
  82. nege_flag <= nege_flag;
  83. end
  84. end
  85. //ćŁ?ćľĺ°čžšć˛żĺ°ąĺźĺ§čŽĄć?
  86. always@(posedge sys_clk or negedge sys_rst_n)begin
  87. if(!sys_rst_n)
  88. cnt <= 10'd0;
  89. //ćŁ?ćľĺ°čžšć˛żďźĺźĺ§ć¸éśčŽĄć?
  90. else if(((buf_res == 1'b1)&&(buffer1 == 1'b0))||((buf_res == 1'b0)&&(buffer1 == 1'b1)))
  91. cnt <= 10'd0;
  92. //else if((pose_flag == 1'b1)||(nege_flag == 1'b1)) //ĺŞćć螚沿觌ĺďźĺ°ąĺźĺ§čŽĄć°ďźčŽĄć°čśčż20msďźćĺłçćŻä¸ćŹĄćé?
  93. else if(cnt == CNT_MAX - 1'b1)
  94. cnt <= 10'd0;
  95. else
  96. cnt <= cnt + 1'd1;
  97. end
  98. //ĺ¤ć­čŽĄć°ćśé´ćŻĺŚč˝čžžĺ?20ms
  99. always@(posedge sys_clk or negedge sys_rst_n)begin
  100. if(!sys_rst_n)
  101. key_filter <= 1'b0;
  102. else if((cnt == CNT_MAX - 1'b1)&&(nege_flag == 1'b1)) //莥ć°čžžĺ°1000ďźĺł20ms
  103. key_filter <= 1'b1; //ćä¸ćŹĄćéŽćä¸äş
  104. else if((cnt == CNT_MAX - 1'b1)&&(pose_flag == 1'b1))
  105. key_filter <= 1'b0;//ćéŽĺˇ˛çťćžĺźäş?
  106. else
  107. key_filter <= key_filter;
  108. end
  109. endmodule
5.仿真与验证:

功能验证正确,但是我引入了较多的中间变量,而且输出的key_filter是与key信号消抖后的信号相反的,不过功能都验证正确了,也说明实现功能的方法可以有很多。

6.总结与易错点:

(1)几个重要且常用的模块:

        按键消抖模块,边沿检测模块,同步打拍子操作(解决亚稳态问题)

(2)加强对于自锁与非自锁概念的理解:

        自锁:一直输出有效电平。

        非自锁:仅输出一个周期的有效电平,需要自己对有效电平进行采集和保持。

(3)这次写testbench代码时遇到了一个从没遇到的问题:

        参数重定义,我的CNT_MAX是在key_filter模块定义的,然后key_beep例化了此模块,然后我在这个key_beep模块加入parameter语句or采用参数传递的模式(#(.CNT_MAX(CNT_MAX)))都不行,编译不报错,但是仿真的时候,也就是例化key_beep模块的时候,一直给我提示了CNT_MAX 不能被overwrite这个错误,但是正点原子的代码也参数重定义了,他的没有错误,我暂时还不知道问题出在哪,待定,等那天知道答案了来补充吧。贴一个将参数传递的文字:

verilog中参数传递与参数定义

待补充.....

(4)$stop(n)  和  $finish(n)的区别:可以参考一下

Verilog中$finish、$stop的使用与区别

over

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

闽ICP备14008679号