音频接口实验
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 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 UDA1341TS系统框图
2.3 UDA1341TS的控制接口时序
FPGA和UDA1341TS之间的控制接口是L3接口, L3接口的时序如图 17 3所示。
图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 extended addressing registers所需要的子地址及数据
本实验中为了程序简单起见,将direct addressing registers和extended addressing registers两种地址模式归一化处理,对于direct addressing registers模式通过发送两次相同数据,使其工作方式和extended addressing registers相同。
3. 程序设计
3.1 总体架构
整个程序基本由3个模块(图 17-5)组成: 1. Sound是顶层模块,将各子模块实例化并连接各模块。 2. L3AUDControl模块依次将24位宽的控制字传递给L3Control模块。 3. L3Control模块将L3AUDControl送来的24位宽的控制字转为L3接口时序对UDA1341TS进行配置。 4. Counter模块产生音频芯片所需要的AUDIOWSEL和AUDIOBCK。 5. PLL模块产生音频芯片需要的AUDIO_SYSCLK。
图17-5 L3控制接口时序
3.2 sound模块(sound.v)
sound模块是顶层模块,负责连接各子模块。在整个设计中最重要的就是各个时钟之间关系,也就是AUDIOWSEL、AUDIOBCLK和 AUDIO_SYSCLK之间的对应关系。
在介绍该三个时钟信号之间关系之前需要介绍一下声音采样频率fs的概念。在此fs选择为44.1kHz,44.1kHz的采样频率是很多音频标准中使用的采样频率,其源于著名的“乃奎斯特采样定理”。“乃奎斯特采样定理”指出,在模拟信号数字化过程中,如果保证取样频率大于模拟讯号最高频率的2倍,就能100%精确地再还原出原始的模拟信号。音频的最高频率为20kHz,所以取样率至少应该大于40kHz,为了留一点安全系数,再考虑到工程上的习惯,很多标准最终选择了44.1kHz这个值。
UDA1341TS的Datasheet中提到UDA1341TS的系统时钟AUDIOSYSCLK可以可以通过设置STATUS寄存器的SC位选择是256fs、384fs,还是512fs。本实验中选择256fs,由于声音的采样频率为44.1Khz,所以UDA1341TS的系统时钟AUDIOSYSCLK为11289.6Khz。该时钟是用FPGA内部的PLL(audio_pll模块)完成。
对于时钟AUDIOBCLK,其对应的主要是AD后的数字音频的比特率。如果采样频率为fs,而采样量化比特为16bit,同时因为分为左右声道,也就是一个采用量化点需要32比特,所以AUDIOBCLK应该为32fs,那么AUDIOBCLK可以通过对UDA1341TS的系统时钟AUDIOSYSCLK进行32分频得到。
对于AUDIOWSEL信号,其用于I2S的左右声道选择,每一个采样点分为左右声道,也就是每个采样点为一个时钟周期,对应的AUDIOWSEL的频率为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)
L3AUDControl模块依次将24位宽的控制字传递给L3Control模块。L3接口中的L3CLOCK的频率无特殊的要求,未要求和UDA1341TS的系统时钟AUDIOSYSCLK成为分频或者同步关系,是完全独立的时钟。
L3AUDControl模块接口为:
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
L3Control模块将L3AUDControl模块传递过来的24位宽的数据发送完成后,将返回一个mEnd,则L3AUDControl模块根据该信号使L3index加一,以发送下一个24位宽数据,直至将待设置数据发送完。
3.4 L3 _Control模块(L3 _Control.v)
L3Control模块将L3AUD_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 L3运行结果
可以看出,L3MODE拉低的第一个周期内,接口发送的数据为:{6'b000101,2'b00},而在连续两个的L3MODE拉高的周期内发送的的数据分别为:8'b11100000,8'b11000000。注意两点,第一,程序L3接口是数据低位先发送,第二,数据上升沿有效。从运行结果可以看出L3MODE、L3CLK和L3_DATA完全符合图3所示标准。
图17-7 I2S运行结果
图17-7所示为I2S音频时序,其中WSEL为左右声道选择信号,符合i2s数字音频接口标准定义。
5. 演示程序文件说明
文件名 | 功能 |
sound.v | 顶层模块。 |
L3AUDContro.v | 依次将24位宽的控制字传递给L3_Control模块。 |
L3 _Contro.v | 产生L3接口时序。 |
6. 演示程序使用
演示设备:核心板、扩展板、音频播放设备、音箱或耳机。
演示方法:将音频播放设备的音频输出连接到开发板的J4接口上,同时将音箱或耳机线连到开发板上的音频输出接口J5上。把程序下载到开发板上之后,通过耳机就可以听到音频播放设备播放的音频。