文章目录

  • 前言
  • 一、蜂鸣器
    • 1.蜂鸣器简介:
    • 2.有源蜂鸣器:
    • 3.无源蜂鸣器:
  • 二、简谱常识
    • 1.音符时值:
    • 2.简谱名:
    • 3.简谱名频率:
  • 三、程序设计
    • 1.调用ROM IP核储存每个简谱名所播放的时间:
    • 2.编写频率计数值选用代码及用ROM IP核储存对应选择值:
    • 3.PWM产生:
    • 4.ROM存储器地址改变:
  • 四、整体代码:
  • 五、遇到的问题:
  • 总结

前言

蜂鸣器是我们常用的电子元器件,本文使用无源蜂鸣器进行音乐《花海》的播放


一、蜂鸣器

1.蜂鸣器简介:

蜂鸣器按其是否带有信号源分为有源蜂鸣器和无源蜂鸣器,有源蜂鸣器内部装有集成电路,不需要音频驱动电路,只需要接通直流电源就可以直接发出声响,而无源蜂鸣器只有外加音频驱动才能发出响声。

2.有源蜂鸣器:

内部自带震荡源,只需要加上适当的直流电源即可发生,程序控制比较简单,通常使用于报警、提示等,但因为声音是固定的,所以无法实现音乐播放。

3.无源蜂鸣器:

相比于有源蜂鸣器,无源蜂鸣器成本更低,声音频率可控,需要输入PWM方波才能驱动其发声,通过改变PWM波的频率,可以实现不同音调的改变;通过改变PWM波的占空比,可以实现声音大小的改变,所以我们只需要产生不同频率和占空比的PWM方波去驱动无源蜂鸣器就能让无源蜂鸣器发出不同的音调了。

二、简谱常识

作为一个资深的只会唱歌,不会看谱子的音乐小白痴,对简谱的理解我是废了九牛二虎之力,问了好几个人,才明白具体原理:

1.音符时值:

4/4拍是指按四分音符为一拍,每小节有四拍,我们按照一拍时间为一秒计算,半拍就是1/2秒,四分之一拍就是1/4秒,如果音符后面加一个圆点,时值延长本身时长的二分之一。

2.简谱名:

中音就是我们常见的Do,Re,Mi,Fa,Sol,La,Si,在下面加一点表示低音,加两点表示超低音,在上面加一点表示高音,加两点表示超高音,本实验中只编码了低、中、高、超高音(因为我找到了这些音的现成频率计数值,所以就没编码超低音,想要更完整的可以自己去搜索一下)

3.简谱名频率:

每个音调的高低,是因为声音频率的不同,以下是各简谱名对应的频率,在播放某一个简谱名时,只需要转换出相应的频率给蜂鸣器就可以了。

三、程序设计

1.调用ROM IP核储存每个简谱名所播放的时间:

我们把一秒分为8份,这样一拍就是8✖一个数,这个数是八分之一秒的计数值,这样我们就得到了每一个简谱名对应的拍数对应的计数值:
time_cycle<=time_music(CLK_FRE1000000/8)**
我们在ROM里面储存的数据即为每个简谱名对应的拍数。
ROM选用单口,位宽8位,深度256即可(可根据储存的简谱长度来做适当调整)。这里给出《花海》的一些简谱时间

可以看到,我们储存了112(0~111)个简谱名,对应的我们在调用ROM的时候需要注意address即地址的长度,一定要大于111,否则就可能出现歌没唱完就结束了的情况。

2.编写频率计数值选用代码及用ROM IP核储存对应选择值:

频率计数值选用代码:

