当前位置: 首页 > news >正文

FPGA开发:音乐播放器

相关阅读

FPGA开发专栏https://blog.csdn.net/weixin_45791458/category_12388695.html?spm=1001.2014.3001.5482


FPGA开发板上的蜂鸣器可以用来播放音乐,只需要控制蜂鸣器信号的方波频率、占空比和持续时间即可。

1、简谱原理

简谱上的4/4表示该简谱以4分音符为一拍,每小节4拍,简谱上应该也会标注每分钟多少拍。音符时值对照表如下图所示,这表示了每个音符的演奏时长。

音符是记录音的高低和长短的符号,简谱中的音符是七个阿拉伯数字,它们是:1(Do)、2(Re)、3(Mi)、4(Fa)、5(Sol)、6(La)、7(Ti),为了标记更高或更低的音,则在基本符号的上面或下面加上小圆点。在简谱中,不带点的基本符号叫中音。记在简谱基本音符号下面的小圆点,叫低音点,它表示将基本音符降低一个音组,即降低一个纯八度。在基本符号下面加一个点叫低音,加两个点叫倍低音,加三个点叫超低音。记在简谱基本音符号上面的小圆点,叫高音点,它表示将基本音符升高一个音组,即升高一个纯八度。在基本符号上面加一个点叫高音,加两个点叫倍高音,加三个点叫超高音

音符所对应的频率如下表所示。

音符频率
低音1261Hz
低音2293Hz
低音3329Hz
低音4349Hz
低音5392Hz
低音6440Hz
低音7499Hz
中音1523Hz
中音2587Hz
中音3659Hz
中音4698Hz
中音5784Hz
中音6880Hz
中音7998Hz
高音11046Hz
高音21174Hz
高音31318Hz
高音41396Hz
高音51568Hz
高音61760Hz
高音71976Hz

2、结构设计

2.1、按键消抖模块

由于要是用按键控制音乐开始播放,所以需要一个按键消抖模块,具体可以在FPGA开发:按键消抖一文中找到。

Debounce debounce_0 ( .clk (clk), .rst (rst_n), .button_in (button_in), .button_out (button_out) );

同时我们还需要一个边沿检测的机制来保证一次按下只触发一次按键操作。

always @ (posedge clk or negedge rst_n)begin if(~rst_n)begin button_out_d0 <= 1'b1; button_negedge <= 1'b0; end else begin button_out_d0 <= button_out; button_negedge <= button_out_d0 & ~button_out; end end

2.2、ROM模块

使用ROM保存音符时长和音调,创建ROM的过程可以根据不同的FPGA开发环境而定,如果是Quartus的话步骤如下:

首先新建两个个MIF文件,它们是用来初始化ROM的,如下图所示。

根据你的简谱长度,设置深度,如下图所示。

随后根据简谱填入对应信息并保存,如下图所示。

接着在IP窗口搜索ROM IP,如下图所示。

选好模块名和HDL类型并保存,这里选择Verilog HDL,如下图所示。

在ROM创建菜单中选择创建的ROM大小(这里应该要和刚才的MIF文件一致),如下图所示。

在初始化界面,选择使用刚才创建的MIF文件并Finish即可完成ROM的创建,如下图所示。

2.3、频率译码模块

规定中音1使用十进制数11表示,而低音1使用01表示,中音2使用12表示。译码模块根据对应的音符频率,输出相应的周期,其中CLK_FRE根据开发板的频率而定。

module music_hz( input [7:0] hz_sel, output reg [19:0] cycle ); parameter CLK_FRE = 50 ; always @(*)begin case(hz_sel) 8'h01 : cycle = CLK_FRE*1000000/261 ; //low 1 261Hz 8'h02 : cycle = CLK_FRE*1000000/293 ; //low 2 293Hz 8'h03 : cycle = CLK_FRE*1000000/329 ; //low 3 329Hz 8'h04 : cycle = CLK_FRE*1000000/349 ; //low 4 349Hz 8'h05 : cycle = CLK_FRE*1000000/392 ; //low 5 392Hz 8'h06 : cycle = CLK_FRE*1000000/440 ; //low 6 440Hz 8'h07 : cycle = CLK_FRE*1000000/499 ; //low 7 499Hz 8'h11 : cycle = CLK_FRE*1000000/523 ; //middle 1 523Hz 8'h12 : cycle = CLK_FRE*1000000/587 ; //middle 2 587Hz 8'h13 : cycle = CLK_FRE*1000000/659 ; //middle 3 659Hz 8'h14 : cycle = CLK_FRE*1000000/698 ; //middle 4 698Hz 8'h15 : cycle = CLK_FRE*1000000/784 ; //middle 5 784Hz 8'h16 : cycle = CLK_FRE*1000000/880 ; //middle 6 880Hz 8'h17 : cycle = CLK_FRE*1000000/998 ; //middle 7 998Hz 8'h21 : cycle = CLK_FRE*1000000/1046 ; //high 1 1046Hz 8'h22 : cycle = CLK_FRE*1000000/1174 ; //high 2 1174Hz 8'h23 : cycle = CLK_FRE*1000000/1318 ; //high 3 1318Hz 8'h24 : cycle = CLK_FRE*1000000/1396 ; //high 4 1396Hz 8'h25 : cycle = CLK_FRE*1000000/1568 ; //high 5 1568Hz 8'h26 : cycle = CLK_FRE*1000000/1760 ; //high 6 1760Hz 8'h27 : cycle = CLK_FRE*1000000/1976 ; //high 7 1976Hz default : cycle = 20'd0 ; endcase end endmodule

