当前位置:   article > 正文

【FPGA】五、蜂鸣器播放音乐_vhdl蜂鸣器播放音乐

vhdl蜂鸣器播放音乐

文章目录

前言

一、蜂鸣器简介

二、音频音符简介

三、任务要求

四、程序设计

1、设计思路

 2、程序代码   

总结


前言

        蜂鸣器(Buzzer)是现代常用的一种电子发声器,主要用于产生声音信号。它是一种一体化结构的电子讯响器,采用直流电压供电,被广泛用于计算机、报警器、电子玩具、定时器等一些列电子产品中。


一、蜂鸣器简介

        蜂鸣器按照其驱动方式不同主要分为有源蜂鸣器和无源蜂鸣器,两者的主要区别为蜂鸣器内部是否还有振荡源。一般有源蜂鸣器内部自带振荡源,通电就会发声,而无源蜂鸣器内部不含振荡源,需要外接振荡信号才能发声。

        相较于有源蜂鸣器,无源蜂鸣器的成本较低,而我们FPGA开发板上的蜂鸣器一般都是无源蜂鸣器,需要我们对其进行编程配置,我们利用不同的频率变化,控制蜂鸣器发出不同音调的声音。


二、音频音符简介

        我们是通过不同的频率去控制蜂鸣器的音调变化的,所以我们想要使蜂鸣器播放音乐,首先我们要直到不同音符所对应的频率,下面是低、中、高音下不同音符的频率对应表:

         根据上图可以计算出不同音符振动的周期,我所采用的Cyclong IV开发板上的晶振时钟为50MHZ,每一个周期就是20ns。那么每个音调的分频系数为:50 000 000 / 音调频率(可四舍五入)。


三、任务要求

        本次设计的要求就是利用FPGA开发板上的无源蜂鸣器,通过不同振动频率播放歌曲两只老虎。

 


四、程序设计

