差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
book_excise_audio_if [2020/11/01 14:01]
gongyu 创建
book_excise_audio_if [2021/08/23 16:57] (当前版本)
zili
行 1: 行 1:
-### 音频接口实验+## 音频接口实验 
 + 
 +### 1. 实验内容 
 +通过本实验初步了解音频基本知识,熟悉音频芯片的工作原理。  
 + 
 +本实验要求:将从计算机输出的音频信号,通过音频接口输入到音频芯片UDA1341TS的输入端,通过UDA1341TS转为数字信号,并将其发送给FPGA,然后通过FPGA将该数字信号再次发送给UDA1341TS,UDA1341TS将该信号通过D/​A后输出,实验者可以通过耳机听到输出的音频信号。 
 + 
 +\\ 
 +\\ 
 +### 2. 实验原理 
 +#### 2.1 音频芯片与FPGA的连接 
 +音频芯片与FPGA的连接如图 17 1所示,这里需要关心的是与FPGA相连的芯片引脚。 
 +<code verilog>  
 + 
 +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工作的整个时钟信号。 
 + 
 +</​code>​ 
 + 
 +{{ :​图17-1.png |图17-1 UDA1341TS与FPGA连接方式}} 
 +<WRAP centeralign>​ 
 +**图17-1 UDA1341TS与FPGA连接方式** 
 +</​WRAP>​ 
 + 
 +#### 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系统框图}} 
 +<WRAP centeralign>​ 
 +**图17-2 UDA1341TS系统框图** 
 +</​WRAP>​ 
 + 
 +#### 2.3 UDA1341TS的控制接口时序 
 +FPGA和UDA1341TS之间的控制接口是L3接口, L3接口的时序如图 17 3所示。 
 +{{ :​图17-3.png |图17-3 L3控制接口时序}} 
 +<WRAP centeralign>​ 
 +**图17-3 L3控制接口时序** 
 +</​WRAP>​ 
 + 
 +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所需要的子地址及数据}} 
 +<WRAP centeralign>​ 
 +**图17-4 extended addressing registers所需要的子地址及数据** 
 +</​WRAP>​ 
 + 
 +本实验中为了程序简单起见,将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控制接口时序}} 
 +<WRAP centeralign>​ 
 +**图17-5 L3控制接口时序** 
 +</​WRAP>​ 
 + 
 +#### 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分频得到。严格意义上,采用触发器搭建的时钟有很大的抖动,但是对于低频信号影响不大。 
 + 
 +程序如下所示: 
 +<code verilog>  
 + 
 +//​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 
 + 
 +</​code>​ 
 + 
 +#### 3.3 L3_AUD_Control模块(L3_AUD_Control.v) 
 +L3_AUD_Control模块依次将24位宽的控制字传递给L3_Control模块。L3接口中的L3CLOCK的频率无特殊的要求,未要求和UDA1341TS的系统时钟AUDIO_SYSCLK成为分频或者同步关系,是完全独立的时钟。 
 + 
 +L3_AUD_Control模块接口为: 
 +<code verilog>  
 + 
 +  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 
 + 
 +</​code>​ 
 + 
 +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进行配置。其接口为: 
 +<code verilog>  
 + 
 + ​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 
 + 
 +</​code>​ 
 + 
 +\\ 
 +\\ 
 +### 4. 运行结果 
 +通过signaltap看到程序的内部时序如图 17-6所示。 
 +{{ :​图17-6.jpg |图17-6 L3运行结果}} 
 +<WRAP centeralign>​ 
 +**图17-6 L3运行结果** 
 +</​WRAP>​ 
 + 
 +可以看出,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运行结果}} 
 +<WRAP centeralign>​ 
 +**图17-7 I2S运行结果** 
 +</​WRAP>​ 
 + 
 +图17-7所示为I2S音频时序,其中WSEL为左右声道选择信号,符合i2s数字音频接口标准定义。 
 + 
 +\\ 
 +\\ 
 +### 5. 演示程序文件说明 
 + 
 +|文件名|功能| 
 +|sound.v|顶层模块。| 
 +|L3_AUD_Contro.v|依次将24位宽的控制字传递给L3_Control模块。| 
 +|L3 _Contro.v|产生L3接口时序。| 
 + 
 +\\ 
 +\\ 
 +### 6. 演示程序使用 
 +演示设备:核心板、扩展板、音频播放设备、音箱或耳机。 
 + 
 +演示方法:将音频播放设备的音频输出连接到开发板的J4接口上,同时将音箱或耳机线连到开发板上的音频输出接口J5上。把程序下载到开发板上之后,通过耳机就可以听到音频播放设备播放的音频。 
 + 
  
-#### 实验内容 
-#### 实验原理 
-#### 程序设计 
-#### 仿真结果 
-#### 演示程序文件说明 
-#### 演示程序使用