第七章 实战项目提升,完善简历
18.SD卡存放音频WAV播放(下)
进一步地我们再结合图1的示意图来分析wav_play模块的时序逻辑设计,大家可以清楚地看到WM8731在Right justified和主从时钟模式下,是先发左声道后发右声道数据的,这里需要强调4点也是之前困扰笔者很久的问题:
1. WAV格式的音频数据采用了16Bit双声道编码格式,相当于一个基本音频数据单元是32Bit,这也就是为什么要把SD卡扇区中读到的4个字节数据拼接到一起后存入FIFO中,主要是为了方便wav_play模块的数据拆分;
2. 在这个例程中我们选取的是Right justified、主时钟、默认32bit模式,注意到手册里明确写道在主时钟模式下,DACLRC/ADCLRC是输出64个BCLK时钟周期即32个BCLK高电平,32个BCLK低电平;
3. 在Right justified、默认32bit模式下,前32个BCLK对应DACLRC/ADCLRC的高电平,这时应该在后16个BCLK中输入左声道16位的数据,后32个BCLK对应对应DACLRC/ADCLRC的低电平,这时应该在后16个BCLK中输入右声道16位的数据,而对应的左右声道的前16个BCLK可以做补零操作,且DACDAT/ADCDAT的数据应该在BCLK时钟下降沿更新;
4. 注意到WAV下16 Bit双声道编码格式为:左声道数据低字节、左声道数据高字节、
右声道数据低字节、右声道数据高字节,而WM8731在Right justified、默认32bit模式下的编码格式为:左声道数据高字节、左声道数据低字节、右声道数据高字节、右声道数据低字节,所以需要在程序里把上游wav_query模块从FIFO中读到的32位数据进行数据拼接,重新组成左声道和右声道的数据。
图1 WM8731在Right justified和主从时钟模式下的说明
如表1所示是wav_play模块信号列表,在这个模块里我们先把上游wav_query模块从FIFO中读到的32位数据进行数据拆分处理,再拼接成32位的左声道数据和32位的右声道数据,最后按照Right justified、主时钟、默认32bit模式,在BCLK和ADCLRC的作用下把数据一位位地送到DACDAT上。
显然本模块的CLK对应50Mhz时钟比BCLK快很多,可以捕获到BCLK的时钟下降沿和ADCLRC的时钟上升沿,在这里wav_data和wav_data_vld分别对应上游模块的数据信号和数据指示信号,而wav_playrdy作为读准备信号例化到上游wav_query模块,激发FIFO的读使能信号,如图2所示是WAV音频播放模块的代码设计。
信号列表 | ||
信号名 |
I/O |
位宽 |
clk |
I |
1 |
rst_n |
I |
1 |
wav_bclk |
I |
1 |
wav_adclrc |
I |
1 |
wav_data |
I |
32 |
wav_data_vld |
I |
1 |
wav_playrdy |
O |
1 |
wav_dacdata |
O |
1 |
表1 wav_play模块信号列表
图2 WAV音频播放模块的代码设计
如表2和表3所示分别是config_wm8731和i2c_wm8731模块的信号列表,显然我们需要去初始化WM8731后,芯片才能按照我们的想法工作,其中config_wm8731模块存储了10个寄存器地址和参数值,i2c_wm8731模块则实现了整个IIC配置的时序逻辑,依次配置10个寄存器,完成IIC初始化后,通过i2c_config_done信号指示初始完成,如图3和4所示分别是2个模块的代码设计。
信号列表 | ||
信号名 |
I/O |
位宽 |
lut_index |
I |
4 |
lut_data |
O |
24 |
lut_size |
O |
4 |
表2 config_wm8731模块信号列表
信号列表 | ||
信号名 |
I/O |
位宽 |
clk |
I |
1 |
rst_n |
I |
1 |
i2c_config_size |
I |
4 |
i2c_config_data |
I |
24 |
i2c_sda |
I/O |
1 |
i2c_scl |
O |
1 |
i2c_config_index |
O |
4 |
i2c_config_done |
O |
1 |
表3 i2c_wm8731模块信号列表
图3 WM8731初始化寄存器值模块的代码设计
图4 WM8731初始化寄存器设置模块的代码设计
如图5所示是SD卡存放音频WAV播放顶层模块的代码设计,大家需要把各个模块的相关信号例化到一起,在这个例程中首先我们在SD卡中事先存储好了很多首WAV格式的歌曲,然后按下按键KEY1,FPGA会按照SD卡的扇区依次遍历各个扇区直到找到WAV文件头格式的歌曲,把WAV格式的音频数据按照WM8731配置的Right justified、主时钟、默认32bit模式播放歌曲,接着当播放完一首歌曲后,再按下按键KEY1,FPGA会再去查找SD卡扇区的下一首WAV文件头格式的歌曲,找到后继续播放下一首。
为了便于调试观察,这里笔者也把各个模块的关键信号添加到ILA IP核中,同时通过LED0点亮代表SD卡和WM8731均初始化完毕,通过LED1闪烁代表当前正在播放SD卡中的音频数据,插上SD卡后即可观察到板载的LED0点亮,按下按键KEY1很快板载的LED1会不断地闪烁,同时把耳机座子插到豌豆开发板的绿色耳机接口处,即可播放出动听美妙的战歌Counting Stars。
图5 SD卡存放音频WAV播放顶层模块的代码设计
SD卡音乐WM8731播放