搜索
bottom↓
回复: 0

【正点原子FPGA连载】第四十二章 音乐播放器实验--摘自【正点原子】开拓者 FPGA 开发指南

[复制链接]

出0入234汤圆

发表于 2019-7-23 16:50:51 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-24 15:18 编辑

1)实验平台:正点原子开拓者FPGA开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=579749209820
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-281143-1-1.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:712557122  点击加入: O1CN01abYcZO23XsL4ETed4_!!230947266.png
5)关注正点原子公众号,获取最新资料更新

203429z6c3os33t8albi33.png

第四十二章 音乐播放器实验

在 “音频环回实验” 中, 我们成功地用WM8978实现了音频环回, 将WM8978输入的音频数据通过WM8978输出。本章我们将使用FPGA实现从SD卡读取音乐并使用WM8978播放音乐的音乐播放器功能。
本章包括以下几个部分:
42.1 音乐播放器简介
42.2 实验任务
42.3 硬件设计
42.4 程序设计
42.1 音乐播放器简介
WM8978作为带扬声器驱动的立体声多媒体数字信号编译码器, 可以结合FPGA实现音乐播放器的功能。 本次实验, 我们结合SD卡, 把音乐文件存入SD卡中, FPGA从SD卡中读取音乐文件的数据传送给WM8978进行播放, 就可实现音乐播放器的功能。
我们在“音频环回实验” 中对WM8978的控制接口和音频接口等内容作了详细的介绍。 如果大家对这部分内容不是很熟悉的话, 请参考“音频环回实验” 中的WM8978简介部分。 这里我们简单介绍一下音频方面的知识。
人的说话频率基本上为300Hz-3400Hz, 但是人耳朵听觉频率基本上为20Hz-20000Hz。 由于人发出的声音信号为连续的模拟信号, 电子设备中处理的为数字信号, 所以需要对声音进行采样、 量化、 编码的数字化处理。 处理的第一步为采样, 即模数转换, 将连续信号转换为离散信号。 根据奈奎斯特(Nyquist) 采样定理, 用不低于两倍信号的频率进行采样就能还原该信号。所以, 对于声音信号而言, 要想对离散信号进行还原, 必须将采样频率定为40KHz以上。 在实际应用中, 一般定为44.1KHz。 可以理解为在1秒钟之内对声音波形采样44100次。 原则上采样率越高, 声音的质量越好, 采样频率一般分为22.05KHz、 44.1KHz、 48KHz三个等级。 22.05KHz只能达到FM广播的声音品质, 44.1KHz则是理论上的CD音质界限, 48KHz则已达到DVD音质了。WM8978支持的采样率有48kHz、 32kHz、 24kHz、 16kHz、 12kHz、 8kHz, 我们采用的是48kHz。信号经采样后, 进行量化、 编码。 量化位数代表用多少bit位表示采样点的值, 常用的有8位(低品质) 、 12位、 16位(高品质) 等, 16bit是最常见的采样精度。 WM8978支持的位数有16位、 20位、 24位、 32位, 我们采用的是16位。 编码在这里指信源编码, 即数据压缩。 对于音乐而言, 有无压缩的音乐格式如WAV和有压缩的音乐格式如MP3。
无压缩的音乐格式WAV文件是波形文件, 是微软公司推出的一种音频储存格式, 主要用于保存Windows平台下的音频源。 WAV文件储存的是声音波形的二进制数据, 由于没有经过压缩,使得WAV波形声音文件的体积很大。WAV文件占用的空间大小计算公式是[(采样频率×量化位数×声道数)÷8]×时间(秒) , 单位是字节(Byte) 。 理论上, 采样频率和量化位数越高越好,但是所需的磁盘空间就更大。 通用的WAV格式(即CD音质的WAV) 是44100Hz的采样频率, 16Bit的量化位数, 双声道, 这样的WAV声音文件储存一分钟的音乐需要10.34MB, 占用空间大, 但无需解码就可直接播放。
作为数字音乐文件格式的标准, WAV格式容量过大, 因而使用起来不方便。 因此, 一般情况下我们把它压缩为MP3或WMA格式。 压缩方法有无损压缩与有损压缩。 MP3, OGG就属于有损压缩, 如果把压缩的音频还原回去, 音频其实是不一样的。 当然, 人耳很难分辨出这种细微的差
别。 因此, 如果把MP3, OGG格式从压缩的状态还原回去的话, 就会产生损失。 而像APE和FLACALIENTEK PIONEER 开发板教程这类音频格式即使还原, 也能毫无损失地保留原有音质。 所以, APE和FLAC可以无损失高音质地压缩和还原。 这些压缩的音频格式由于都采用了各自的压缩算法, 若要播放音乐需要先用相应的算法进行解压缩。
本次实验我们只是把存放在SD卡里的音乐通过FPGA读出来并输出给WM8978进行播放。如果采用压缩的音乐格式而不通过相应的解压缩算法处理的话听到的就是噪声了, 所以本次实验我们使用无压缩的WAV格式。 由于一般WAV格式的音乐采样率为44.1kHz, 而WM8978支持的采用率
中没有44.1kHz, 所以我们需要把存放在SD卡里的音乐采样率转换成WM8978支持的48kHz, 如果不转换成48kHz, 直接使用原始的44.1kHz进行播放, 播放的速度会快一些, 相当于1.088倍的速度播放,转换可以用音乐格式转换类的软件。本次实验使用的WAV格式音乐的采样率为48kHz,
量化位数为16位。WM8978需要配置的寄存器如下:
1.寄存器R0(00h) , 该寄存器用于控制WM8978的软复位, 写任意值到该寄存器地址, 即可实现WM8978的软复位。
2.寄存器R1(01h) , 该寄存器需要设置VMIDSEL(bit[1: 0]) 为2’ b11, 开启最快启动;BUFIOEN(bit2) 为1, 避免输入输出直接在WM8978内部环回; BIASEN(bit3) 为1, 模拟部分的放大器才会工作, 才可以听到声音, PLLEN(bit5) 为1使能WM8978内部PLL功能, 使WM8978内部的主时钟为12.288MHz。
3.寄存器R2(02h) , 该寄存器只需要设置ROUT1EN(bit8)、 LOUT1EN(bit7)为1, 使能耳机输出。
4.寄存器R3(03h) , 该寄存器要设置LOUT2EN(bit6), ROUT2EN(bit5), RMIXER(bit3),
LMIXER(bit2), DACENR(bit1)和DACENL(bit0)等6个位为1。 LOUT2EN和ROUT2EN, 设置为1, 使能喇叭输出; LMIXER和RMIXER设置为1, 使能左右声道混合器; DACENL和DACENR则是使能左右声道的DAC, 使数字音频信号转换为模拟音频信号。
5.寄存器R4(04h), 该寄存器要设置WL(bit[6:5])和FMT(bit[4:3]) 4个位。 WL(bit[6:5])用于设置字长(即设置音频数据有效位数), 00表示16位音频, 10表示24位音频; FMT(bit[4:3])用于设置音频接口数据传输格式, 我们设置为2’ b10, 使用I2S音频数据格式传输音频数据。
6.寄存器R6(06h) , 该寄存器的MS(bit0) 设置为1, 使WM8978工作在主模式下, 输出BCLK和LRC给FPGA。
7.寄存器R7(07h) , 该寄存器我们要设置采样率SR(bit[3:1]) 为000, 使用48kHz的采样率; 设置SLOWCLKEN(bit0) 为1, 使能零交叉功能。ALIENTEK PIONEER 开发板教程
8.寄存器R10(0Ah) , 该寄存器我们要设置DACOSR128(bit3)为1, DAC得到最好的SNR。
9.寄存器R43(2Bh) , 该寄存器我们只需要设置INVROUT2(bit4) 为1即可, 反转ROUT2输出, 更好的驱动喇叭。
10.寄存器R49(31h) , 该寄存器我们要设置SPKBOOST(bit2)和TSDEN(bit1)这两个位。SPKBOOST用于设置喇叭的增益, 我们设置为1(gain=+1.5) 以获得更大的声音; TSDEN用于设置过热保护, 设置为1(开启) 即可。
11.寄存器R50(32h) 和 R51(33h) , 这两个寄存器一个用于设置左声道(R50) , 另外一个用于设置右声道(R51) 。 我们只需要设置这两个寄存器的最低位为1即可, 将左右声道的DAC输出接入左右声道混合器里面, 才能在耳机/喇叭听到音乐。
12.寄存器R52(34h) 和R53(35h) , 这两个寄存器用于设置耳机音量, 同样一个用于设置左声道(R52) , 另外一个用于设置右声道(R53) 。 这两个寄存器的最高位(HPVU) 用于设置是否更新左右声道的音量, 最低6位用于设置左右声道的音量, 我们可以先设置好两个寄存
器的音量值, 最后设置其中一个寄存器最高位为1, 即可更新音量设置。
13.寄存器R54(36h) 和R55(37h) , 这两个寄存器用于设置喇叭音量, 同R52, R53设置一模一样, 这里就不细说了。以上, 就是我们用WM8978做播放器时的设置, 按照以上所述, 对各个寄存器进行相应的配置, 即可使用WM8978做播放器播放音乐了。
42.2 实验任务
本节实验任务是使用FPGA开发板实现从SD卡中读取存放的音乐, 并输出给WM8978实现音乐
播放器的功能。
42.3 硬件设计
音频WM8978接口部分的硬件设计与“音频环回实验” 完全相同, 请参考“音频环回实验”
中的硬件设计部分。
本实验中, 各端口信号的管脚分配如下表所示