2.4、状态机演奏模块

状态机设有四个状态,IDLE,PLAY,PLAY_WAIT和PLAY_END,其中PLAY状态使用一个计数器对每个音符的演奏时长进行计数,PLAY_WAIT用于检查是否全部音符演奏完毕,如果否,则会对演奏时长计数器清零并再次进入PLAY状态。

always @(*)begin case(state) IDLE:begin if (button_negedge) next_state = PLAY; else next_state = IDLE; end PLAY:begin if (play_cnt == music_time) next_state = PLAY_WAIT; else next_state = PLAY; end PLAY_WAIT:begin if (music_cnt == music_len - 1) next_state = PLAY_END; else next_state = PLAY; end PLAY_END:next_state = IDLE; default:next_state = IDLE; endcase end

周期计数器用于对音符的每个周期进行计数,并提供计数值给输出信号模块。

always @(posedge clk or negedge rst_n)begin if (~rst_n) hz_cnt <= 20'd0; else if (state == PLAY || state == PLAY_WAIT)begin if (hz_cnt == cycle - 1) hz_cnt <= 20'd0; else hz_cnt <= hz_cnt + 1'b1; end else hz_cnt <= 20'd0; end

输出信号模块根据计数值输出信号,其中还可以控制占空比。

always @(posedge clk or negedge rst_n)begin if (~rst_n) buzzer <= 1'b1; else if (state == PLAY || state == PLAY_WAIT)begin if (hz_cnt < cycle/32) //控制占空比 buzzer <= 1'b0; else buzzer <= 1'b1; end else if (state == IDLE || state == PLAY_END) buzzer <= 1'b1; end

演奏时长计数器用于对每个音符的演奏时间计数。

always @(posedge clk or negedge rst_n)begin if (~rst_n) play_cnt <= 32'd0; else if (state == PLAY) play_cnt <= play_cnt + 1'b1; else play_cnt <= 32'd0; end

演奏个数计数器用于对演奏的音符数计数。

always @(posedge clk or negedge rst_n)begin if (~rst_n) music_cnt <= 32'd0; else if (state == PLAY_WAIT) music_cnt <= music_cnt + 1'b1; else if (state == IDLE || state == PLAY_END) music_cnt <= 32'd0; end

最后实例化ROM,并且注意,这里规定演奏时长rom值以8为一拍,所以读取rom值后需要进行转换,假设一分钟85拍。

music_hz hz0 ( .hz_sel(rom_hz_data), .cycle(cycle) ) ; music_rom hz_rom ( .address(music_cnt[8:0]), .clock(clk), .q(rom_hz_data) ); music_time_rom time_rom ( .address(music_cnt[8:0]), .clock(clk), .q(rom_time_data) ); always @(posedge clk or negedge rst_n)begin if (~rst_n) music_time <= 32'hffff_ffff; else music_time <= rom_time_data*(CLK_FRE*1000000*60/85/8); end
http://www.jsqmd.com/news/585806/

相关文章:

  • 05:输出保留12位小数的浮点数
  • 从零开始构建P2P视频分发网络:PCDN实战指南
  • 盘点2026年徐州能做一般纳税人升级的好用代理记账公司 - 工业品牌热点
  • 如何通过脚本化工作流突破Adobe Illustrator的效率瓶颈?
  • WarcraftHelper:经典《魔兽争霸III》的现代适配解决方案
  • 告别手动抢茅台!Campus-iMaoTai智能预约系统完整使用指南
  • ArcGIS空间统计实战:用Global Moran‘s I分析你所在城市的人口聚集度(附2000-2020年数据)
  • 终极指南:Feast特征推送Push模式实现实时数据写入的5个关键步骤
  • 如何彻底卸载Microsoft Edge浏览器:EdgeRemover终极指南
  • 2026年想在成都挑资质代办公司?这些要点一定要掌握! - 红客云(官方)
  • RHCA II之路---EX442-12
  • FLUX.2-klein-base-9b-nvfp4与STM32嵌入式视觉项目结合:离线图像预处理方案
  • 2023-2024学年第一学期语文教研组资源清单
  • 抖音视频批量采集技术架构:多策略智能调度与抗反爬机制深度解析
  • 哔咔漫画下载器终极指南:3步打造个人漫画图书馆
  • 3分钟完成Windows和Office免费激活:KMS_VL_ALL_AIO智能解决方案
  • Qwen3.5-4B-Claude模型API接口自动化测试用例设计与生成
  • ModTheSpire定制引擎:打造你的个性化杀戮尖塔体验
  • 绝配!免费的Qwen3.6Plus接入阿里CoPaw!
  • OpenClaw性能实测:Qwen3-4B-Thinking在不同硬件下的表现
  • GoConvey终极指南:如何在浏览器中进行Go测试的完整教程
  • 如何快速掌握 Bowser 浏览器嗅探工具:从架构到实战的完整指南
  • NormalMap-Online:3步掌握专业法线贴图生成的终极指南
  • Video2X:AI视频增强全攻略:从问题诊断到效能优化的完整路径
  • FreeGPT WebUI终极安全配置指南:如何设置用户权限与访问控制
  • B站m4s格式转换完整教程:5秒极速实现缓存视频永久保存
  • 3大核心功能解析:飞秋Mac版如何实现高效局域网通信
  • 终极指南:Farm 与 Rollup 插件生态的完美兼容方案
  • 如何彻底解决暗影精灵游戏本的原厂控制软件痛点?OmenSuperHub开源方案深度解析
  • HardSourceWebpackPlugin序列化器对比:JSON、Append、Cacache和LevelDB性能分析