通过本实验初步了解音频基本知识,熟悉音频芯片的工作原理。

本实验要求:将从计算机输出的音频信号,通过音频接口输入到音频芯片UDA1341TS的输入端,通过UDA1341TS转为数字信号,并将其发送给FPGA,然后通过FPGA将该数字信号再次发送给UDA1341TS,UDA1341TS将该信号通过D/A后输出,实验者可以通过耳机听到输出的音频信号。



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连接方式

图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系统框图

图17-2 UDA1341TS系统框图

2.3 UDA1341TS的控制接口时序

FPGA和UDA1341TS之间的控制接口是L3接口, L3接口的时序如图 17 3所示。 图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 extended addressing registers所需要的子地址及数据

图17-4 extended addressing registers所需要的子地址及数据

本实验中为了程序简单起见,将direct addressing registers和extended addressing registers两种地址模式归一化处理,对于direct addressing registers模式通过发送两次相同数据,使其工作方式和extended addressing registers相同。



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控制接口时序

图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



通过signaltap看到程序的内部时序如图 17-6所示。 图17-6 L3运行结果

图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运行结果

图17-7所示为I2S音频时序,其中WSEL为左右声道选择信号,符合i2s数字音频接口标准定义。



文件名功能
sound.v顶层模块。
L3AUDContro.v依次将24位宽的控制字传递给L3_Control模块。
L3 _Contro.v产生L3接口时序。



演示设备:核心板、扩展板、音频播放设备、音箱或耳机。

演示方法:将音频播放设备的音频输出连接到开发板的J4接口上,同时将音箱或耳机线连到开发板上的音频输出接口J5上。把程序下载到开发板上之后,通过耳机就可以听到音频播放设备播放的音频。