表 42.3.1 音乐播放器实验管脚分配

1.png

2.png



42.4 程序设计
根据实验任务, 我们可以大致规划出系统的控制流程: FPGA首先通过控制接口配置WM8978相关的寄存器, 然后从SD卡中读取存放的音乐文件; 由于SD卡的操作时钟(50MHz) 和WM8978的音频时钟(12MHz) 为非同步时钟, 所以需要使用FIFO对数据进行缓存。 由此画出系统的功
能框图如下所示:ALIENTEK PIONEER 开发板教程

3.png

图 42.4.1 音乐播放器实验系统框图


由系统框图可知, FPGA部分包括六个模块, 顶层模块(top_audio_sd) 、 时钟分频模块(pll_clk) 、 SD卡控制模块(sd_ctrl_top) 、 fifo模块(fifo) 、 音频-SD交互模块(audio_sd_ctrl) 、 WM8978控制模块(wm8978_ctrl) 。 各模块功能如下:
顶层模块(top_audio_sd) : 完成对另外五个模块的例化, 并实现各模块控制及数据信号的交互。 其中时钟分频模块为WM8978音频芯片提供主时钟以及SD卡控制模块提供驱动时钟; 从SD中读取的音频数据送入fifo模块; 音频-SD交互模块实现WM8978与SD交互信号的处理。 顶层
模块的原理图如下图所示:

