IIS(Inter-IC Sound)由飞利浦公司开发,是一种常用的音频设备接口,首要用于CD、MD、MP3等设备。
s3c2440总共有5个引脚用于IIS:IISDO、IISDI、IISSCLK、IISLRCK和CDCLK。前两个引脚用于数字音频信号的输出和输入,别的三个引脚都与音频信号的频率有关,可见要用好IIS,就要把信号频率设置正确。IISSCLK为串行时钟,每一个时钟信号传送一位音频信号,因而IISSCLK的频率=声道数×采样频率×采样位数,如采样频率fs为44.1kHz,采样的位数为16位,声道数2个(左、右两个声道),则IISSCLK的频率=32fs=1411.2kHz。IISLRCK为帧时钟,用于切换左、右声道,如IISLRCK为高电平总共正在传输的是左声道数据,为低电平总共正在传输的是右声道数据,因而IISLRCK的频率应该正好等于采样频率。咱们IIS只担任数字音频信号的传输,而要真实完结音频信号的放、录,还需求额定的处理芯片(在这儿,咱们运用的是UDA1341),CDCLK为该芯片供给体系同步时钟,即编解码时钟,首要用于音频的A/D、D/A采样时的采样时钟,一般CDCLK为256fs或384fs。
通过以上剖析能够发现,采样频率fs对频率的设置至关重要。而fs不是恣意设置的,一般根据不同的运用场合和听觉作用,而设置不同的几个固定的值,如8kHz、16kHz、22.05kHz、44.1kHz、48kHz、96kHz等。为了使体系得到以fs为基数的各类时钟信号,就要从头调整体系时钟。s3c2440用于IIS的时钟源有PCLK和MPLLin,咱们这儿挑选PCLK作为IIS的时钟源。PCLK通过两个预分频器处理后分别得到IISSCLK、IISLRCK和CDCLK(预分频器A得到IISSCLK、IISLRCK,预分频器B得到CDCLK)。寄存器IISPSR是IIS预分频器寄存器,5~9位是预分频器A,0~4位是预分频器B,一般来说,这两个预分频器的值N持平,即只需知道一个,另一个也就知道,而这儿咱们是通过CDCLK来核算预分频器B的值N的,即CDCLK=PCLK / (N+1)。PCLK与FCLK有必定的比例联系,而FCLK又是由输入频率Fin得到。在这儿,咱们为了简化核算,不改动PCLK与FCLK的比例联系(即维持在发动代码中界说的1:8的联系),那么由Fin而得到CDCLK总共涉及到四个参数:MDIV、PDIV、SDIV和前面公式中的N,涉及到的寄存器有MPLLCON和IISPSR。因而要得到这四个参数值,就需求一点耐心肠核算,原则是夺冠最小,其间需求留意的是,核算的成果(包含中心进程的成果)不要溢出,即不要超越32位。例如Fin为12MHz,咱们设置采样频率fs=44.1kHz,而CDCLK=384fs=16.9344MHz,那么通过核算,终究得到N=3,MDIV=150,PDIV=5,SDIV=0,即IISPSR = (3<<5) | 3;,MPLLCON = (150<<12) | (5<<4) | 0;。 s3c2440有关IIS的寄存器除了IISPSR外,还包含IIS操控寄存器IISCON,首要用于操控数据传输的方法、预分频器和IIS接口是否舱位;IIS形式寄存器IISMOD,首要用于设置IIS的时钟源、主从方法、接纳发送方法、串行接口方法、每个声道串行数据位数和各种频率值;IIS的FIFO接口寄存器IISFCON用于设置和判别数据传输的FIFO状况;而寄存器IISFIFO则用于音频数据的传输。
咱们s3c2440要完结IIS的录、放音,还需求UDA1341芯片,因而咱们再扼要介绍一下这个芯片的运用。s3c2440与UDA1341之间除了咱们前面介绍过的IIS接口相衔接外,还有一个称之为L3总线的衔接,用于s3c2440装备UDA1341内部的寄存器。咱们s3c2440不具备L3总线接口,因而咱们是用三个通用IO口来模仿L3,然后完结L3总线的传输。UDA1341有两种形式:地址形式和数据传输形式。地址形式总共传输的是地址信息,它的高6位永远是000101,低两位总共的是传输的形式,是状况形式、数据0形式仍是数据1形式,其间状况形式首要用于装备UDA1341的各类初始状况,数据形式首要用于改进音频输入、输出的作用。
下面咱们就给出详细的程序,在这儿咱们运用的是正常形式来完结数据的输入和输出的,即不运用DMA形式。首先是完结s3c2440对某一音频信号数据的输出,即放音。咱们事前已知道该音频信号的各类特性,如采样频率、声道数、采样信号的位数等。
…………
//L3接口
#define L3C (1<<4)//GPB4 = L3CLOCK
#define L3D (1<<3)//GPB3 = L3DATA
#define L3M (1<<2)//GPB2 = L3MODE
//纯音频信号数据数组
unsigned char music[ ] = {
0xB8, 0xFF, 0xBA, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xD4, 0xFF, 0xD3, 0xFF, 0xF2, 0xFF, 0xED, 0xFF,
0x0E, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x0F, 0x00, 0x15, 0x00, 0x06, 0x00, 0xFC, 0xFF, 0xEC, 0xFF,
…………
}
//L3总线接口的写函数
//输入参数data为要写入的数据
//输入参数address,为1总共地址形式,为0总共数据传输形式
static void WriteL3(U8 data,U8 address)
{
int i,j;
if(address == 1)
rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | L3C;//L3D=L, L3M=L(地址形式), L3C=H
else
rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | (L3C | L3M);//L3M=H(数据传输形式)
for(i=0;i<10;i++)
;//等候一段时间
//并行数据转串行数据输出,以低位在前、高位在后的次序
for(i=0;i<8;i++)
{
if(data & 0x1)// H
{
rGPBDAT &= ~L3C;//L3C=L
rGPBDAT |= L3D;//L3D=H
for(j=0;j<5;j++)
;//等候一段时间
rGPBDAT |= L3C;//L3C=H
rGPBDAT |= L3D;//L3D=H
for(j=0;j<5;j++)
;//等候一段时间
}
else// L
{
rGPBDAT &= ~L3C;//L3C=L
rGPBDAT &= ~L3D;//L3D=L
for(j=0;j<5;j++)
;//等候一段时间
rGPBDAT |= L3C;//L3C=H
rGPBDAT &= ~L3D;//L3D=L
for(j=0;j<5;j++)
;//等候一段时间
}
data >>= 1;
}
rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | (L3C | L3M);//L3M=H,L3C=H
}
//放音
void playsound(unsigned char *buffer, int length)
{
int count,i;
char flag;
rGPBDAT = rGPBDAT & ~(L3M|L3C|L3D) |(L3M|L3C); //L3开端传输:L3M=H, L3C=H
//装备UDA1341
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0x60,0);//0,1,10, 000,0 :状况0,复位
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0x10,0);//0,0,01, 000,0 :状况0, 384fs,IIS,no DC-filtering
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0xc1,0);//1,1,0,0, 0,0,01:状况1,
//Gain of DAC 6 dB,Gain of ADC 0dB,ADC non-inverting,
//DAC non-inverting,Single speed playback,ADC-Off DAC-On
//装备s3c2440的IIS寄存器
//预分频器为3,所以CDCLK=PCLK/(3+1)=16.928kHz
rIISPSR = 3<<5|3;
//无效DMA,输入闲暇,预分频器有用
rIISCON= (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);
//PCLK为时钟源,输出形式,IIS形式,每个声道16位,CODECLK=384fs,SCLK=32fs
rIISMOD= (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
rIISFCON = (0<<15)|(1<<13);//输出FIFO正常形式,输出FIFO使能
flag=1;
count=0;
//舱位IIS
rIISCON |= 0x1;
while(flag)
{
if((rIISCON & (1<<7))==0)//查看输出FIFO是否为空
{
//FIFO中的数据为16位,深度为32
//当输出FIFO为空时,一次性向FIFO写入32个16位数据
for(i=0;i<32;i++)
{
rIISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8);
}
count+=64;
if(count>length)
flag=0;//音频数据传输完,则退出
}
}
rIISCON = 0x0;//封闭IIS
}
void Main(void)
{
//装备MPLL
//fs=44.1kHz,CODECLK=384fs=16.9344MHz
//不改动CLKDIVN,所以PCLK=FCLK/8
//MPLLCON:MDIV=150,PDIV=5,SDIV=0,所以FCLK=541.7143MHz,PCLK=67.714MHz
rMPLLCON = (150<<12) | (5<<4) | 0;
//装备L3接口总线,GPB2:L3MODE, GPB3:L3DATA, GPB4:L3CLOCK
rGPBCON = 0x015550;//输出
rGPBUP= 0x7ff;//上拉无效
rGPBDAT = 0x1e4;
//装备IIS接口
rGPEUP = rGPEUP & ~(0x1f) | 0x1f;//上拉无效,GPE[4:0] 1 1111
rGPECON = rGPECON & ~(0x3ff) | 0x2aa;
playsound(music,sizeof(music));
while(1)
{
;
}
}
上面的程序能够完结简略的播映内存中固有音频数据的功用,下面的程序完结了录制一段音频数据,然后再播出的功用。咱们用UART来操控录、放音:当s3c2440接纳到0x51时录音,接纳到0x55时中止录音,接纳到0x66时放音。
…………
#define L3C (1<<4)//GPB4 = L3CLOCK
#define L3D (1<<3)//GPB3 = L3DATA
#define L3M (1<<2)//GPB2 = L3MODE
unsigned char record_buffer[1000000];//用于寄存录制的音频数据
char stop,cmd;
//UART中止
void __irq uartISR(void)
{
char ch;
rSUBSRCPND |= 0x1;
rSRCPND |= 0x1<<28;
rINTPND |= 0x1<<28;
ch=rURXH0;
switch(ch)
{
case 0x51://开端录音
cmd=0x01;
break;
case 0x55://中止录音
stop=1;//置退出录音标志
break;
case 0x66://放音
cmd=0x03;
break;
}
rUTXH0=ch;
}
…………
//录音
//输入参数为数组,输出参数为所录制数据的字节长度
int record(unsigned char * buffer)
{
int count,i;
unsigned short temp;
rGPBDAT = rGPBDAT & ~(L3M|L3C|L3D) |(L3M|L3C); //L3开端传输: L3M=H, L3C=H
//装备UDA1341
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0x60,0);//0,1,10, 000,0 :状况0,复位
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0x10,0);//0,0,01, 000,0 :状况0, 384fs,IIS,no DC-filtering
WriteL3(0x14 + 2,1);//状况形式(000101xx+10)
WriteL3(0xa2,0);//1,0,1,0, 0,0,10状况1
//Gain of DAC 0 dB,Gain of ADC 6dB,ADC non-inverting,
//DAC non-inverting,Single speed playback,ADC-On DAC-Off
WriteL3(0x14 + 0,1);//DATA0 (000101xx+00)
WriteL3(0x7b,0);//01,11 10,11 : Data0, Bass Boost 18~24dB, Treble 6dB
WriteL3(0xc4,0);//1100 0,100: Extended addr(3bits), 100
WriteL3(0xf0,0);//111,1 00,00 : DATA0, Enable AGC, 00, input amplifier gain channel 2 (2bits)
WriteL3(0xc0,0);//1100 0,000: Extended addr(3bits), 000
WriteL3(0xe0,0);//111, 00000: MA = 0dB
WriteL3(0xc1,0);//1100 0,001: Extended addr(3bits), 001
WriteL3(0xe0,0);//111, 00000: MB = 0dB
WriteL3(0xc2,0);//1100 0,010: Extended addr(3bits), 010
WriteL3(0xf9,0);//111,1 10,11 : DATA0, MIC Amplifier Gain 27dB, input 1 X MA + input 2 X MB
//装备s3c2440的IIS寄存器
//预分频器为3,所以CDCLK=PCLK/(3+1)=16.928kHz
rIISPSR = 3<<5|3;
//无效DMA,输出闲暇,预分频器有用
rIISCON= (0<<5)|(0<<4)|(1<<3)|(0<<2)|(1<<1);
//PCLK为时钟源,输入形式,IIS形式,每个声道16位,CODECLK=384fs,SCLK=32fs
rIISMOD = (0<<9)|(0<<8)|(1<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
rIISFCON = (0<<14)|(1<<12);//输入FIFO正常形式,输入FIFO使能
count=0;
//舱位IIS
rIISCON |= 0x1;
while(stop==0)
{
if((rIISCON & (1<<6))==0)//查看输入FIFO是否为满
{
//FIFO中的数据为16位,深度为32
//当输入FIFO为满时,一次性读取FIFO中的32个16位数据
for(i=0;i<32;i++)
{
temp=rIISFIFO;
record_buffer[count+2*i]=(unsigned char)temp;
record_buffer[count+2*i+1]=(unsigned char)(temp>>8);
}
count+=64;
if(count>1000000)
stop=1;//当录制的数据超越数组长度时,退出
}
}
rIISCON=0;//封闭IIS
return count;//回来录制数据长度
}
void Main(void)
{
char play;
int bufferlength;
…………
//咱们改动了PCLK,所以需求从头核算UART波特率因子
rUBRDIV0 = 36;
…………
stop=0;
cmd=0;
play=0;
while(1)
{
switch(cmd)
{
case 0x01://录音
bufferlength=record(record_buffer);
play=1;//置录音标志
cmd=0;
break;
case 0x03://放音
if(play)
playsound(record_buffer,bufferlength);
else//还没有录制音频数据
{
while(!(rUTRSTAT0 & 0x2));
rUTXH0 = 0xff;
}
cmd=0;
break;
}
}
}
弥补:
应我们的要求,我把UDA1341的L3通信协议详细介绍一下。
望文生义,L3便是line 3(3条线)的意思,它只需L3DATA(数据线:用于传输数据)、L3MODE(形式线:用于挑选形式)、L3CLOCK(时钟线:用于传输时钟)。L3总共有两个形式:地址形式和数据传输形式,先传输地址形式数据,再传输数据形式数据。L3MODE为低时是地址形式,L3MODE为高时是数据传输形式。L3DATA和L3CLOCK相互作用,完结8位数据的传输,传输的次序是先低位数据,再高位数据。
地址形式是用于挑选设备和界说方针寄存器,在这种形式下,8位数据的意义是:高6位是设备地址(UDA1341的地址为000101),低两位是后边数据形式下寄存器的类型(00:DATA0,01:DATA1,10:STATUS)。只需没有再改动地址形式下的数据,则数据形式下的数据始终是传输到上一个地址形式所界说的寄存器内。
在传输数据形式下,STATUS是用于设置复位,体系时钟频率、数据输入形式、DC滤波等内容。DATA0分为直接寻址形式和扩展寻址形式,直接寻址形式是直接进行形式的操控,包含音量、静音等等,而扩展寻址形式是在直接寻址形式下先设置3位扩展地址,再在直接寻址形式下设置5位扩展数据。在DATA1下,能够读取到被检测峰值。至于详细的DATA0、DATA1、STATUS下,每一位数据详细的意义,还请自己查阅手册。