module hz(input wire [7:0]hz_sel,output reg [19:0]cycle
);parameter CLK_FRE = 50 ;always @(*)begincase(hz_sel)8'h01 : cycle <= CLK_FRE*1000000/261 ; //low 1 261Hz8'h02 : cycle <= CLK_FRE*1000000/293 ; //low 2 293Hz8'h03 : cycle <= CLK_FRE*1000000/329 ; //low 3 329Hz8'h04 : cycle <= CLK_FRE*1000000/349 ; //low 4 349Hz8'h05 : cycle <= CLK_FRE*1000000/392 ; //low 5 392Hz8'h06 : cycle <= CLK_FRE*1000000/440 ; //low 6 440Hz8'h07 : cycle <= CLK_FRE*1000000/499 ; //low 7 499Hz8'h11 : cycle <= CLK_FRE*1000000/523 ; //middle 1 523Hz8'h12 : cycle <= CLK_FRE*1000000/587 ; //middle 2 587Hz8'h13 : cycle <= CLK_FRE*1000000/659 ; //middle 3 659Hz8'h14 : cycle <= CLK_FRE*1000000/698 ; //middle 4 698Hz8'h15 : cycle <= CLK_FRE*1000000/784 ; //middle 5 784Hz8'h16 : cycle <= CLK_FRE*1000000/880 ; //middle 6 880Hz8'h17 : cycle <= CLK_FRE*1000000/998 ; //middle 7 998Hz8'h21 : cycle <= CLK_FRE*1000000/1046 ; //high 1 1046Hz8'h22 : cycle <= CLK_FRE*1000000/1174 ; //high 2 1174Hz8'h23 : cycle <= CLK_FRE*1000000/1318 ; //high 3 1318Hz8'h24 : cycle <= CLK_FRE*1000000/1396 ; //high 4 1396Hz8'h25 : cycle <= CLK_FRE*1000000/1568 ; //high 5 1568Hz8'h26 : cycle <= CLK_FRE*1000000/1760 ; //high 6 1760Hz8'h27 : cycle <= CLK_FRE*1000000/1976 ; //high 7 1976Hz8'h31 : cycle <= CLK_FRE*1000000/2093 ; //super high 1 2093Hz8'h32 : cycle <= CLK_FRE*1000000/2349 ; //super high 2 2349Hz8'h33 : cycle <= CLK_FRE*1000000/2637 ; //super high 3 2637Hz8'h34 : cycle <= CLK_FRE*1000000/2794 ; //super high 4 2794Hz8'h35 : cycle <= CLK_FRE*1000000/3136 ; //super high 5 3136Hz8'h36 : cycle <= CLK_FRE*1000000/3520 ; //super high 6 3520Hz8'h37 : cycle <= CLK_FRE*1000000/3951 ; //super high 7 3951Hzdefault:cycle<=20'd0;endcaseendendmodule

低音对应的选择值为1-7,中音为11-17,高音为21-27,超高音为31-37。我们在使用ROM进行储存时只需要储存简谱名对应的选择值即可。
ROM选用单口,位宽8位,深度256即可(可根据储存的简谱长度来做适当调整)。这里给出《花海》的一些简谱频率计数值

3.PWM产生:

如何控制PWM方波的产生呢?
首先是PWM频率:
我们需要定义一个频率计数器:cnt_cycle,计数时长为频率计数值,即为我们上面频率选择模块中的cycle,在播放一个频谱名时,计数器记满频率计数值或者本次频谱名播放时间结束,计数器清零。
然后是PWM占空比:
我们需要定义一个PWM占空比数值:duty_data,当占空比为50%时,duty_data=cycle/2,通过调节占空比大小,可以调节声音大小,因为我的蜂鸣器声音有点大,所以我把占空比调到了25%。在频率计数器计数值小于duty_data时,输出到蜂鸣器为0,大于时为1。

//频率计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)cnt_cycle<=1'b0;
else if(cnt_cycle==cycle || cnt==time_cycle)cnt_cycle<=1'b0;
elsecnt_cycle<=cnt_cycle+1'b1;//占空比
assign duty_data=cycle/4;//蜂鸣器输出PWM
always @(posedge clk or negedge rst_n)
if(!rst_n)beep=1'b0;
else if(cnt_cycle>=duty_data)beep=1'b1;
elsebeep=1'b0;

4.ROM存储器地址改变:

每计数完成一个频谱名的时间,地址加一。

