当前位置:   article > 正文

FPGA开发——蜂鸣器实现音乐播放器的设计

FPGA开发——蜂鸣器实现音乐播放器的设计

一、概述

我们在进行蜂鸣器的学习的时候,总会在想既然蜂鸣器能够发出声音,那么它能够播放音乐吗,今天这篇我们文章我们就一起来学习怎样使用使用蜂鸣器来播放音乐,也就是怎样成为一个音乐播放器。

1、蜂鸣器的类型

在设计的时候其实蜂鸣器的类型也对我们最后所实现的效果有一点影响,蜂鸣器分为有源和无源两种,有源蜂鸣器的器件内部本身就带有振荡器,本身来讲完全不用我们进行外部振荡,二无源蜂鸣器是不带振荡器的,所以当涉及到相关频率使用时就需要我们使用外部振荡。这里因为我们使用PWM进行设计实现,所以如果有条件的话使用无源蜂鸣器所实现的效果会更好。

2、PWM介绍

PWM(Pulse Width Modulation),即脉冲宽度调制,是一种模拟信号电平数字编码方法。它通过将有效的电信号分散成离散形式,从而来降低电信号所传递的平均功率。PWM技术的核心在于通过调节脉冲的时间宽度(占空比)来等效地获得所需要合成的相应幅值和频率的波形。通过调整PWM信号的占空比和频率,可以实现声音的音量调节和音调变化。

  • 占空比:占空比是脉冲处于较高电压的时间占整个脉冲周期的百分比。通过改变占空比,可以实现对信号、能量等的调节。例如,在LED电路中,高占空比意味着LED非常明亮,而低占空比则意味着LED较为暗淡。
  • 频率:频率是单位时间内脉冲信号的次数。PWM信号的频率越高,控制效果越接近模拟信号,但也会受到电路响应时长的限制。

3、PWM调制的实现方式

硬件实现:许多微控制器和数字信号处理器(DSP)已包括了PWM控制器芯片,因此可以更轻松地实施数字化控制。这些芯片通过内部定时器或计数器来生成PWM信号,并通过调整相关参数来改变占空比和频率。
软件实现:在没有内置PWM控制器的系统中,也可以通过软件编程来模拟PWM信号。这通常涉及到对定时器中断的精确控制,以及在高电平和低电平之间快速切换IO口的输出状态。

4、音符的相关设计

这是关于高中低音符的频率和周期表,我们将会参考这个对代码进行设计。

二、工程实现

1、本次播放的音乐

本次我们设计得比较简单,设计了一个《我有一头小毛驴》的简单音乐播放器,具体乐谱如下:

2、基本构建思路

在本次的设计中,采用了一个音符播放播放0.5秒的设计,针对上述乐谱,采用四个字符为一组的方式进行划分,便于代码的赋值。利用三个计数器完成对于音乐播放的相关设计,第一个播放器用于技术每个音符技术的相关频率,第二个计数器用于对于在0.5秒播放时间内对于每个音符重复的次数进行计数,而第三个计数器用于对音符的播放顺序进行一个计数,由此关于音符的设置就设计完成,最后就是对于蜂鸣器进行占空比调制,对每个音符的频率进行一个8分频就完成了最终功能。

3、设计文件的编写