1、设计思路

        ① 首先我们需要计算出每个不同的音符对应的分频系数,由此产生不同的音调,我们需要设计一个分频计数器,来计数当前发出音调的分频系数。

        ② 然后我们需要一个节拍计数器,我们定义为半拍300ms,一拍500ms。

        ③ 利用组合逻辑case语句进行设计,将需要的节拍和要发出的音调写入case语句中。

 2、程序代码   

  1. /*========================================*
  2. filename : beep_music.v
  3. description : 无源蜂鸣器播放音乐实验
  4. time : 2022-11-010
  5. author : 卡夫卡与海
  6. *========================================*/
  7. module beep_music(
  8. input clk ,//系统时钟 50MHZ
  9. input rst_n ,//系统复位
  10. output reg beep //蜂鸣器控制信号
  11. );
  12. //参数定义
  13. //每个音符震动一次所占用的时钟周期
  14. //低音
  15. parameter MIN_DO = 18'd190800,//(50_000_000/262)
  16. MIN_RE = 18'd170050,//(50_000_000/294)
  17. MIN_MI = 18'd151500,//(50_000_000/330)
  18. MIN_FA = 18'd143250,//(50_000_000/349)
  19. MIN_SO = 18'd127550,//(50_000_000/392)
  20. MIN_LA = 18'd113600,//(50_000_000/440)
  21. MIN_XI = 18'd101200;//(50_000_000/494)
  22. //中音
  23. parameter MID_DO = 17'd95600,//(50_000_000/523)
  24. MID_RE = 17'd85150,//(50_000_000/587)
  25. MID_MI = 17'd75850,//(50_000_000/659)
  26. MID_FA = 17'd71600,//(50_000_000/698)
  27. MID_SO = 17'd63750,//(50_000_000/784)
  28. MID_LA = 17'd56800,//(50_000_000/880)
  29. MID_XI = 17'd50600;//(50_000_000/988)
  30. //高音
  31. parameter MAX_DO = 16'd47755,//(50_000_000/1047)
  32. MAX_RE = 16'd42553,//(50_000_000/1175)
  33. MAX_MI = 16'd37907,//(50_000_000/1319)
  34. MAX_FA = 16'd35790,//(50_000_000/1397)
  35. MAX_SO = 16'd31887,//(50_000_000/1568)
  36. MAX_LA = 16'd28409,//(50_000_000/1760)
  37. MAX_XI = 16'd25419;//(50_000_000/1967)
  38. parameter TIME_300ms = 24'd14_999_999,//300ms,半拍
  39. TIME_500ms = 25'd24_999_999;//500ms,一拍
  40. parameter NOTE_NUM = 6'd33;//音符个数 34个
  41. //信号定义
  42. reg [24:0] cnt_delay ;//300ms或500ms计数器
  43. reg [5:0] cnt_note ;//音符计数器
  44. reg [18:0] cnt_freq ;//音符播放计数器
  45. reg [18:0] freq_data ;//音符数据寄存器
  46. wire [17:0] duty_data ;//占空比
  47. wire end_note ;//单个音符播放结束标志
  48. wire end_flag ;//所有音符结束标志
  49. reg [24:0] cnt_delay_r ;
  50. reg flag ;//蜂鸣器输出标志
  51. //300ms计数器 cnt_delay
  52. always @(posedge clk or negedge rst_n)begin
  53. if(!rst_n)begin
  54. cnt_delay <= 25'd0;
  55. end
  56. else if(cnt_delay == cnt_delay_r)begin
  57. cnt_delay <= 25'd0;
  58. end
  59. else begin
  60. cnt_delay <= cnt_delay + 1'b1;
  61. end
  62. end
  63. //音符计数器 cnt_note
  64. always @(posedge clk or negedge rst_n)begin
  65. if(!rst_n)begin
  66. cnt_note <= 6'd0;
  67. end
  68. else if(end_flag)begin
  69. cnt_note <= 6'd0;
  70. end
  71. else if(cnt_delay == cnt_delay_r)begin
  72. cnt_note <= cnt_note + 1'b1;
  73. end
  74. else begin
  75. cnt_note <= cnt_note;
  76. end
  77. end
  78. //所有音符结束标志 end_flag
  79. assign end_flag = cnt_note == NOTE_NUM && cnt_delay == cnt_delay_r;
  80. //单个音符振动周期 cnt_freq
  81. always @(posedge clk or negedge rst_n)begin
  82. if(!rst_n)begin
  83. cnt_freq <= 19'd1;
  84. end
  85. else if(end_note)begin
  86. cnt_freq <= 19'd1;
  87. end
  88. else begin
  89. cnt_freq <= cnt_freq + 1'b1;
  90. end
  91. end
  92. //单个音符结束标志 end_note
  93. assign end_note = (cnt_freq == freq_data);
  94. //音符数据选择 freq_data
  95. always @(posedge clk or negedge rst_n)begin
  96. if(!rst_n)begin
  97. freq_data <= MAX_DO;
  98. end
  99. else begin
  100. case(cnt_note)
  101. 6'd0:begin
  102. freq_data <= MID_DO;
  103. cnt_delay_r <= TIME_300ms;
  104. end
  105. 6'd1:begin
  106. freq_data <= MID_RE;
  107. cnt_delay_r <= TIME_300ms;
  108. end
  109. 6'd2:begin
  110. freq_data <= MID_MI;
  111. cnt_delay_r <= TIME_300ms;
  112. end
  113. 6'd3:begin
  114. freq_data <= MID_DO;
  115. cnt_delay_r <= TIME_300ms;
  116. end
  117. 6'd4:begin
  118. freq_data <= MID_DO;
  119. cnt_delay_r <= TIME_300ms;
  120. end
  121. 6'd5:begin
  122. freq_data <= MID_RE;
  123. cnt_delay_r <= TIME_300ms;
  124. end
  125. 6'd6:begin
  126. freq_data <= MID_MI;
  127. cnt_delay_r <= TIME_300ms;
  128. end
  129. 6'd7:begin
  130. freq_data <= MID_DO;
  131. cnt_delay_r <= TIME_300ms;
  132. end
  133. 6'd8:begin
  134. freq_data <= MID_MI;
  135. cnt_delay_r <= TIME_300ms;
  136. end
  137. 6'd9:begin
  138. freq_data <= MID_FA;
  139. cnt_delay_r <= TIME_300ms;
  140. end
  141. 6'd10:begin
  142. freq_data <= MID_SO;
  143. cnt_delay_r <= TIME_500ms;
  144. end
  145. 6'd11:begin
  146. freq_data <= MID_MI;
  147. cnt_delay_r <= TIME_300ms;
  148. end
  149. 6'd12:begin
  150. freq_data <= MID_FA;
  151. cnt_delay_r <= TIME_300ms;
  152. end
  153. 6'd13:begin
  154. freq_data <= MID_SO;
  155. cnt_delay_r <= TIME_500ms;
  156. end
  157. 6'd14:begin
  158. freq_data <= MID_SO;
  159. cnt_delay_r <= TIME_300ms;
  160. end
  161. 6'd15:begin
  162. freq_data <= MID_LA;
  163. cnt_delay_r <= TIME_300ms;
  164. end
  165. 6'd16:begin
  166. freq_data <= MID_SO;
  167. cnt_delay_r <= TIME_300ms;
  168. end
  169. 6'd17:begin
  170. freq_data <= MID_FA;
  171. cnt_delay_r <= TIME_500ms;
  172. end
  173. 6'd18:begin
  174. freq_data <= MID_MI;
  175. cnt_delay_r <= TIME_500ms;
  176. end
  177. 6'd19:begin
  178. freq_data <= MID_DO;
  179. cnt_delay_r <= TIME_300ms;
  180. end
  181. 6'd20:begin
  182. freq_data <= MID_SO;
  183. cnt_delay_r <= TIME_300ms;
  184. end
  185. 6'd21:begin
  186. freq_data <= MID_LA;
  187. cnt_delay_r <= TIME_300ms;
  188. end
  189. 6'd22:begin
  190. freq_data <= MID_SO;
  191. cnt_delay_r <= TIME_300ms;
  192. end
  193. 6'd23:begin
  194. freq_data <= MID_FA;
  195. cnt_delay_r <= TIME_500ms;
  196. end
  197. 6'd24:begin
  198. freq_data <= MID_MI;
  199. cnt_delay_r <= TIME_500ms;
  200. end
  201. 6'd25:begin
  202. freq_data <= MID_DO;
  203. cnt_delay_r <= TIME_300ms;
  204. end
  205. 6'd26:begin
  206. freq_data <= MID_RE;
  207. cnt_delay_r <= TIME_500ms;
  208. end
  209. 6'd27:begin
  210. freq_data <= MID_SO;
  211. cnt_delay_r <= TIME_500ms;
  212. end
  213. 6'd28:begin
  214. freq_data <= MID_DO;
  215. cnt_delay_r <= TIME_500ms;
  216. end
  217. 6'd29:begin
  218. freq_data <= 1'b0;
  219. cnt_delay_r <= TIME_500ms;
  220. end
  221. 6'd30:begin
  222. freq_data <= MID_RE;
  223. cnt_delay_r <= TIME_500ms;
  224. end
  225. 6'd31:begin
  226. freq_data <= MID_SO;
  227. cnt_delay_r <= TIME_500ms;
  228. end
  229. 6'd32:begin
  230. freq_data <= MID_DO;
  231. cnt_delay_r <= TIME_500ms;
  232. end
  233. 6'd33:begin
  234. freq_data <= 1'b0;
  235. cnt_delay_r <= TIME_500ms;
  236. end
  237. default:begin
  238. freq_data <= MID_DO;
  239. cnt_delay_r <= TIME_300ms;
  240. end
  241. endcase
  242. end
  243. end
  244. //占空比 duty_data
  245. assign duty_data = freq_data >> 3;//移位越多,占空比越高
  246. // flag
  247. always @(posedge clk or negedge rst_n)begin
  248. if(!rst_n)begin
  249. flag <= 1'b0;
  250. end
  251. else begin
  252. flag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;
  253. end
  254. end
  255. //输出 beep
  256. always @(posedge clk or negedge rst_n)begin
  257. if(!rst_n)begin
  258. beep <= 1'b0;
  259. end
  260. else if(flag)begin
  261. beep <= 1'b1;
  262. end
  263. else begin
  264. beep <= 1'b0;
  265. end
  266. end
  267. endmodule

总结

        这个原理还是挺简单的,如果感兴趣的话还可以尝试这去写更复杂的音乐,但是这个蜂鸣器的声音真正不是很友好,如果能加一个音频转换器效果应该会好很多。

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

闽ICP备14008679号

        
cppcmd=keepalive&