提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 基于FPGA设计的音乐播放器
  • 一、生成PCM音频格式的音乐文件
    • 1.PCM音频文件格式
    • 2.Matlab读取WAV文件
  • 二、音频文件储存
    • 1.ROM存储简单音频文件
    • 2. I2S数据传输协议
  • 三、PCM5102解码模块介绍

基于FPGA设计的音乐播放器

本文将介绍如何使用FPGA和PCM5102音频解码模块来制作音乐播放器,从生成PCM格式的音频文件开始,到如何编写I2S总线协议代码,音频数据的储存等。

一、生成PCM音频格式的音乐文件

1.PCM音频文件格式

PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。PCM音频的几个关键参数如下。

参数 描述
采样率(Sample Rate) 表示一帧音频数据的出现频率,在每一个周期期间内,传输完所有声道的音频信息。常用的有44.1kHz
量化位数(Sample Size) 表示对音频数据的量化位数,即单个通道的位宽。常用的有16bit,24bit,32bit
通道个数(Channels ) 表示音频数据的通道个数,双通道即可完成立体声效果,但双通道不一定是立体声,而立体声一定是多通道
数据符号(Sign) 表示音频数据是否带符号,有符号和无符号数的表示范围不同。例如当位宽为8bit时,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255

简单介绍如下:假设正弦波形为音频模拟信号,发出的声音为"滴",按照如图所示的采样、量化、编码为32bit有符号的数据,即为单通道PCM音频数据。

当然我们制作音乐播放器肯定不是只能播放出"滴",所以需要先生成满足需求的音频数据,就选取周杰伦的七里香钢琴曲作为音频源文件。下载WAV格式的音频文件,这里可以使用qq音乐下载MP3格式后,在进行音频转码为WAVE格式,如图所示。

2.Matlab读取WAV文件

当音频转码完成后,生成的WAV文件可直接使用matlab进行读取,使用函数audioread即可,下面附上完整读取、采样、量化、进制转换代码。

%% 读取wav音频文件,写入mif/coe
clear all ;
clc ;
wav  = audioread('周杰伦 - 七里香(钢琴版).wav') ;
% 低采样
sr = 3 ;
m = floor( max(wav) ) / 3 ;
wav_catch = zeros(m , 2) ;
wav_catch(: , 1) = wav(1:m , 1) ;
wav_catch(: , 2) = wav(1:m , 2) ;
save wav_catch wav_catch ;
% 截取部分时间,同时增大幅度值,位宽为32bit
l = 16384 ;
audio_l = wav_catch(1:l , 1) * 2^31 ;
audio_r = wav_catch(1:l , 2) * 2^31 ;
% 十进制 -> 十六进制
audio_l(find(audio_l<0)) = audio_l(find(audio_l<0)) + 2^32 ;
audio_r(find(audio_r<0)) = audio_r(find(audio_r<0)) + 2^32 ;
audio_l_hex = dec2hex(audio_l) ;
audio_r_hex = dec2hex(audio_r) ;

其中各个模块已经给出了中文注释,由于负数在FPGA中是以补码的形式进行储存,对于其中的十进制转换十六进制有不懂的地方可自行百度。到这里就已经得到了满足PCM格式的音频数据文件,由于数据量过大,如若使用ROM来存储,将会耗费大量的BRAM资源以及底层逻辑资源,甚至造成资源不够的情况。

当然若使用此种方式,需要生成COE或MIF文件可参考MATLAB生成COE或MIF文件代码
在本设计中需要完整的播放整个音乐文件,即使已经将采样率压缩到16KHz,FPGA上的ROM资源依然不够,所以采用SD卡的方式,或是通过串口写入到FLASH上面进行音频文件的储存,该部分内容会在后续完成后上传。

2022/03/22 毕业论文初稿完成,得闲,不愿荒废时光,却又找不到什么实际意义的事情,随便记录一下,留着以后怀念。

二、音频文件储存

1.ROM存储简单音频文件

本来之前想把整首曲子存储在sd卡或是ddr里面,然中间很多事情耽搁了,现在又投身工作了,难以抽身。无意间看到有私信求更,才想起来,那就简单的更完吧。