4.png

图 42.4.2 顶层模块原理图


由于不需要WM8978输入音频给FPGA, 所以aud_adcdat输入音频引脚未用到。ALIENTEK PIONEER 开发板教程时钟分频模块(pll_clk) : 时钟分频模块通过调用锁相环(PLL) IP核来实现, 总共输出3个时钟, 频率分别为50Mhz、 50Mhz(相位偏移180度) 和12Mhz时钟。 50Mhz时钟和50Mhz(相
位偏移180度) 时钟作为SD卡的驱动时钟, 12Mhz时钟作为WM8978传感器的主时钟。SD卡控制模块(sd_ctrl_top) : SD卡控制模块负责驱动SD卡, 输出音乐音频数据。 有关该模块的详细介绍请大家参考“SD卡读写测试实验” 章节。fifo模块(fifo) : SD输出音频数据时钟(50MHz) 和WM8978接收音频数据时钟(12MHz)不是同步时钟, 所以用FIFO实现跨时钟数据交互。 FIFO的宽度为16bit, 深度为1024。WM8978控制模块(wm8978_ctrl) : 用于WM8978的寄存器配置和WM8978输出的音频数据的接收处理以及FPGA发送的音频数据的发送处理。
音频-SD交互模块(audio_sd_ctrl) : 音频-SD交互模块用于控制从SD卡读取音频数据给FIFO, 防止FIFO写满或读空, 并对从FIFO读出的音频数据进行处理以送给WM8978进行播放。
顶层模块的代码如下:
1 module top_audio_sd(
2 //system clock
3 input sys_clk , // 时钟信号
4 input sys_rst_n , // 复位信号
5 6
//wm8978 interface
7 //audio interface(master mode)
8 input aud_bclk , // WM8978位时钟
9 input aud_lrc , // 对齐信号
10 input aud_adcdat , // 音频输入
11 output aud_mclk , // WM8978的主时钟(最大为12.288MHz)
12 output aud_dacdat , // 音频输出
13 //control interface
14 output aud_scl , // WM8978的SCL信号
15 inout aud_sda , // WM8978的SDA信号
16
17 //sd interface
18 output sd_clk , //SD卡SPI时钟
19 input sd_miso , //SD卡SPI主机输入数据脚ALIENTEK PIONEER 开发板教程
20 output sd_mosi , //SD卡SPI主机输出数据脚
21 output sd_cs //SD卡SPI片选
22
23 );
24
25 //parameter define
26 parameter START_ADDR = 17'd8256 ; // 音乐存放的起始地址
27 parameter AUDIO_SEC = 17'd79076; // 音乐占用的扇区数
28
29 //wire define
30 wire tx_done ; // 一次发送完成信号
31 wire clk_ref ; // SD卡操作时钟
32 wire clk_ref_180deg ; // SD卡180度反相时钟
33 wire rst_n ; // 复位信号
34 wire locked ; // 锁相环locked信号
35 wire sd_init_done ; // SD卡初始化完成
36 wire rd_start_en ; // 开始读出使能
37 wire [31:0] rd_sec_addr ; // 读SD卡扇区地址
38 wire rd_busy ; // 读忙信号
39 wire rd_val_en ; // 数据读取有效使能信号
40 wire [15:0] music_data ; // 音乐数据
41 wire [31:0] dac_data ; // 音频数据
42 wire [ 9:0] wrusedw_cnt ; // fifo内剩余写入的字数
43 wire [15:0] rd_val_data ; // 读数据
44
45 //*****************************************************
46 //** main code
47 //*****************************************************
48
49 assign rst_n = sys_rst_n & locked;
50
51 //例化锁相环
52 pll_clk u_pll_clk(
53 .areset (~sys_rst_n ), // 复位信号, 高电平有效ALIENTEK PIONEER 开发板教程
54 .inclk0 (sys_clk ), // 输入时钟信号
55 .c0 (clk_ref ), // SD驱动时钟
56 .c1 (clk_ref_180deg), // SD驱动时钟反相
57 .c2 (aud_mclk ), // WM8978的主时钟
58 .locked (locked )
59 );
60
61 //SD卡控制模块
62 sd_ctrl_top u_sd_ctrl_top(
63 .clk_ref (clk_ref ), // 时钟信号(25Mhz)
64 .clk_ref_180deg (clk_ref_180deg), // 时钟信号, 与sd_clk相位相差180度
65 .rst_n (rst_n ), // 复位信号, 低电平有效
66 //SD卡接口
67 .sd_clk (sd_clk ), // SD卡输出时钟
68 .sd_miso (sd_miso ), // SD卡SPI 主机输入数据脚
69 .sd_mosi (sd_mosi ), // SD卡SPI 主机输出数据脚
70 .sd_cs (sd_cs ), // SD卡SPI 片选
71 //用户写SD卡接口
72 .wr_start_en (0), // 开始写入数据信号
73 .wr_sec_addr (0), // 写数据扇区地址
74 .wr_data (0), // 写数据
75 .wr_req (), // 写请求数据信号
76 .wr_busy (), // 写忙信号
77 //用户读SD卡接口
78 .rd_sec_addr (rd_sec_addr ), // 读数据扇区地址
79 .rd_start_en (rd_start_en ), // 开始读取数据信号
80 .rd_busy (rd_busy ), // 读忙信号
81 .rd_val_en (rd_val_en ), // 读数据有效信号使能
82 .rd_val_data (rd_val_data ), // 读数据
83 .sd_init_done (sd_init_done ) // SD卡初始化完成
84 );
85
86 //例化fifo模块
87 fifo u_fifo(ALIENTEK PIONEER 开发板教程
88 .data (rd_val_data), // 写入fifo的数据
89 .rdclk (aud_bclk ), // 读fifo的时钟
90 .rdreq (tx_done ), // 读fifo的请求信号
91 .wrclk (sd_clk ), // 写fifo的时钟
92 .wrreq (rd_val_en ), // 写fifo的请求信号
93 .q (music_data[15:0]), // 从fifo读出的数据
94 .wrusedw(wrusedw_cnt) // fifo内剩余写入的数据
95 );
96
97 //例化WM8978模块
98 wm8978_ctrl u_wm8978_ctrl(
99 //system clock
100 .clk (sys_clk ), // 时钟信号
101 .rst_n (rst_n ), // 复位信号
102 //wm8978 interface
103 //audio interface(master mode)
104 .aud_bclk (aud_bclk ), // WM8978位时钟
105 .aud_lrc (aud_lrc ), // 对齐信号
106 .aud_adcdat (), // 音频输入
107 .aud_dacdat (aud_dacdat ), // 音频输出
108 //control interface
109 .aud_scl (aud_scl ), // WM8978的SCL信号
110 .aud_sda (aud_sda ), // WM8978的SDA信号
111 //user interface
112 .adc_data (), // 接收的音频数据
113 .dac_data (dac_data ), // 输出的音频数据
114 .rx_done (), // 一次接收完成
115 .tx_done (tx_done ) // 一次发送完成
116 );
117
118 //例化audio_sd_ctrl, 用于实现SD卡和WM8978的音频交互
119 audio_sd_ctrl #(
120 .START_ADDR (START_ADDR), // 音乐存放的起始地址
121 .AUDIO_SEC (AUDIO_SEC ) // 音乐占用的扇区数ALIENTEK PIONEER 开发板教程
122 ) u_audio_sd_ctrl(
123 //system clock
124 .sd_clk (sd_clk ), // SD卡时钟信号
125 .aud_bclk (aud_bclk ), // WM8978位时钟信号
126 .rst_n (rst_n ), // 复位信号
127 //user interface
128 .sd_init_done (sd_init_done), // SD卡初始化完成
129 .rd_busy (rd_busy ), // 读忙信号
130 .tx_done (tx_done ),
131 .music_data (music_data ), // 音乐数据
132 .wrusedw_cnt (wrusedw_cnt ), // fifo内剩余写入的字数
133 .rd_start_en (rd_start_en ), // 开始读出使能
134 .rd_sec_addr (rd_sec_addr ), // 读SD卡扇区地址
135 .dac_data (dac_data ) // 音频数据
136 );
137
138 endmodule
顶层模块中主要完成对其余模块的例化。在代码的第26行和第27行分别定义了音乐存放的起始地址参数START_ADDR和音乐占用的扇区数AUDIO_SEC。 音乐存放的起始地址参数START_ADDR可以使用WinHex软件工具获得,该工具位于开发板所随附的资料中“6_软件资料/1_软件/WinHex” 目录下, 双击“WinHex.exe” 或者“WinHex64.exe” 打开软件。 软件打开后,单击“工具” 菜单, 然后单击“打开磁盘(D) …” , 如下图所示:

5.png

图 42.4.3 打开WinHex步骤




6.png

图 42.4.4 打开WinHex步骤


单击“打开磁盘(D) …” 后弹出“选择磁盘” 界面, 在“物理驱动器” 下, 我们看到箭头2所指的地方有RM2、 SD/MMC(7.5GB, USB) 的字样, 可知该物理驱动器对应的是SD卡, 标号为RM2, 我们找到标号RM2在逻辑驱动器中的位置, 即箭头1所指的地方, 单击进入如图 42.4.5
所示界面。 在该界面我们找到想要播放的音乐, 对应第一扇区所在列也就是箭头所指的数据8256为该音乐存放在SD中的地址, 故START_ADDR为8256。 知道了音乐存放在SD中的起始地址,我们还需要知道音乐占用SD卡的扇区数AUDIO_SEC。 首先我们通过查看该音乐文件的属性找到
该音乐文件的大小(以字节计) , 如图 42.4.6所示, 大小为40486446字节, 而SD每扇区的字节数为512字节, 所以共占用40486446/512ൎ79076个扇区, 所以AUDIO_SEC为79076。

7.png

图 42.4.5 查看存放起始扇区




8.png

图 42.4.6 查看音乐文件大小


WM8978控制模块在“音频环回实验” 中已经作了详细的介绍, SD卡控制模块sd_ctrl在“SD读写测试实验” 也已经作了详细的介绍, 对于这两个模块有不清楚的请大家分别参考对应的实验章节
音频-SD交互模块代码如下所示:
1 module audio_sd_ctrl(
2 //system clock
3 input sd_clk , // SD卡时钟信号
4 input aud_bclk , // WM8978位时钟信号
5 input rst_n , // 复位信号
6 //user interface
7 input sd_init_done , // SD卡初始化完成
8 input rd_busy , // 读忙信号
9 input tx_done ,
10 input [15:0] music_data , // 音乐数据
11 input [ 9:0] wrusedw_cnt , // fifo内剩余写入的字数
12 output reg rd_start_en , // 开始读出使能
13 output reg [31:0] rd_sec_addr , // 读SD卡扇区地址
14 output reg [31:0] dac_data // 音频数据
15 );
16
17 //parameter define
18 parameter START_ADDR = 15'd8448 ; // 音乐存放的起始地址
19 parameter AUDIO_SEC = 17'd104422; // 音乐占用的扇区数
20
21 //reg define
22 reg [ 1:0] flow_cnt ; // 状态流计数
23 reg [16:0] rd_sec_cnt ; // 读扇区次数计数器
24 reg rd_busy_d0 ; // 读忙信号打拍d0
25 reg rd_busy_d1 ; // 读忙信号打拍d1
26
27 //wire define
28 wire neg_rd_busy ; // 读忙信号的下降沿
29
30 //*****************************************************
31 //** main code
32 //*****************************************************
33
34 assign neg_rd_busy = rd_busy_d1 & (~rd_busy_d0); // 采读忙信号下降沿
35
36 //音频处理
37 always @(posedge aud_bclk or negedge rst_n) begin
38 if(!rst_n) begin
39 dac_data <= 31'd0;
40 end
41 else if(tx_done)
42 dac_data[15:0] <= {music_data[7:0],music_data[15:8]};
43 end
44
45 //打拍采上升沿
46 always @(posedge sd_clk or negedge rst_n) begin
47 if(!rst_n) begin
48 rd_busy_d0 <= 1'b0;ALIENTEK PIONEER 开发板教程
49 rd_busy_d1 <= 1'b0;
50 end
51 else begin
52 rd_busy_d0 <= rd_busy;
53 rd_busy_d1 <= rd_busy_d0;
54 end
55 end
56
57 //SD扇区地址变更
58 always @(posedge sd_clk or negedge rst_n) begin
59 if(!rst_n) begin
60 rd_sec_addr <= 32'd0;
61 end
62 else if(rd_sec_addr <= START_ADDR + AUDIO_SEC)
63 rd_sec_addr <= rd_sec_cnt + START_ADDR;
64 end
65
66 //读取音频数据
67 always @(posedge sd_clk or negedge rst_n) begin
68 if(!rst_n) begin
69 flow_cnt <= 2'b0;
70 rd_sec_cnt <= 17'd0;
71 end
72 else begin
73 rd_start_en <= 1'b0;
74 case(flow_cnt)
75 2'd0: begin
76 if(sd_init_done == 1'b1) begin
77 flow_cnt <= flow_cnt + 1'd1;
78 rd_start_en <= 1'b1;
79 end
80 end
81 2'd1: begin
82 //读忙信号下降沿说明单次读出结束, 开始读取下一扇区地址数据
83 if(rd_sec_cnt < AUDIO_SEC) begin
84 if(neg_rd_busy) begin
85 rd_sec_cnt <= rd_sec_cnt + 17'd1;
86 flow_cnt <= flow_cnt + 1'd1;
87 end
88 end
89 else begin
90 rd_sec_cnt <= 17'd0;
91 flow_cnt <= 2'd0;
92 end
93 end
94 2'd2: begin
95 if(wrusedw_cnt <= 10'd255) begin
96 rd_start_en <= 1'b1;
97 flow_cnt <= 2'd1;
98 end
99 end
100 default: flow_cnt <= 2'd0;
101 endcase
102 end
103 end
104
105 endmodule
代码第37行的always语句块我们对音乐数据进行了处理, 交换了SD输出的16位音频的高低字节的顺序。 这是因为WAV音频格式存放音频数据是以低字节在前, 高字节在后的方式存放的,而I2S是先输出音频的高字节。代码第95行当FIFO内部的字节数少于256时就从SD中读取音频数据, 写入FIFO。 这样避免FIFO写满和读空。
图 42.4.7是音频-SD交互模块运行过程中SignalTap抓取的波形图。 从图中可以看到当FIFO中的数据等于255时, 就使能开始读SD信号rd_start_en, 过一个时钟后SD卡读忙信号rd_busy拉高。 对比music_data信号和dac_data信号, 可以看到其低16位的高低字节进行了交换。
9.png

图 42.4.7 SignalTap波形图

I2C配置模块在“音频环回实验” 已经详细介绍, 本实验主要是更改寄存器的配置, 寄存
器配置部分的代码如下:
95 case(init_reg_cnt)
96 // R0,软复位
97 5'd0 : i2c_data <= {7'd0 ,9'b1 };
98 // R1,设置BUFIOEN = 1,VMIDSEL[1:0]设置为:11(5K),BIASEN设置为1(模拟器工作)
99 5'd1 : i2c_data <= {7'd1 ,9'b0_0000_1111};
100 // R3,LOUT2,ROUT2输出使能(喇叭工作),RMIX,LMIX,DACENR、 DACENL使能
101 5'd2 : i2c_data <= {7'd3 ,9'b0_0110_1111};
102 // R4,配置wm8978音频接口数据为I2S格式, 字长度(wl)
103 5'd3 : i2c_data <= {7'd4 ,{2'd0,wl,5'b10000}};
104 // R6,设置为MASTER MODE(BCLK和LRC输出)
105 5'd4 : i2c_data <= {7'd6 ,9'b0_0000_0001};
106 // R7,使能slow clock已使用零交叉(bit0) SR(bit3: 1)
107 5'd5 : i2c_data <= {7'd7 ,9'b0_0000_0101};
108 // R10,关闭SOFTMUTE(bit6) ,128x采样(bit4) ,最佳SNR
109 5'd6 : i2c_data <= {7'd10,9'b0_0000_1000};
110 // R43,INVROUT2反向,驱动喇叭
111 5'd7 : i2c_data <= {7'd43,9'b0_0001_0000};
112 // R49,TSDEN,开启过热保护
113 5'd8 : i2c_data <= {7'd49,9'b0_0000_0010};
114 // R50,选择左DAC输出至左输出混合器(bit0)
115 5'd9 : i2c_data <= {7'd50,9'b1 };
116 // R51,选择右DAC输出至右输出混合器(bit0)
117 5'd10: i2c_data <= {7'd51,9'b1 };
118 // R52,耳机左声道音量设置(bit5: 0) , 使能零交叉(bit7)
119 5'd11: i2c_data <= {7'd52,{3'b010,PHONE_VOLUME}};
120 // R53,耳机右声道音量设置(bit5: 0) , 使能零交叉(bit7) ,同步更新(HPVU=1)
121 5'd12: i2c_data <= {7'd53,{3'b110,PHONE_VOLUME}};
122 // R54,喇叭左声道音量设置(bit5: 0) , 使能零交叉(bit7)
123 5'd13: i2c_data <= {7'd54,{3'b010,SPEAK_VOLUME}};
124 // R55,喇叭右声道音量设置(bit5: 0) , 使能零交叉(bit7) ,同步更新(SPKVU=1)
125 5'd14: i2c_data <= {7'd55,{3'b010,SPEAK_VOLUME}};
126 // R3,使能左右输出通道混合器和DAC
127 5'd15 : i2c_data <= {7'd3 ,9'b0_0000_1111};
128 // R2,ROUT1,LOUT1输出使能(耳机可以工作)
129 5'd16 : i2c_data <= {7'd2 ,9'b1_1000_0000};
130 default:;
131 endcase
42.5 下载验证
首先我们打开WM8978音乐播放器实验工程, 在工程所在的路径下打开top_audio_sd/par文件夹, 在里面找到“top_audio_sd.qpf” 并双击打开。 注意工程所在的路径名只能由字母、数字以及下划线组成, 不能出现中文、 空格以及特殊字符等。 工程打开后如图 42.5.1所示。

10.png

图 42.5.1 音乐播放器实验工程


接下来我们将SD卡适配器(用于插入MicroSD卡) 或者SD卡插入开发板的SD卡插槽, 注意带有金属引脚的一面朝上; 然后将下载器一端连电脑, 另一端与开发板上对应端口连接, 并将耳机连接至WM8978的PHONE接口, 最后连接电源线并打开电源开关。
开拓者开发板实物图如下所示:

11.png

图 42.5.2 开发板实物图


接下来我们下载程序, 验证FPGA从SD卡读取音乐并输出给WM8978播放的功能。工程打开后通过点击工具栏中的“Programmer” 图标打开下载界面, 通过“Add File” 按钮选择top_audio_sd/par/output_files目录下的“top_audio_sd.sof” 文件。 开发板电源打开后, 在程序下载界面点击“Hardware Setup” , 在弹出的对话框中选择当前的硬件连接为“USB-Blaster[USB-0]” 。 然后点击“Start” 将工程编译完成后得到的sof文件下载到开发板中, 如图 42.5.3所示。
12.png


图 42.5.3 程序下载界面


下载完成后可以听到喇叭播放音乐, 戴上耳机, 也能听到音乐, 说明音乐播放器实验程序下载验证成功。

阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 21:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表