//频率rom计数器加一
always @(posedge clk or negedge rst_n)
if(!rst_n)address<=1'b0;
else if(address==8'd111 && cnt==time_cycle)address<=1'b0;
else if(cnt==time_cycle)address<=address+1'b1;

四、整体代码:

RTL代码:

module beep_music(input wire clk,input wire rst_n,output reg beep);parameter CLK_FRE   = 50 ;reg [31:0]cnt;wire [19:0]duty_data;reg [7:0]address;wire [7:0]time_music;reg [31:0]time_cycle;wire [7:0]hz_sel;wire [19:0]cycle;reg [19:0]cnt_cycle;time_music time_music0(.address(address),.clock(clk),.q(time_music));hz_sel hz_sel0(.address(address),.clock(clk),.q(hz_sel));hz hz0(.hz_sel(hz_sel),.cycle(cycle));//单个音符时间计数值always @(posedge clk or negedge rst_n)if(!rst_n)time_cycle<=32'd0;elsetime_cycle<=time_music*(CLK_FRE*1000000/8) ;//计时器always @(posedge clk or negedge rst_n)if(!rst_n)cnt<=1'b0;else if(cnt==time_cycle)cnt<=1'b0;elsecnt<=cnt+1'b1;//频率rom计数器加一always @(posedge clk or negedge rst_n)if(!rst_n)address<=1'b0;else if(address==8'd111 && cnt==time_cycle)address<=1'b0;else if(cnt==time_cycle)address<=address+1'b1;//频率计数器always @(posedge clk or negedge rst_n)if(!rst_n)cnt_cycle<=1'b0;else if(cnt_cycle==cycle || cnt==time_cycle)cnt_cycle<=1'b0;elsecnt_cycle<=cnt_cycle+1'b1;//占空比assign duty_data=cycle/4;//蜂鸣器输出PWMalways @(posedge clk or negedge rst_n)if(!rst_n)beep=1'b0;else if(cnt_cycle>=duty_data)beep=1'b1;elsebeep=1'b0;endmodule

仿真测试模块:

`timescale 1ns/1ns
`define clk_period 20module beep_music_tb;reg clk;reg rst_n;wire beep;beep_music beep_music(.clk(clk),.rst_n(rst_n),.beep(beep));initial clk=1'b1;always #(`clk_period/2) clk=~clk;initial beginrst_n=1'b0;#(`clk_period*20+1);rst_n=1'b1;#(`clk_period*100000000);$stop;end
endmodule

这个程序的仿真需要改变一些计数值来进行,否则运行时间过长,无法观察到有效波形,因此就不在此贴出仿真波形图了,想进行仿真的可以自行修改计数时长来观察仿真。

五、遇到的问题:

这个代码不算长也不难,但是我却调试了很久才正确播放,有几个细节问题导致了错误,希望对你有用:
1.设置寄存器的长度:cycle这个数值的长度不对是我一开始无法正确播放音乐的原因,因为长度不够,所以频率截止了,导致每个音都是同样的音调。
2.ROM调用时的地址长度,如果选小了就会一句音乐循环播放了。

总结

写完代码就可以上板听音乐了,但蜂鸣器的声音着实是不太好听,可以学着用扬声器(喇叭)来进行播放,音质会好很多。

FPGA之蜂鸣器播放音乐《花海》相关推荐

  1. FPGA实现蜂鸣器播放音乐实验,以儿歌《两只老虎》为例

    ** FPGA实现蜂鸣器播放音乐 ** 实现功能: 开发板上电后,通过蜂鸣器播放<两只老虎>. 设计思路 原理:想要让蜂鸣器播放音乐,就需要知道每个音符的频率,再通过换算将频率转换为周期, ...

  2. FPGA蜂鸣器播放音乐

      最近做EDA课设,看到自己的买的板子上有蜂鸣器,所以就打算做一个FPGA控制蜂鸣器播放音乐.   这里我使用的板子是睿智助学的FPGA开发板,板子上的芯片是EP4CE6E22C8,如果是你使用的是 ...

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

    文章目录 前言 一.蜂鸣器简介 二.音频音符简介 三.任务要求 四.程序设计 1.设计思路 2.程序代码 总结 前言 蜂鸣器(Buzzer)是现代常用的一种电子发声器,主要用于产生声音信号.它是一种一 ...

  4. 蜂鸣器播放音乐 fpga实现

    目录 原理 实现 原理 蜂鸣器分为有源蜂鸣器和无源蜂鸣器,即以有无震荡源分类, 有震荡源直接通直流电就可发生,无震荡源则需要通震荡波才能发声. 这里介绍无源蜂鸣器播放音乐的方法. 思路: 利用计数器产 ...

  5. 单片机并行口控制蜂鸣器播放音乐

    要用单片机并行口控制蜂鸣器播放音乐,需要完成以下步骤: 设计音乐:需要先确定音乐的节拍和音符,并将其转换为代码中的数值. 设置并行口:需要将单片机的并行口配置为输出模式,以便控制蜂鸣器. 编写代码:需 ...

  6. MCU-51:单片机蜂鸣器播放音乐和提示音

    目录 一.蜂鸣器介绍 二.驱动电路 2.1 三极管驱动 2.2 集成电路驱动 三.蜂鸣器播放音乐 3.1 键盘与音符对照 3.2 音符与频率对照 3.3 简谱 四.代码演示 4.1 按键蜂鸣器播放提示 ...

  7. 51单片机_11-2 蜂鸣器播放音乐

    文章目录 要求 资源 main函数 要求 控制蜂鸣器播放音乐 资源 工程文件压缩包 链接:https://pan.baidu.com/s/1hA-aa0JRP33IlOYFNzKf7A 提取码:0id ...

  8. Arduino 蜂鸣器播放音乐LED灯闪烁

    一.实验所需组件: led灯 三脚蜂鸣器 面包板 Arduino开发板 杜邦线 二.实验原理 蜂鸣器实现音乐播放: 控制好频率和节拍,就能演奏出好听的音乐.因此,在实验之前要先弄清楚各音调的频率.可参 ...

  9. STC-B学习板蜂鸣器播放音乐

    功能:单片机烧录好程序后,按key1按键播放<IF YOU>,再次按key1可暂停播放,按key2可切换歌曲到<国际歌>,再按key3可切换歌曲到<孤勇者> 芯片: ...

最新文章

  1. 【狂人小白】如何将Java项目发布到Maven中
  2. php读取子目录下文件内容,php小代码----目录下读取子文件或子目录_PHP教程
  3. Android 系统(68)---使用Xshell在Windows系统和Linux系统之间进行文件传输
  4. mysql主从复制及问题解决
  5. Atitit 判断判断一张图片是否包含另一张小图片
  6. 前端使用身份证阅读器(高拍仪)集成vue项目
  7. ubuntu邮件客户端_在Ubuntu中将Gmail设置为默认邮件客户端
  8. Android 国内阿里云镜像
  9. 骗子网站--正规网赚系统--www.j9m2.com--诈骗网站
  10. 远程网络监视(rmon)与简单网络管理协议(snmp)之间是什么关系
  11. Cocos2d-js cc.director介绍
  12. VIVADO中WNS,WHS,TNS,THS含义
  13. 龙架构(LoongArch)赋能众享链网,相关产品已完成适配
  14. [CTS2019]氪金手游(容斥+树形背包DP)
  15. Vulkan_Shader—天空盒原理及源码
  16. 市政协调研组:建议设大数据管理局
  17. 【JavaSE】《基础篇005》集合
  18. python中,获取字符串的长度
  19. JXTA v2.5技术手册
  20. iframe如何刷新的三种实现方案

热门文章

  1. ajax 请求被终止 chrome查询发现请求状态status为canceled
  2. 项目中的软件质量管理
  3. mpvue返回上一个页面_小程序返回上一页 - 芊芊一隅
  4. CTF-隐写术(六)
  5. HDFS配置lzo压缩
  6. Java工程师:双非计算机小硕的秋招经历总结
  7. 三、histeq的原理和用法
  8. 如何将windows安装在移动硬盘上
  9. php转繁体代码,php在gbk编码下繁体与简体互转函数
  10. [翻译] 神经网络与深度学习 致谢、常见问题 - Acknowledgements, Frequently Asked Questions