这里新建一个beep.v文件,如下:

  1. //蜂鸣器
  2. module beep(
  3. input clk,
  4. input rst_n,
  5. output reg beep_out
  6. );
  7. parameter CLK_CLY=50_000_000;//时钟频率
  8. localparam H1 =CLK_CLY/523,
  9. H1_L=CLK_CLY/262,
  10. H1_H=CLK_CLY/1047,
  11. H2 =CLK_CLY/587,
  12. H3 =CLK_CLY/659,
  13. H4 =CLK_CLY/698,
  14. H5 =CLK_CLY/784,
  15. H6 =CLK_CLY/880,
  16. H7 =CLK_CLY/988;
  17. parameter TIME_500MS=25_000_000;//0.5秒
  18. reg [17:0] cnt0;// 每个音符频率计数器
  19. wire add_cnt0;
  20. wire end_cnt0;
  21. reg [9:0] cnt1;// 0.5秒内音符周期重复个数
  22. wire add_cnt1;
  23. wire end_cnt1;
  24. reg [5:0] cnt2;// 计数音符播放顺序
  25. wire add_cnt2;
  26. wire end_cnt2;
  27. reg [15:0] cnt_max;
  28. reg [17:0] display;//音符计数器的最大值
  29. //cnt0计数
  30. always @(posedge clk or negedge rst_n)begin
  31. if(!rst_n)
  32. cnt0<=0;
  33. else if(add_cnt0)begin
  34. if(end_cnt0)
  35. cnt0<=0;
  36. else
  37. cnt0<=cnt0+1'b1;
  38. end
  39. end
  40. assign add_cnt0=1'b1;
  41. assign end_cnt0=add_cnt0 &&(cnt0==display-1);
  42. //cnt1计数
  43. always @(posedge clk or negedge rst_n)begin
  44. if(!rst_n)
  45. cnt1<=0;
  46. else if(add_cnt1)begin
  47. if(end_cnt1)
  48. cnt1<=0;
  49. else
  50. cnt1<=cnt1+1'b1;
  51. end
  52. end
  53. assign add_cnt1=end_cnt0;
  54. assign end_cnt1=add_cnt1 &&(cnt1==(TIME_500MS/display-1));
  55. //cnt2计数
  56. always @(posedge clk or negedge rst_n)begin
  57. if(!rst_n)
  58. cnt2<=0;
  59. else if(add_cnt2)begin
  60. if(end_cnt2)
  61. cnt2<=0;
  62. else
  63. cnt2<=cnt2+1'b1;
  64. end
  65. end
  66. assign add_cnt2=end_cnt1;
  67. assign end_cnt2=add_cnt2 &&(cnt2==50-1);
  68. always @(posedge clk or negedge rst_n)begin
  69. if(!rst_n)
  70. display <=H1;
  71. else begin
  72. case (cnt2)
  73. 0:display <=H1_L;
  74. 1:display <=H1;
  75. 2:display <=H1;
  76. 3:display <=H3;
  77. 4:display <=H5;
  78. 5:display <=H5;
  79. 6:display <=H5;
  80. 7:display <=H5;
  81. 8:display <=H6;
  82. 9:display <=H6;
  83. 10:display <=H6;
  84. 11:display <=H1_H;
  85. 12:display <=H5;
  86. 13:display <=H5;
  87. 14:display <=H5;
  88. 15:display <=H5;
  89. 16:display <=H4;
  90. 17:display <=H4;
  91. 18:display <=H4;
  92. 19:display <=H6;
  93. 20:display <=H3;
  94. 21:display <=H3;
  95. 22:display <=H3;
  96. 23:display <=H3;
  97. 24:display <=H2;
  98. 25:display <=H2;
  99. 26:display <=H2;
  100. 27:display <=H2;
  101. 28:display <=H5;
  102. 29:display <=H5;
  103. 30:display <=H5;
  104. 31:display <=H5;
  105. 32:display <=H1;
  106. 33:display <=H1_L;
  107. 34:display <=H1;
  108. 35:display <=H3;
  109. 36:display <=H5;
  110. 37:display <=H5;
  111. 38:display <=H5;
  112. 39:display <=H5;
  113. 40:display <=H6;
  114. 41:display <=H6;
  115. 42:display <=H6;
  116. 43:display <=H1_H;
  117. 44:display <=H5;
  118. 45:display <=H5;
  119. 46:display <=H5;
  120. 47:display <=H5;
  121. 40:display <=H4;
  122. 41:display <=H4;
  123. 42:display <=H4;
  124. 43:display <=H6;
  125. 44:display <=H3;
  126. 45:display <=H3;
  127. 46:display <=H3;
  128. 47:display <=H3;
  129. 40:display <=H3;
  130. 41:display <=H3;
  131. 42:display <=H2;
  132. 43:display <=H2;
  133. 44:display <=H2;
  134. 45:display <=H3;
  135. 46:display <=H1;
  136. 47:display <=H1;
  137. 48:display <=H1;
  138. 49:display <=H1;
  139. default: display <=H1;
  140. endcase
  141. end
  142. end
  143. //pwm输出
  144. //蜂鸣器pwm
  145. always @(posedge clk or negedge rst_n) begin
  146. if(!rst_n)begin
  147. beep_out <= 1'b1;
  148. end
  149. else if(cnt0 == (display>>3))begin//设置占空比
  150. beep_out <= 1'b1;
  151. end
  152. else if(cnt0==0)begin
  153. beep_out<= 1'b0;
  154. end
  155. end
  156. endmodule

4、测试文件的编写

新建beep_tb.v文件,这里出来蜂鸣器输出没有任何激励,所以我们只需要进行时钟和复位进行赋值就行。如下:

  1. //定义时间尺度
  2. `timescale 1ns/1ns
  3. module beep_tb ;
  4. //输入信号定义
  5. reg clk ;
  6. reg rst_n ;
  7. wire beep_out ;
  8. //模块例化
  9. beep beep_inst(
  10. /*input */ .clk (clk ) ,
  11. /*input */ .rst_n (rst_n ) ,
  12. /*output */ .beep_out (beep_out )
  13. );
  14. //激励信号产生
  15. parameter CLK_CYC = 20;
  16. //时钟
  17. initial clk=1;
  18. always #(CLK_CYC/2)clk=~clk;
  19. //复位
  20. initial begin
  21. rst_n= 1'b0;
  22. #(CLK_CYC*2);
  23. #3;//复位结束避开时钟上升沿
  24. rst_n= 1'b1;
  25. end
  26. endmodule

5、仿真波形图

通过对于波形图的观察,我们可以看到在三个计数器的不断计数之下蜂鸣器的输出波形占空比在不断进行变化 ,并且在经过对于计数器进行检查后没有发现什么问题。(这里方针波形图太长,没有截全),感兴趣的可以去自行进行仿真观察。最后进行下板验证之后,蜂鸣器也进行了正常播放,我们的设计成功。

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

闽ICP备14008679号