在FPGA中,只读存储器ROM(read only memory)常用于存放初始数据。ROM中的数据需要先进行初始化,即要先将数据写入到ROM内部的存储单元中,然后系统正常工作时,只能读出其中存储的数据,而不能写入信息,且其中储存的数据掉电不会丢失。由前面章节介绍可知,我们需要在ROM中存入音频数据,格式为PCM音频数据格式,如下图所示。

为了体现立体声的效果,我们设计传输两个声道的音频信息,选用双声道的数据格式。音频数据都选用正弦波形数据,其播放出来的声音为“滴”,但两个声道传输的正弦波形数据的幅度不同,在播放时两边声音的音量大小不同。设计时先使用Matlab生成幅度变化满足需求的音频数据,并创建内存初始化(mif)文件,将正弦波数据写入。然后使用EDA工具quartus直接调用已经封装好的ROM存储器IP核,如图所示。
我们设定ROM的输出端口位宽为32bit,指定存储器的深度为256,表示本IP核可以存储256个32bit位宽的音频数据。通过载入mif文件为存储器提供存储器初始化数据,并编写仿真文件对该ROM进行测试验证。

如下图所示为ROM的功能仿真波形图。从图中可知,左右声道中传输的数据不同,故在经过PCM5102解码模块解码后的音频信息中,可以明显的感受到立体声效果,这是由于双耳播放的音频不同导致的。

2. I2S数据传输协议

PCM5102解码模块是基于I2S传输协议,选择左对齐模式下的I2S传输协议进行代码编写和功能仿真。
首先,I2S总线共拥有三条数据信号线,一条系统时钟线,各信号简要介绍如下:
(1)BCK:串行时钟信号,每一个脉冲周期对应于数字音频文件中的每一位数据,所以也称为位同步时钟信号。在本设计中,声道数为2,音频数据位宽为32位,那么BCK的频率可通过如下公式计算:

其中f(lrck)为采样频率。
(2)LRCK:声道选择信号,用于切换左右声道的数据,也称为帧同步信号。命令选择线表明了正在被传输的声道,LRCK为低电平表示正在传输的是左声道的数据,LRCK为高电平表示正在传输的是右声道的数据。该信号的频率即是采样频率。
(3)SDIN:串行数据信号,即将音频数据按照串行的方式进行传输,先传输数据的最高位,最低位的位置则是依赖于数据的有效位数,本设计中有效位数是32位,那么不存在无效位,传输前音频数据都应转换为二进制补码的形式。
(4)SCK:系统时钟信号,当处于主模式时,可用于为外部设备提供系统时钟,工作为从模式时,不可用。

本文根据解码模块的系统时钟要求,如下图所示,图片来自pcm5102模块文档说明。我们选择采样时钟为16KHz,系统时钟为4.096MHz,进行时序设计。

其中基于i2s协议的数据发送代码如下:

module I2S_SEND (input           clk     ,   // 50Minput           rstn    ,input   [31:0]  audio_l ,input   [31:0]  audio_r ,output          flag    ,output          bck     ,output          lrck    ,output          sck     ,output          dout
);//----------------------
reg         [31:0]  r_audio_l ;
reg         [31:0]  r_audio_r ; reg                 SDIN ;reg                 BCLK ;
reg                 WCLK ;
reg                 SCLK ;
reg         [5:0]   SCLK_cnt ;
reg         [11:0]  BCLK_cnt ;
reg         [15:0]  WCLK_cnt ;reg         [1:0]   pos_BCLK ;
reg         [1:0]   neg_BCLK ;
reg         [1:0]   pos_WCLK ;
reg         [1:0]   neg_WCLK ;//----------------------
assign      bck = BCLK ;
assign      lrck = WCLK ;
assign      sck = SCLK ;
assign      dout = SDIN ;//----------------------
assign      flag = (WCLK_cnt == 16'd3070) ? 1 : 0 ;//- BCLK gen
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginBCLK_cnt <= 12'd0 ;endelse if(BCLK_cnt == 12'd47)beginBCLK_cnt <= 12'd0 ;endelsebeginBCLK_cnt <= BCLK_cnt + 1'b1 ;end
endalways @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginBCLK <= 1'b0 ;endelse if(BCLK_cnt < 12'd24)beginBCLK <= 1'b0 ;endelsebeginBCLK <= 1'b1 ;end
end
//- WCLK gen
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginWCLK_cnt <= 16'd0 ;endelse if(WCLK_cnt == 16'd3071)beginWCLK_cnt <= 16'd0 ;endelsebeginWCLK_cnt <= WCLK_cnt + 1'b1 ;end
endalways @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginWCLK <= 1'b1 ;endelse if(WCLK_cnt < 16'd1536)beginWCLK <= 1'b1 ;endelsebeginWCLK <= 1'b0 ;end
end
//- SCLK gen
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginSCLK_cnt <= 6'd0 ;endelse if(SCLK_cnt == 6'd11)beginSCLK_cnt <= 6'd0 ;endelsebeginSCLK_cnt <= SCLK_cnt + 1'b1 ;end
endalways @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginSCLK <= 1'b0 ;endelse if(SCLK_cnt < 6'd6)beginSCLK <= 1'b0 ;endelsebeginSCLK <= 1'b1 ;end
end
//-
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginr_audio_l <= 32'd0 ;r_audio_r <= 32'd0 ;endelse if(WCLK_cnt == 16'd3071)beginr_audio_l <= audio_l ;r_audio_r <= audio_r ;endelse if(BCLK_cnt == 12'd0)beginr_audio_l <= {r_audio_l[30:0] , r_audio_l[31]} ;r_audio_r <= {r_audio_r[30:0] , r_audio_r[31]} ;end
end
//- DOUT gen
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginSDIN <= 1'b0 ;endelse if(WCLK_cnt < 16'd1536)beginif(BCLK_cnt == 12'd0)beginSDIN <= r_audio_l[31] ;endelse beginSDIN <= SDIN ;endendelse if(WCLK_cnt > 16'd1535)beginif(BCLK_cnt == 12'd0)beginSDIN <= r_audio_r[31] ;endelse beginSDIN <= SDIN ;endend
end
endmodule

以下是从ROM中读取数据传给I2S的代码:

module MP3_TEST(input           clk     ,   // 50Minput           rstn    ,input           enable  ,output  [31:0]  audio_l ,output  [31:0]  audio_r );// -
wire        [31:0]      dout1 ;
wire        [31:0]      dout2 ;
reg         [4:0]       switch_state ;
reg                     switch_flag ;
reg         [7:0]       addr ;
reg         [23:0]      cnt ;
//- binary twos-complement
// assign  audio_l = (dout1[31]) ? {dout1[31] , ~dout1[30:0] + 1} :  dout1 ;
assign  audio_l = dout1 ;
assign  audio_r = dout2 ;//-
blk_mem_gen1 u1_blk_mem_gen1 (.clka               ( clk  ),          .rsta               ( ~rstn ),          .ena                ( 1'b1 ),           .addra              ( addr ),        .douta              ( dout1 ),          .rsta_busy          (  )
);
//-
blk_mem_gen2 u2_blk_mem_gen2 (.clka               ( clk  ),          .rsta               ( ~rstn ),          .ena                ( 1'b1 ),           .addra              ( addr ),        .douta              ( dout2 ),          .rsta_busy          (  )
);
//- switch frequence
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)begincnt <= 24'd0 ;switch_flag <= 1'b0 ;endelse if(cnt == 24'd6_144_000)begincnt <= 24'd0 ;switch_flag <= 1'b1 ;endelse begincnt <= cnt + 1'b1 ;switch_flag <= 1'b0 ;end
end
//- switch
always @(posedge clk or negedge rstn)
beginif(rstn == 1'b0)beginaddr <= 8'd0 ;switch_state <= 5'd0 ;endelse begincase(switch_state)0:begin addr <= 8'd0 ; switch_state <= 5'd1; end1:beginif(switch_flag)begin addr <= 8'd16 ; switch_state <= 5'd2 ; endelse if(addr == 8'd15)addr <= 8'd0 ; else if(enable)addr <= addr + 1'b1 ;end2:beginif(switch_flag)begin addr <= 8'd32 ;switch_state <= 5'd3 ; endelse if(addr == 8'd31)addr <= 8'd16 ;else if(enable)addr <= addr + 1'b1 ;end3:beginif(switch_flag)begin addr <= 8'd48 ;switch_state <= 5'd4 ; endelse if(addr == 8'd47)addr <= 8'd32 ;else if(enable)addr <= addr + 1'b1 ;end4:beginif(switch_flag)begin addr <= 8'd64 ; switch_state <= 5'd5 ; endelse if(addr == 8'd63)addr <= 8'd48 ;else if(enable)addr <= addr + 1'b1 ;end5:beginif(switch_flag)begin addr <= 8'd80 ; switch_state <= 5'd6 ; endelse if(addr == 8'd79)addr <= 8'd64 ;else if(enable)addr <= addr + 1'b1 ;end6:beginif(switch_flag)begin addr <= 8'd96 ; switch_state <= 5'd7 ; endelse if(addr == 8'd95)addr <= 8'd80 ;else if(enable)addr <= addr + 1'b1 ;end7:beginif(switch_flag)begin addr <= 8'd112 ; switch_state <= 5'd8 ; endelse if(addr == 8'd111)addr <= 8'd96 ;else if(enable)addr <= addr + 1'b1 ;end8:beginif(switch_flag)begin addr <= 8'd128 ; switch_state <= 5'd9 ; endelse if(addr == 8'd127)addr <= 8'd112 ;else if(enable)addr <= addr + 1'b1 ;end9:beginif(switch_flag)begin addr <= 8'd144 ; switch_state <= 5'd10 ; endelse if(addr == 8'd143)addr <= 8'd128 ; else if(enable)addr <= addr + 1'b1 ;end10:beginif(switch_flag)begin addr <= 8'd160 ;switch_state <= 5'd11 ; endelse if(addr == 8'd159)addr <= 8'd144 ;else if(enable)addr <= addr + 1'b1 ;end11:beginif(switch_flag)begin addr <= 8'd176 ;switch_state <= 5'd12 ; endelse if(addr == 8'd175)addr <= 8'd160 ;else if(enable)addr <= addr + 1'b1 ;end12:beginif(switch_flag)begin addr <= 8'd192 ; switch_state <= 5'd13 ; endelse if(addr == 8'd191)addr <= 8'd176 ;else if(enable)addr <= addr + 1'b1 ;end13:beginif(switch_flag)begin addr <= 8'd208 ; switch_state <= 5'd14 ; endelse if(addr == 8'd207)addr <= 8'd192 ;else if(enable)addr <= addr + 1'b1 ;end14:beginif(switch_flag)begin addr <= 8'd224 ; switch_state <= 5'd15 ; endelse if(addr == 8'd223)addr <= 8'd208 ;else if(enable)addr <= addr + 1'b1 ;end15:beginif(switch_flag)begin addr <= 8'd240 ; switch_state <= 5'd16 ; endelse if(addr == 8'd239)addr <= 8'd224 ;else if(enable)addr <= addr + 1'b1 ;end16:beginif(switch_flag)begin addr <= 8'd0 ; switch_state <= 5'd1 ; endelse if(addr == 8'd255)addr <= 8'd240 ;else if(enable)addr <= addr + 1'b1 ;enddefault:begin addr <= 8'd0 ; switch_state <= 5'd0; endendcaseend
end
endmodule

三、PCM5102解码模块介绍

pcm5102的芯片手册和原理图,网上应该很容易搜到。在这里我也创建了一份网盘分享文件,里面有相关的文档和完整的工程文件,工程是vivado2018.3版本下开发的,可供有相关兴趣的爱好者参考。

仓促结尾,实属无奈。

链接:https://pan.baidu.com/s/1cu_7JYVaHr0OJrM5-iiuqg
提取码:k87f

基于FPGA设计的音乐播放器相关推荐

  1. (附源码)springboot+基于微信小程序音乐播放器的设计与实现 毕业设计271156

    Springboot音乐播放小程序的设计与实现 摘 要 本文设计了一种基于微信小程序的音乐播放器,系统为人们提供了方便快捷.即用即搜的音乐搜索播放服务,包括音乐资讯.音乐库推荐.交流论坛.注册登录.最 ...

  2. springboot+基于微信小程序音乐播放器的设计与实现 毕业设计-附源码271156

    Springboot音乐播放小程序的设计与实现 摘 要 本文设计了一种基于微信小程序的音乐播放器,系统为人们提供了方便快捷.即用即搜的音乐搜索播放服务,包括音乐资讯.音乐库推荐.交流论坛.注册登录.最 ...

  3. html实现音乐界面设计,基于HTML5技术的音乐播放器的设计与实现.doc

    基于HTML5技术的音乐播放器的设计与实现 基于HTML5技术的音乐播放器的设计与实现 常志强 刘正余 杨劲楠 皖西学院电子与信息工程学院 X 关注成功! 加关注后您将方便地在 我的关注中得到本文献的 ...

  4. web html5音乐播放器设计与实现,基于HTML5技术的音乐播放器的设计与实现

    Vol.33No.11Nov.2017 赤峰学院学报(自然科学版) Journal of Chifeng University (Natural Science Edition )第33卷第11期(下 ...

  5. [内附完整源码和文档] 基于Android的手机音乐播放器的设计与实现

    摘 要 随着Android系统和移动互联网的快速崛起,手机已经成为人们生活不可缺的一部分,在现代人的生活中,人们生活节奏的加快,生活压力越来越大,碎片化的时间越来越多,那么一个可以在碎片化的时间内调节 ...

  6. 基于android的在线音乐播放器app设计

    Android是Google公司公布的基于Linux内核的手机操作系统,其代码属于完全开放,为开源软件开发人员提供使用方便的框架和平台.,本文以Android开发平台为基础,介绍了音乐播放器的开发.首 ...

  7. 基于STM32的电子琴音乐播放器设计

    基于STM32的电子琴/音乐播放器设计 文章目录 基于STM32的电子琴/音乐播放器设计 @[toc] 引言 第一章 总体设计 1.1 系统功能 1.2 主要技术性能指标 第二章 系统设计 2.1 系 ...

  8. 张利国,龚海平,王植萌.android移动开发入门与进阶,开题报告-基于Android的手机音乐播放器的设计与实现.doc...

    盐城师范学院 毕业设计开题报告 题 目: 基于android的手机音乐播放器 的设计与实现 姓 名: 二级学院: 信息工程学院 专 业: 软件工程 班 级: 12(1) 学 号: 指导教师: 职称: ...

  9. 基于Android的手机音乐播放器的设计与实现

    源码及论文下载:http://www.byamd.xyz/tag/android/ 摘 要 随着Android系统和移动互联网的快速崛起,手机已经成为人们生活不可缺的一部分,在现代人的生活中,人们生活 ...

最新文章

  1. GitLab安装说明
  2. Java项目:在线电影售票系统设计和实现(java+Springboot+ssm+mysql+jsp+maven)
  3. TransDecoder
  4. 【bzoj 2002】弹飞绵羊
  5. 如何在一个程序集中序列化在另一个中反序列化
  6. bat文件先杀掉端口号,然后启动jar包
  7. eth显卡算力2020最新排行_最新三大主流币IPFS比特币ETH挖矿全网算力动态速递单周报(12.3更新)...
  8. eclipse java工程目录_转载:Eclipse下的java工程目录
  9. 遇见王沥川的人生感悟_23岁酱油泡饭默默无闻,31岁逆袭人生,王彦霖有何魅力?...
  10. down redis集群_redis有哪些集群模式
  11. Html富文本编辑器
  12. java读写锁死锁例子_Java并发关于重入锁与读写锁的详解
  13. ISO 18000-6c 访问标签--应用程序访问操作ISO 18000-6C标签的方法
  14. 计算机二级用的ms什么版本,计算机二级ms office用的哪个版本
  15. 基于bootstrap拖拽布局完成的web表单设计器
  16. MSP430指令初探
  17. java 对话框计算器,《Java程序设计》第16周礼拜四:GUI编程及文件对话框的使用 计算器...
  18. 2022年全国计算机四级考试精选模拟题及答案
  19. 手机网络邻居访问电脑_通过WiFi局域网在手机上观看电脑上的视频
  20. 使用VS2015 VC++第一步 写一个hello world程序

热门文章

  1. 几个关于图像识别的有意思的事
  2. 软件中级设计师 - 计算机组成与结构
  3. Poison社RPG,百花缭乱的补充资料
  4. python中byte类型_详解python string类型 bytes类型 bytearray类型
  5. 计算机基础:(计算机的起源与发展)
  6. 电脑上桌面便签怎么移动位置
  7. Automa: 一款超强大的 Chrome 自动化神器
  8. 如何在3dmax里查看有几套UV集(UV通道)以及如何在max里删除多余的UV集
  9. 如何在计算机上更改英语语言,电脑系统语言设置如何更改
  10. [Python]HTML转换为TXT的脚本