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。
由于s3c2440要实现IIS的录、放音,还需要UDA1341芯片。s3c2440与UDA1341之间除了我们前面介绍过的IIS接口相连接外,还有一个称之为L3总线的连接,用于s3c2440配置UDA1341内部的寄存器。由于s3c2440不具备L3总线接口,因此我们是用三个通用IO口来模拟L3,从而实现L3总线的传输。
UDA1341有两种模式:地址模式和数据传输模式。地址模式表示传输的是地址信息,它的高6位永远是000101,低两位表示的是传输的模式,是状态模式、数据0模式还是数据1模式,其中状态模式主要用于配置UDA1341的各类初始状态,数据模式主要用于改善音频输入、输出的效果。(00:DATA0,01:DATA1,10:STATUS)。只要没有再改变地址模式下的数据,则数据模式下的数据始终是传输到上一个地址模式所定义的寄存器内。
WAV文件格式:
WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。
RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个
字节便是“RIFF”。
WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE
Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。具体见下图:
------------------------------------------------
| RIFF WAVE Chunk |
| ID = 'RIFF' |
| RiffType = 'WAVE' |
------------------------------------------------
| Format Chunk |
| ID = 'fmt ' |
------------------------------------------------
| Fact Chunk(optional) |
| ID = 'fact' |
------------------------------------------------
| Data Chunk |
| ID = 'data' |
------------------------------------------------
图1 Wav格式包含Chunk示例
其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位
于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大
小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节
表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk内容。
PS:
所有数值表示均为低字节表示低位,高字节表示高位。
具体介绍:
RIFF WAVE Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'RIFF' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| Type | 4 Bytes | 'WAVE' |
----------------------------------
图2 RIFF WAVE Chunk
以'FIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID
和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表
示是wav文件。
结构定义如下:
struct RIFF_HEADER
{
char szRiffID[4]; // 'R','I','F','F'
DWORD dwRiffSize;
char szRiffFormat[4]; // 'W','A','V','E'
};
Format Chunk
====================================================================
| | 字节数 | 具体内容 |
====================================================================
| ID | 4 Bytes | 'fmt ' |
--------------------------------------------------------------------
| Size | 4 Bytes | 数值为16或18,18则最后又附加信息 |
-------------------------------------------------------------------- ----
| FormatTag | 2 Bytes | 编码方式,一般为0x0001 | |
-------------------------------------------------------------------- |
| Channels | 2 Bytes | 声道数目,1--单声道;2--双声道 | |
-------------------------------------------------------------------- |
| SamplesPerSec | 4 Bytes | 采样频率 | |
-------------------------------------------------------------------- |
| AvgBytesPerSec| 4 Bytes | 每秒所需字节数 | |===> WAVE_FORMAT
-------------------------------------------------------------------- |
| BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) | |
-------------------------------------------------------------------- |
| BitsPerSample | 2 Bytes | 每个采样需要的bit数 | |
-------------------------------------------------------------------- |
| | 2 Bytes | 附加信息(可选,通过Size来判断有无) | |
-------------------------------------------------------------------- ----
图3 Format Chunk
以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18
则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的
附加信息。
结构定义如下:
struct WAVE_FORMAT
{
WORD wFormatTag;
WORD wChannels;
DWORD dwSamplesPerSec;
DWORD dwAvgBytesPerSec;
WORD wBlockAlign;
WORD wBitsPerSample;
};
struct FMT_BLOCK
{
char szFmtID[4]; // 'f','m','t',' '
DWORD dwFmtSize;
WAVE_FORMAT wavFormat;
};
Fact Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'fact' |
----------------------------------
| Size | 4 Bytes | 数值为4 |
----------------------------------
| data | 4 Bytes | |
----------------------------------
图4 Fact Chunk
Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。
结构定义如下:
struct FACT_BLOCK
{
char szFactID[4]; // 'f','a','c','t'
DWORD dwFactSize;
};
Data Chunk
==================================
| |所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'data' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| data | | |
----------------------------------
图5 Data Chunk
Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是
数据的大小。紧接着就是wav数据。根据Format Chunk中的声道数以及采样bit数,
wav数据的bit位置可以分成以下几种形式:
---------------------------------------------------------------------
| 单声道 | 取样1 | 取样2 | 取样3 | 取样4 |
| |--------------------------------------------------------
| 8bit量化 | 声道0 | 声道0 | 声道0 | 声道0 |
---------------------------------------------------------------------
| 双声道 | 取样1 | 取样2 |
| |--------------------------------------------------------
| 8bit量化 | 声道0(左) | 声道1(右) | 声道0(左) | 声道1(右) |
---------------------------------------------------------------------
| | 取样1 | 取样2 |
| 单声道 |--------------------------------------------------------
| 16bit量化 | 声道0 | 声道0 | 声道0 | 声道0 |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------
| | 取样1 |
| 双声道 |--------------------------------------------------------
| 16bit量化 | 声道0(左) | 声道0(左) | 声道1(右) | 声道1(右) |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------
图6 wav数据bit位置安排方式
Data Chunk头结构定义如下:
struct DATA_BLOCK
{
char szDataID[4]; // 'd','a','t','a'
DWORD dwDataSize;
};
实际文件中的数据格式如下图所示:
L3接口时序:
L3接口:
#define L3C (1<<4);//GPB4=L3CLOCK; #define L3D (1<<3);//GPB3=L3DATA; #define L3M (1<<2);//GPB2=L3MODE
L3总线接口的写函数:
//输入参数data为要写入的数据 //输入参数address,为1表示地址模式,为0表示数据传输模式 //L3DATA(数据线:用于传输数据)、L3MODE(模式线:用于选择模式)、L3CLOCK(时钟线:用于传输时钟)//L3MODE为低时是地址模式,L3MODE为高时是数据传输模式 static void WriteL3(U8 data,U8 address) { int i,j; if(address == 1) { GPBDAT = GPBDAT & ~(L3C|L3D|L3M)|L3C;//L3D=L,L3M=L(地址模式),L3C=H } else { GPBDAT = GPBDAT & ~(L3C|L3D|L3M)|(L3C|L3M);//L3M=H(数据模式) } for(i=0;i<10;i++); //并行数据转串行数据输出,以地位在前、高位在后的顺序 for(i=0;i<8;i++) { if(data&0x1) { GPBDAT &= ~L3C; GPBDAT |= L3D; for(i=0;i<5;i++); GPBDAT |= L3C; GPBDAT |= L3D; for(i=0;i<5;i++); } else { GPBDAT &= ~L3C; GPBDAT &= ~L3D; for(i=0;i<5;i++); GPBDAT |= L3C; GPBDAT &= ~L3D; for(i=0;i<5;i++); } data>>=1; } GPBDAT = GPBDAT & ~(L3C|L3D|L3M)|(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); //0110 0000 : 状态0,复位 WriteL3(0x14 + 2,1); //状态模式 (000101xx+10) WriteL3(0x10,0); //0001 0000 : 状态0, 384fs,IIS,no DC-filtering WriteL3(0x14 + 2,1); //状态模式 (000101xx+10) WriteL3(0xc1,0); //1000 0001:状态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寄存器
//fs=22.05kHz,CODECLK=384fs=8.4672MHz //预分频器为3,CDCLK=PCLK/(3+1)
rIISPSR = 3<<5|3;
rIISCON = (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1); //无效DMA,输入空闲,预分频器有效 rIISMOD =(0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);//PCLK为时钟源,输出模式,IIS模式,每个声道16位,CODECLK=384fs,SCLK=32fs rIISFCON = (0<<15)|(1<<13); //输出FIFO正常模式,输出FIFO使能 flag=1; count=0; rIISCON |= 0x1; //开启IIS while(flag) { if((rIISCON & (1<<7))==0) //检查输出FIFO是否为空 { //FIFO中的数据为16位,深度为32,当输出FIFO为空时,一次性向FIFO写入32个16位数据 for(i=0;i<32;i++) { *IISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8); } count+=64; if(count>length) flag=0; //音频数据传输完,则退出 } } rIISCON = 0x0; //关闭IIS }
时钟频率:
static struct { U16 Freq;//采样频率 U32 MPLL;//MPLL配置 U16 PreScaler;//分频值 U8 ofs;//CDCLK选择 }CodecPara[7] = {{8000,(123<<12)|(6<<4)|0,(23<<5)|23,0}, {11025,(229<<12)|(5<<4)|1,(11<<5)|11,1}, {16000,(123<<12)|(6<<4)|0,(11<<5)|11,0}, {22050,(229<<12)|(5<<4)|1,(5<<5)|5,1}, {32000,(123<<12)|(6<<4)|0,(5<<5)|5,0}, {44100,(229<<12)|(5<<4)|1,(2<<5)|2,1}, {48000,(123<<12)|(6<<4)|0,(3<<5)|3,0}};
开始播放:
void PlayMusicTest(void) { //配置MPLL
//fs=22.05kHz,CODECLK=384fs=8.4672MHz
rMPLLCON = (150<<12) | (5<<4) | 1; //配置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; Uart_Init(33857000,115200);//根据PCLK设置波特率 Uart_Printf("nNOW PLAY THE WindowsXP"); Uart_Printf("nsample=%d",*(U32 *)(WindowsXP_Wav+0x18));//输出采样频率 playsound(WindowsXP_Wav+44,sizeof(WindowsXP_Wav)-44);//在数据开始处开始播放 }