FPGA开发:音乐播放器

news2024/12/24 10:26:07

        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 posedge rst)begin
	if(rst == 1'b1)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.coloradmin.cn/o/827588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无涯教程-Lua - while语句函数

只要给定条件为真&#xff0c;Lua编程语言中的 while 循环语句就会重复执行目标语句。 while loop - 语法 Lua编程语言中 while 循环的语法如下- while(condition) dostatement(s) end while loop - 流程图 在这里&#xff0c;需要注意的关键是 while 循环可能根本不执行。…

Go语音介绍

Go语言介绍 Go 即Golang&#xff0c;是Google公司2009年11月正式对外公开的一门编程语言。 Go是静态强类型语言&#xff0c;是区别于解析型语言的编译型语言。 解析型语言——源代码是先翻译为中间代码&#xff0c;然后由解析器对代码进行解释执行。 编译型语言——源代码编…

【树形DP+换根思想】2022牛客多校加赛 H

登录—专业IT笔试面试备考平台_牛客网 题意&#xff1a; 思路&#xff1a; 这个虽然是树形DP&#xff0c;却用了换根的思想.... 首先&#xff0c;后缀0的个数可以转化成min(cnt2,cnt5)&#xff0c;其中cnt2为2的因子个数&#xff0c;cnt5为5的因子个数 然后进行DP 设dp[u]…

面试之多线程(三)

1.进程和线程的区别 根本区别&#xff1a;进程是操作系统分配资源的最小单位&#xff1b;线程是CPU调度的最小单位所属关系&#xff1a;一个进程包含了多个线程&#xff0c;至少拥有一个主线程&#xff1b;线程所属于进程开销不同&#xff1a;进程的创建&#xff0c;销毁&…

基于Vue+ElementUI+Echarts+G2Plot的仪表盘设计器,代码完全开源

简介 &#x1f525;DashBoard基于SpringBoot、MyBatisPlus、ElementUI、G2Plot、Echarts等技术栈的仪表盘设计器&#xff0c;具备仪表盘设计、预览、资源管理、组件管理等能力&#xff0c;支持JSON、MySQL、Oracle、PostgreSQL、HTTP、JavaScript、Groovy等数据集接入&#xf…

类加载器的双亲委派机制和源码分析

双亲委派机制 双亲委派就是一个从子到父的过程,然后还有一个从父到子的状态。具体如下: 如果一个类加载器收到了类加载的请求,它会首先去自己的缓存中查找是否加载过这个类(findLoadedClass),如果有,那么直接加载。如果没有就会让父加载器加载,父加载器会先去自己的缓…

自定义elementui的主题

通常情况下&#xff0c;我们使用elementui框架的时候默认组件的主题都是白色的&#xff0c;比如&#xff1a; 但是如果想自定义主题&#xff0c;改变主题颜色&#xff0c;以及各种默认颜色&#xff0c;其实也不难&#xff1a; 配置默认主题&#xff0c;选好后点击下载 在vu…

软件外包开发的PHP开发框架

PHP有许多流行的开发框架&#xff0c;每个框架都有其独特的特点和优势。下面列举的只是一部分PHP开发框架&#xff0c;还有其他一些框架如Slim、Zend Framework等也值得一提。选择合适的框架取决于项目的需求和开发团队的偏好&#xff0c;您可以根据项目规模、复杂性和功能需求…

Golang之路---02 基础语法——异常机制:panic 和 recover

Golang的异常处理 Go 没有像 Java 和 .NET 那样的 try/catch 异常机制&#xff1a;不能执行抛异常操作。 在 Golang 中&#xff0c;有不少常规错误&#xff0c;在编译阶段就能提前告警&#xff0c;比如语法错误或类型错误等&#xff0c;但是有些错误仅能在程序运行后才能发生&…

【推荐】通用全面的APP测试用例设计

1、安装卸载 用例编号 测试内容 操作步骤 预期结果 测试次数 测试结果 备注 安装 1 通过第三方软件协助安装是否正常 第三方软件搜索app&#xff0c;安装 目标:支持360、豌豆荚、应用宝等主流辅助工具 1 Pass 2 在不同操作系统下安装是否正常 1、使用测试手机安装 …

面试总被问高并发负载测试,你真的会么?

本文将介绍使用50K并发用户测试轻松运行负载测试所需的步骤&#xff08;以及最多200万用户的更大测试&#xff09;。 ❶ 写你的剧本 ❷ 使用JMeter在本地测试 ❸ BlazeMeter SandBox测试 ❹ 使用一个控制台和一个引擎设置每引擎用户数量 ❺ 设置和测试群集&#xff08;一个…

【环形链表(带环链表)超详细总结】

题目一 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 使用快慢指针&#xff0c;如果慢指针等于快指针&#xff0c;就说明是带环链表链表的中间结点 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;…

什么是 webpack?

Webpack 介绍 什么是 webpack&#xff1f; :::tip 官方描述 webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项目中所需的每一个…

Zookeeper基础操作

搭建Zookeeper服务器 windows下部署 下载地址: https://mirrors.cloud.tencent.com/apache/zookeeper/zookeeper-3.7.1/ 修改配置文件 打开conf目录&#xff0c;将 zoo_sample.cfg复制一份&#xff0c;命名为 zoo.cfg打开 zoo.cfg&#xff0c;修改 dataDir路径&#xff0c…

0802|IO进程线程day5 作业(打印时钟在终端上,若终端输入quit,结束时钟)

作业1&#xff1a;守护进程 守护进程的创建&#xff08;5步&#xff09;&#xff1a; 创建孤儿进程&#xff1a;所有工作都在子进程中执行&#xff0c;从形式上脱离终端控制。 fork(), 退出父进程 创建新的会话组&#xff1a;使子进程完全独立出来&#xff0c;防止兄弟进程对其…

蓝桥杯上岸必背!!!(并查集补更)

蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(并查集补更) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &#x1f64f; 距离蓝桥杯省赛倒数第3天 ❗️ ❗️ ❗️ 还没背熟模板的伙伴们背起来 &#x1f4aa; &…

git 常用命令有哪些

Git 是我们开发工作中使用频率极高的工具&#xff0c;下面总结下他的基本指令有哪些&#xff0c;顺便温习一下。 前言 一般项目中长存2个分支&#xff1a; 主分支&#xff08;master&#xff09; 和开发分支&#xff08;develp&#xff09; 项目存在三种短期分支 &#xff1a…

大数据技术之Clickhouse---入门篇---SQL操作、副本

星光下的赶路人star的个人主页 积一勺以成江河&#xff0c;累微尘以崇峻极 文章目录 1、SQL操作1.1 Insert1.2 Update 和 Delete1.3 查询操作1.4 alter操作1.5 导出数据 2、副本2.1 副本写入流程2.2 配置步骤 1、SQL操作 基本上来说传统关系型数据库&#xff08;以 MySQL 为例…

忘记数据库密码如何处理

windows 5.6.51版本及以前 #当前账号设置密码 set password password(123456); #当前账号取消密码 set password ; &#xff08;1&#xff09;用管理员身份打开控制台输入 net stop m5&#xff08;我的电脑MySQL名字为m5&#xff0c;根据自己的更改&#xff09; &#xff08;…

爱尔眼科四川省区“同心博爱 光明工程”“西部健康公益行”炉霍站启动

8月1日&#xff0c;“同心博爱 光明工程”“西部健康公益行”炉霍站出征仪式在四川爱尔眼科医院隆重举行。 此次公益活动由民革成都市委会、中共锦江区委统战部指导&#xff0c;如意树爱心促进会主办&#xff0c;民革锦江区总支部、爱尔眼科四川省区支持&#xff0c;四川爱尔眼…