## 音频接口实验
### 1. 实验内容
通过本实验初步了解音频基本知识,熟悉音频芯片的工作原理。
本实验要求:将从计算机输出的音频信号,通过音频接口输入到音频芯片UDA1341TS的输入端,通过UDA1341TS转为数字信号,并将其发送给FPGA,然后通过FPGA将该数字信号再次发送给UDA1341TS,UDA1341TS将该信号通过D/A后输出,实验者可以通过耳机听到输出的音频信号。
\\
\\
### 2. 实验原理
#### 2.1 音频芯片与FPGA的连接
音频芯片与FPGA的连接如图 17 1所示,这里需要关心的是与FPGA相连的芯片引脚。
AUDIO_DI, //FPGA输出给UDA1341TS的数字音频信号。
AUDIO_DO, //UDA1341TS输出给FPGA的数字音频信号。
AUDIO_WSEL, //左右声道选择线,该信号和上面的信号组成I2S接口。
AUDIO_BCLK, //与数字音频对应的时钟,频率为:(fs)×(AD的位数)。
AUDIO_L3_MODE, //L3接口模式选择信号。
AUDIO_L3_CLK, //L3接口时钟信号。
AUDIO_L3_DATA, //L3接口数字信号。
AUDIO_SYSCLK //UDA1341TS工作的整个时钟信号。
{{ :图17-1.png |图17-1 UDA1341TS与FPGA连接方式}}
**图17-1 UDA1341TS与FPGA连接方式**
#### 2.2 音频芯片UDA1341TS的基本工作流程
图 17 2为UDA1341TS的系统框图,在此仅介绍本实验中音频信号的流向。在本实验中从音频接口输入的信号从2和4引脚输入到UDA1341TS内部,注意的是因为音频是分左右声道的,所以需要2和4两个引脚,然后通过一个可供选择的6dB放大器输入到UDA1341TS内部的ADC1输入端。AD变换后的数字音频通过DIGITAL MIXER(数字混频器)和DECIMATION FILTER(数字滤波器)通过AUDIO_DO(也即是18引脚),连接到FPGA。
FPGA通过AUDIO_DI(也即是19引脚)将上面输入的数字音频环回给UDA1341TS,该数字音频信号通过DSP FEATURES(数字信号处理)、INTERPOLOATION FILTER和NOISE SHAPER,最后到达UDA1341TS的DAC的输入端,完成最后的数字音频向模拟音频的转换,可以通过耳机听到该模拟音频。这中间数字信号处理模块可以完成很多特效功能,如去加重、低音增强等,可以通过设置UDA1341TS内部的DATA0寄存器来达到这些声音特效。
{{ :图17-2.png |图17-2 UDA1341TS系统框图}}
**图17-2 UDA1341TS系统框图**
#### 2.3 UDA1341TS的控制接口时序
FPGA和UDA1341TS之间的控制接口是L3接口, L3接口的时序如图 17 3所示。
{{ :图17-3.png |图17-3 L3控制接口时序}}
**图17-3 L3控制接口时序**
L3接口的时序和I2C类似,也是先发送地址,然后发送要写入该地址的数据。L3通信协议多了一个L3MODE信号指示信号线,当L3MODE为低电平时,发送的为地址信号,当L3MODE为高时发送的是数据信号。
UDA1341TS有direct addressing registers和extended addressing registers两种地址之分,direct addressing registers只需要一个8位主地址信号,而extended addressing registers不仅需要一个主地址信号,还需要一个子地址信号。
direct addressing registers:
在读写direct addressing registers时,只需要首先发送direct addressing registers的地址,然后即可发送要写入该寄存器的数据。该地址为8位,该8位地址信号的高6位为固定数值:000101,而通过低2位来选择具体需要设置的寄存器。UDA1341TS的寄存器有3个:DATA0、DATA1和STATUS。而direct addressing registers模式只针对寄存器STATUS。
extended addressing registers:
在控制extended addressing registers时,首先需要发送direct addressing registers的地址,然后发送extended addressing registers的地址,最后才是发送数据。该模式主要针对DATA0寄存器。
具体操作就是在发送8位信号00010100后,还需要发送8位信号:11000xxx,其中3个x表示扩展子地址,最后在发送8位信号:111xxxxx,其中5个x表示需要向该扩展位写入的数据。具体操作如图 17 4所示。
在发送direct addressing registers时,需要将L3MODE信号线拉低,而在发送子地址和发送数据时需要将L3MODE信号线拉高。
{{ :图17-4.jpg |图17-4 extended addressing registers所需要的子地址及数据}}
**图17-4 extended addressing registers所需要的子地址及数据**
本实验中为了程序简单起见,将direct addressing registers和extended addressing registers两种地址模式归一化处理,对于direct addressing registers模式通过发送两次相同数据,使其工作方式和extended addressing registers相同。
\\
\\
### 3. 程序设计
#### 3.1 总体架构
整个程序基本由3个模块(图 17-5)组成:
1. Sound是顶层模块,将各子模块实例化并连接各模块。
2. L3_AUD_Control模块依次将24位宽的控制字传递给L3_Control模块。
3. L3_Control模块将L3_AUD_Control送来的24位宽的控制字转为L3接口时序对UDA1341TS进行配置。
4. Counter模块产生音频芯片所需要的AUDIO_WSEL和AUDIO_BCK。
5. PLL模块产生音频芯片需要的AUDIO_SYSCLK。
{{ :图17-5.png |图17-5 L3控制接口时序}}
**图17-5 L3控制接口时序**
#### 3.2 sound模块(sound.v)
sound模块是顶层模块,负责连接各子模块。在整个设计中最重要的就是各个时钟之间关系,也就是AUDIO_WSEL、AUDIO_BCLK和 AUDIO_SYSCLK之间的对应关系。
在介绍该三个时钟信号之间关系之前需要介绍一下声音采样频率fs的概念。在此fs选择为44.1kHz,44.1kHz的采样频率是很多音频标准中使用的采样频率,其源于著名的“乃奎斯特采样定理”。“乃奎斯特采样定理”指出,在模拟信号数字化过程中,如果保证取样频率大于模拟讯号最高频率的2倍,就能100%精确地再还原出原始的模拟信号。音频的最高频率为20kHz,所以取样率至少应该大于40kHz,为了留一点安全系数,再考虑到工程上的习惯,很多标准最终选择了44.1kHz这个值。
UDA1341TS的Datasheet中提到UDA1341TS的系统时钟AUDIO_SYSCLK可以可以通过设置STATUS寄存器的SC位选择是256fs、384fs,还是512fs。本实验中选择256fs,由于声音的采样频率为44.1Khz,所以UDA1341TS的系统时钟AUDIO_SYSCLK为11289.6Khz。该时钟是用FPGA内部的PLL(audio_pll模块)完成。
对于时钟AUDIO_BCLK,其对应的主要是AD后的数字音频的比特率。如果采样频率为fs,而采样量化比特为16bit,同时因为分为左右声道,也就是一个采用量化点需要32比特,所以AUDIO_BCLK应该为32fs,那么AUDIO_BCLK可以通过对UDA1341TS的系统时钟AUDIO_SYSCLK进行32分频得到。
对于AUDIO_WSEL信号,其用于I2S的左右声道选择,每一个采样点分为左右声道,也就是每个采样点为一个时钟周期,对应的AUDIO_WSEL的频率为fs,可以通过对AUDIO_SYSCLK进行256分频得到。严格意义上,采用触发器搭建的时钟有很大的抖动,但是对于低频信号影响不大。
程序如下所示:
//PLL产生时钟AUDIO_SYSCLK
audio_pll audio_pll_inst (.inclk0 ( CLOCK_50 ),.c0 ( AUDIO_SYSCLK ));
//AUDIO_BCLK:AUDIO_SYSCLK 的8分频
assign AUDIO_BCLK = counter[2];
//AUDIO_WSEL:AUDIO_SYSCLK的256分频
assign AUDIO_WSEL = counter[7];
reg [7:0] counter;
always@(posedge AUDIO_SYSCLK or negedge Reset) begin
if(!Reset) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
#### 3.3 L3_AUD_Control模块(L3_AUD_Control.v)
L3_AUD_Control模块依次将24位宽的控制字传递给L3_Control模块。L3接口中的L3CLOCK的频率无特殊的要求,未要求和UDA1341TS的系统时钟AUDIO_SYSCLK成为分频或者同步关系,是完全独立的时钟。
L3_AUD_Control模块接口为:
iReset_n, //异步复位
iClk, // AUDIO_SYSCLK时钟的256分频所得
oL3_mode, //地址还是数据选择信号
oL3_clk, //L3时钟信号
oL3_data //L3数据信号
其核心程序为:
parameter L3_size = 9;
always@(posedge iClk or negedge iReset_n)
begin
if(!iReset_n)
begin
L3_index <= 0;//当前发送24位宽的控制字的序号
start <= 1; //控制L3_Control模块开始发送一个24位宽的数据
end
else
begin
if(mEnd == 1'b1) //mEnd为L3_Control模块返回的24位宽数据通过
//串行L3模块发送完成指示信号,收到该指示信号后,
//L3_index加一,发送下一个24位宽的控制字
begin
if(L3_index<=L3_size)
begin
L3_index <= L3_index + 1;
start <= 1;
end
else
begin
L3_index <= L3_index;
start <= 0;
end
end
end
endalways@(L3_index) begin
case(L3_index)
0:iData = {8'b00100001,8'b00100001,6'b000101,2'b10};//设置status寄存器使
//UDA1341TS得工作时钟为:256fs
1:iData = {8'b11100011,8'b11100011,6'b000101,2'b10};//设置status寄存器使
//UDA1341TS的ADC:on DAC:on
2:iData = {8'b00000000,8'b00000000,6'b000101,2'b00};//通过设置data0寄存器
//设置音量寄存器:Volume control
3:iData = {8'b10_100000,8'b10_100000,6'b000101,2'b00};//输入信号选择信号
4:iData = {8'b111_00000,8'b11000_000,6'b000101,2'b00};//DATAGITAL MIXER处:
//Channel 1输入的增益
5:iData = {8'b111_00000,8'b11000_001,6'b000101,2'b00};//DATAGITAL MIXER处:
//Channel 2输入的增益
6:iData = {8'b111_00001,8'b11000_010,6'b000101,2'b00};//选择输入信号input
//channel 1:on;input channel 2:off
7:iData = {8'b111_10010,8'b11000_100,6'b000101,2'b00};//设置data0使输入端的
//AGC开启
8:iData = {8'b111_00001,8'b11000_101,6'b000101,2'b00};//data0 channel 1 gain
9:iData = {8'b111_00000,8'b11000_110,6'b000101,2'b00};//设置输出自动增益开启
default:iData = 24'h0;
endcase
end
L3_Control模块将L3_AUD_Control模块传递过来的24位宽的数据发送完成后,将返回一个mEnd,则L3_AUD_Control模块根据该信号使L3_index加一,以发送下一个24位宽数据,直至将待设置数据发送完。
#### 3.4 L3 _Control模块(L3 _Control.v)
L3_Control模块将L3_AUD_Control传递过来的24位宽的数据严格按照图 17 3所示的时序对UDA1341TS进行配置。其接口为:
iReset_n, //异步复位
iClk, //AUDIO_SYSCLK时钟的256分频所得
iData, //L3_AUD_Control传递过来的24位宽的数据
iStart, // L3_AUD_Control传递过来的24位宽的数据开始传输信号
oEnd, // L3 _Control传递给L3_AUD_Control的24位宽的数据传输完毕信号
oL3_mode, //地址还是数据选择信号
oL3_clk, //L3时钟信号
oL3_data //L3数据信号
程序如下:
reg [5:0] counter;
always@(posedge iClk or negedge iReset_n) begin
if(!iReset_n)
begin
counter<=0; //计数器counter作为状态机控制L3接口的时序,一次操作大概需要53个L3接口周期。
end
else begin
if(counter<53) begin
counter <= counter + 1;
end
else begin
counter <= 0;
end
end
end
//按照图 17 3所示时序产生相应的L3接口时序
always@(posedge iClk or negedge iReset_n ) begin
if( !iReset_n ) begin
oEnd <= 0;
oL3_data <= 1;
oL3_clk <= 1;
end
else begin
oEnd <= 0;
if( iStart ) begin
case(counter)
//counter从0~19送出地址。
0:begin oL3_clk<=1; oL3_mode<=1; end
1:begin oL3_clk<=1; oL3_mode<=0; end
2:begin oL3_clk<=0; oL3_data <= iData[0]; end
3:begin oL3_clk<=1; end
4:begin oL3_clk<=0; oL3_data <= iData[1]; end
5:begin oL3_clk<=1; end
6:begin oL3_clk<=0; oL3_data <= iData[2]; end
7:begin oL3_clk<=1; end
8:begin oL3_clk<=0; oL3_data <= iData[3]; end
9:begin oL3_clk<=1; end
10:begin oL3_clk<=0; oL3_data <= iData[4]; end
11:begin oL3_clk<=1; end
12:begin oL3_clk<=0; oL3_data <= iData[5]; end
13:begin oL3_clk<=1; end
14:begin oL3_clk<=0; oL3_data <= iData[6]; end
15:begin oL3_clk<=1; end
16:begin oL3_clk<=0; oL3_data <= iData[7]; end
17:begin oL3_clk<=1; end
18:begin oL3_clk<=1; oL3_mode<=1; end
19:begin oL3_clk<=1; end
//counter从20到37送出数据或者子地址。
20:begin oL3_clk<=0; oL3_data <= iData[8]; end
21:begin oL3_clk<=1; end
22:begin oL3_clk<=0; oL3_data <= iData[9]; end
23:begin oL3_clk<=1; end
24:begin oL3_clk<=0; oL3_data <= iData[10]; end
25:begin oL3_clk<=1; end
26:begin oL3_clk<=0; oL3_data <= iData[11]; end
27:begin oL3_clk<=1; end
28:begin oL3_clk<=0; oL3_data <= iData[12]; end
29:begin oL3_clk<=1; end
30:begin oL3_clk<=0; oL3_data <= iData[13];end
31:begin oL3_clk<=1; end
32:begin oL3_clk<=0; oL3_data <= iData[14];end
33:begin oL3_clk<=1; end
34:begin oL3_clk<=0; oL3_data <= iData[15];end
35:begin oL3_clk<=1; end
36:begin oL3_clk<=1; oL3_mode<=0; end
37:begin oL3_clk<=1; oL3_mode<=1; end
////counter从28到53送出数据。
38:begin oL3_clk<=0; oL3_data <= iData[16];end
39:begin oL3_clk<=1; end
40:begin oL3_clk<=0; oL3_data <= iData[17];end
41:begin oL3_clk<=1; end
42:begin oL3_clk<=0; oL3_data <= iData[18];end
43:begin oL3_clk<=1; end
44:begin oL3_clk<=0; oL3_data <= iData[19];end
45:begin oL3_clk<=1; end
46:begin oL3_clk<=0; oL3_data <= iData[20]; end
47:begin oL3_clk<=1; end
48:begin oL3_clk<=0; oL3_data <= iData[21]; end
49:begin oL3_clk<=1; end
50:begin oL3_clk<=0; oL3_data <= iData[22];end
51:begin oL3_clk<=1; end
52:begin oL3_clk<=0; oL3_data <= iData[23];end
//通知L3_AUD_Control模块发送下一个24位宽数据
53:begin oL3_clk<=1; oEnd <= 1; oL3_data <= 1; end
default:begin oL3_clk<=1; oL3_mode<=1; oL3_data <= 1; end
endcase
end
end
end
\\
\\
### 4. 运行结果
通过signaltap看到程序的内部时序如图 17-6所示。
{{ :图17-6.jpg |图17-6 L3运行结果}}
**图17-6 L3运行结果**
可以看出,L3_MODE拉低的第一个周期内,接口发送的数据为:{6'b000101,2'b00},而在连续两个的L3_MODE拉高的周期内发送的的数据分别为:8'b111_00000,8'b11000_000。注意两点,第一,程序L3接口是数据低位先发送,第二,数据上升沿有效。从运行结果可以看出L3_MODE、L3_CLK和L3_DATA完全符合图3所示标准。
{{ :图17-7.png |图17-7 I2S运行结果}}
**图17-7 I2S运行结果**
图17-7所示为I2S音频时序,其中WSEL为左右声道选择信号,符合i2s数字音频接口标准定义。
\\
\\
### 5. 演示程序文件说明
|文件名|功能|
|sound.v|顶层模块。|
|L3_AUD_Contro.v|依次将24位宽的控制字传递给L3_Control模块。|
|L3 _Contro.v|产生L3接口时序。|
\\
\\
### 6. 演示程序使用
演示设备:核心板、扩展板、音频播放设备、音箱或耳机。
演示方法:将音频播放设备的音频输出连接到开发板的J4接口上,同时将音箱或耳机线连到开发板上的音频输出接口J5上。把程序下载到开发板上之后,通过耳机就可以听到音频播放设备播放的音频。