IIS模块实现IIS.c如下:
#include"s3c2416.h"
#include "IIS.h"
#include"Exception.h"
#include"UART0.h"
#defineDEBUG_IIS
#ifdef DEBUG_IIS
#defineDebug(x...) Uart0_Printf(x)
#else
#defineDebug(x...)
#endif
staticunsigned char TX_Channel; // 发送音频的声道数
staticunsigned char TX_BitLen; // 发送位长
staticunsigned char RX_Channel; // 接收音频的声道数
staticunsigned char RX_BitLen; // 接收位长
staticvolatile unsigned char TxBufferFlag;
staticvolatile unsigned char RxBufferFlag;
staticunsigned int TxCount; // 播放时记录缓存中的写位置
staticunsigned int RxCount; // 录音时记录缓存中的读位置
// 插放与录音均采用双缓存,DMA传输的主存与cache会有数据一致性问题
// 音频DMA缓存分配到不开启cache的内存区域
staticunsigned int TxBuffer0[4*1024] __attribute__((section("No_Cache"),zero_init));
staticunsigned int TxBuffer1[4*1024] __attribute__((section("No_Cache"),zero_init));
staticunsigned int RxBuffer0[4*1024] __attribute__((section("No_Cache"),zero_init));
staticunsigned int RxBuffer1[4*1024] __attribute__((section("No_Cache"),zero_init));
static voidDMA_IRQ(void)
{
static unsigned char TxBufferChannel = 0;
static unsigned char RxBufferChannel = 0;
unsigned int DMA_Channel;
DMA_Channel = rSUBSRCPND;
if (DMA_Channel &(1<<SUBINT_DMA0)) { // DMA0中断请求, IIS TX
if (TxBufferChannel == 0) {
rDISRC0 = ((unsigned int)TxBuffer1);// 开始使用Buffer1缓存
TxBufferFlag &= ~(1<<0);// 发送标志0位清空,说明Buffer0数据需填充
TxBufferChannel = 1; // 正在发送Buffer1缓存
} else {
rDISRC0 = ((unsignedint)TxBuffer0);// 开始使用Buffer0缓存
TxBufferFlag &= ~(1<<1);// 发送标志1位清空,说明Buffer1数据需填充
TxBufferChannel = 0; // 正在发送Buffer0缓存
}
rDCON0 = (rDCON0&(~0xfffff)) |(sizeof(TxBuffer0)/4);
rDMASKTRIG0 = (1<<1); // IIS TX打开DMA0通道
rSUBSRCPND |= (1<<SUBINT_DMA0);
}
if (DMA_Channel &(1<<SUBINT_DMA1)) { // DMA1中断请求, IIS RX
if (RxBufferChannel == 0) {
rDIDST1 = ((unsigned int)RxBuffer1);// DMA1目的地址
RxBufferFlag |= (1<<0); // 接收缓存0位置位,说明Buffer0数据准备好
RxBufferChannel = 1; // 下一次使用Buffer1
} else {
rDIDST1 = ((unsigned int)RxBuffer0);// DMA1目的地址
RxBufferFlag |= (1<<1); // 接收缓存1位置位,说明Buffer1数据准备好
RxBufferChannel = 0; // 下一次使用Buffer0
}
rDCON1 = (rDCON1&(~0xfffff)) |(sizeof(RxBuffer0)/4);
rDMASKTRIG1 = (1<<1); // IIS RX打开DMA1通道
rSUBSRCPND |= (1<<SUBINT_DMA1);
}
rSRCPND1 |= (1 << INT_DMA);
rINTPND1 |= (1 << INT_DMA);
}
unsignedint IIS_WriteBuffer(unsigned char *pData, unsigned int MaxLen)
{
unsigned int i;
unsigned int nCount; // 能写入buffer中数据长度(以字计,fifo 32位长)
unsigned int *pBuffer;
unsigned char *pTemp = pData;
if (pTemp==0 || MaxLen==0) {
return 0; // 参数错误,数据未写入缓存
}
if ((TxBufferFlag&0x3) == 0x3) {
return 0; // Buffer0,Buffer1均已写满
}
if (!(TxBufferFlag & (1<<0))) { //Buffer0需填充
pBuffer = &TxBuffer0[TxCount];
} else { // Buffer1需填充
pBuffer = &TxBuffer1[TxCount];
}
nCount = (sizeof(TxBuffer0)/4) - TxCount;
switch (TX_BitLen) {
case 8:
if (TX_Channel != 1) { // 双声道
if (MaxLen/2 == 0) {// 左右声道fifo中有两个8位有效音频数据
pTemp += MaxLen; // 不足一个采样2字节数据,丢弃写入buffer中
}
MaxLen = MaxLen/2; // 32位的fifo中有两个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
*pBuffer++ = (((unsignedint)pTemp[1]<<16)+
((unsignedint)pTemp[0]<<0));
pTemp += 2; // 2个8位的声道数据已写入buffer中
}
} else { // 单声道
if (MaxLen < nCount) {// 32位的fifo中有一个8位有效音频数据
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
*pBuffer++ = (unsignedint)pTemp[0]<<0;
pTemp += 1; // 1个8位的声道数据已写入FIFO中
}
}
break;
case 16:
if (TX_Channel != 1) { // 双声道
if (MaxLen/4 == 0) {// 左右声道fifo中有四个8位有效音频数据
pTemp += MaxLen; // 不足一个采样4字节数据,丢弃写入buffer中
}
MaxLen = MaxLen/4; // 32位的fifo中有四个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
*pBuffer++ = (((unsignedint)pTemp[0]<<0) +
((unsignedint)pTemp[1]<<8)) +
(((unsigned int)pTemp[2]<<16) +
((unsignedint)pTemp[3]<<24));
pTemp += 4; // 4个8位的声道数据已写入FIFO中
}
} else { // 单声道
if (MaxLen/2 == 0) {// 单声道fifo中有两个8位有效音频数据
pTemp += MaxLen; // 不足一个采样2字节数据,丢弃写入buffer中
}
MaxLen = MaxLen/2; // 32位的fifo中有两个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
*pBuffer++ = (((unsignedint)pTemp[1]) << 8) + pTemp[0];
pTemp += 2; // 2个8位的声道数据已写入FIFO中
}
}
break;
case 24:
if (TX_Channel != 1) { // 双声道
if (MaxLen/3 == 0) {// 左右声道fifo中有六个8位有效音频数据
pTemp += MaxLen; // 一个声道采样3字节数据,丢弃写入buffer中
}
MaxLen = MaxLen/3; // 每个声道fifo中有三个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
*pBuffer++ =((pTemp[0]<<0)+
((unsignedint)pTemp[1]<<8))+((unsigned int)pTemp[2]<<16);
pTemp += 3; // 3个8位的声道数据已写入FIFO中
}
} else { // 单声道
if (MaxLen/3 == 0) {// 单声道fifo中只有三个8位有效音频数据
pTemp += MaxLen; // 不足一个采样3字节数据,丢弃写入buffer中
}
MaxLen = MaxLen/3;
if (MaxLen < nCount) {
nCount = MaxLen;
}
if (nCount == 1) {
pTemp += 3;
}
for (i=0; i<nCount/2; i++) {
*pBuffer++ =((pTemp[0]<<0)+
((unsignedint)pTemp[1]<<8))+((unsigned int)pTemp[2]<<16);
*pBuffer++ = 0; // 另一声道静音
pTemp += 3; // 3个8位的声道数据已写入FIFO中
}
}
break;
default:
break;
}
TxCount += nCount; // 记录这一次写后缓存的位置
if (TxCount >= sizeof(TxBuffer0)/4) { // 到达缓存结尾
TxCount = 0; // 标记缓存写满并切换到另一个缓存
if (!(TxBufferFlag & (1<<0))){
TxBufferFlag |= (1<<0); // Buffer0写滿
} else {
TxBufferFlag |= (1<<1); //Buffer1写滿
}
}
return ((unsigned int)(pTemp-pData)); // 返回写入缓存中的字节数
}
unsignedint IIS_ReadBuffer(unsigned char *pData, unsigned int MaxLen)
{
unsigned int i;
unsigned int nCount;
unsigned int Value;
unsigned int *pBuffer;
unsigned char *pTemp = pData;
if (pTemp==0 || MaxLen==0) {
return 0; // 参数错误,数据未写入缓存
}
if ((RxBufferFlag&0x3) == 0x0) {
return 0; // Buffer0,Buffer1均未准备好
}
if (RxBufferFlag & (1<<0)) { //Buffer0准备好
pBuffer = &RxBuffer0[RxCount];
} else { // Buffer1准备好
pBuffer = &RxBuffer1[RxCount];
}
// 剩余缓存的长度
nCount = (sizeof(RxBuffer0)/4) - RxCount;
switch (RX_BitLen) {
case 8:
if (RX_Channel != 1) { // 双声道
if (MaxLen/2 == 0) {// 左右声道fifo中有两个8位有效音频数据
pTemp += MaxLen; // 不足一个采样2字节存储空间,丢弃存入
}
MaxLen = MaxLen/2; // 32位的fifo中有两个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
Value = *pBuffer++;
pTemp[0] = (unsignedchar)(Value>>0);
pTemp[1] = (unsignedchar)(Value>>16);
pTemp += 2; // 2个8位的声道数据已从buffer中读取
}
} else { // 单声道
if (MaxLen < nCount) {// 32位的fifo中有一个8位有效音频数据
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
Value = *pBuffer++;
pTemp[0] = (unsigned char)(Value>>0);
pTemp += 1; // 1个8位的声道数据已写入FIFO中
}
}
break;
case 16:
if (RX_Channel != 1) { // 双声道
if (MaxLen/4 == 0) {// 左右声道fifo中有四个8位有效音频数据
pTemp += MaxLen; // 不足一个采样4字节存储空间,丢弃存入
}
MaxLen = MaxLen/4; // 32位的fifo中有四个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
Value = *pBuffer++;
pTemp[0] = (unsigned char)Value;
pTemp[1] = (unsignedchar)(Value>>8);
pTemp[2] = (unsignedchar)(Value>>16);
pTemp[3] = (unsigned char)(Value>>24);
pTemp += 4; // 4个8位的声道数据已从FIFO中读取
}
} else { // 单声道
if (MaxLen/2 == 0) {// 单声道fifo中有两个8位有效音频数据
pTemp += MaxLen; // 不足一个采样2字节存储空间,丢弃存入
}
MaxLen = MaxLen/2; // 32位的fifo中有两个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
Value = *pBuffer++;
pTemp[0] = (unsigned char)Value;
pTemp[1] = (unsignedchar)(Value>>8);
pTemp += 2; // 2个8位的声道数据已从FIFO中读取
}
}
break;
case 24:
if (RX_Channel != 1) { // 双声道
if (MaxLen/3 == 0) {// 左右声道fifo中有六个8位有效音频数据
pTemp += MaxLen; // 不足一个采样6字节存储空间,丢弃存入
}
MaxLen = MaxLen/3; // 左右声道fifo中有六个8位有效音频数据
if (MaxLen < nCount) {
nCount = MaxLen;
}
for (i=0; i<nCount; i++) {
Value = *pBuffer++;
pTemp[0]= (unsigned char)Value;
pTemp[1] = (unsignedchar)(Value>>8);
pTemp[2] = (unsignedchar)(Value>>16);
pTemp += 3; // 3个8位的声道数据已从FIFO中读取
}
} else { // 单声道
if (MaxLen/3 == 0) {// 单声道fifo中只有三个8位有效音频数据
pTemp += MaxLen; // 不足一个采样3字节存储空间,丢弃存入
}
MaxLen = MaxLen/3;
if (MaxLen < nCount) {
nCount = MaxLen;
}
if (nCount == 1) {
pTemp += 3;
}
for (i=0; i<nCount/2; i++) {
Value = *pBuffer++;
pTemp[0] = (unsigned char)Value;
pTemp[1] = (unsignedchar)(Value>>8);
pTemp[2] = (unsignedchar)(Value>>16);
pTemp += 3; // 3个8位的声道数据已从FIFO中读取
Value = *pBuffer++; // 下一声道数据无需保存
}
}
break;
default:
break;
}
// 记录下一次读时缓存的位置
RxCount += nCount;
if (RxCount >= sizeof(RxBuffer0)/4) { // 读完一个缓存
RxCount = 0;
if (RxBufferFlag & (1<<0)) {
RxBufferFlag &= ~(1<<0);// Buffer0未准备好
} else {
RxBufferFlag &= ~(1<<1);// Buffer1未准备好
}
}
return ((unsigned int)(pTemp-pData)); // 返回读取buffer的字节数
}
voidIIS_RxPause()
{
rIISCON |= (1<<5);
rIISCON &= ~((1<<0) |(1<<1));
}
voidIIS_RxStart()
{
rIISCON &= ~(1<<5);
rIISCON |= (1<<0) | (1<<1);
}
voidIIS_TxPause()
{
rIISCON |= (1<<6);
rIISCON &= ~((1<<0) |(1<<2));
}
voidIIS_TxStart()
{
rIISCON &= ~(1<<6);
rIISCON |= (1<<0) | (1<<2);
}
voidIIS_TxInit(unsigned int Sample, unsigned char BitLen, unsigned char Channel)
{
unsigned int Mode;
unsigned int Scaler;
TX_Channel = Channel; // 单声道/双声道
TX_BitLen = BitLen; // 每声道的字长
rIISFIC |= (1<<15); // TX fifo flush
rIISFIC &= ~(1<<15);
TxBufferFlag = 0;
TxCount = 0;
// Master mode audio clock=96M
// codec clcok 256fs,bit clock 32fs,
Mode = (0x0<<16) + (1<<10);//IISSDO1,IISSDO2不使用,即不会写FIFO1,2
if (((rIISMOD>>8) & 0x3) == 1) {
Mode |= (0x2<<8); // 已在接收模式,转到同时接收发送
}
Mode |= (0x0<<5); // IIS format format
Mode |= (0x2<<3); // 384fs
switch (BitLen) {
case 8: // 8位采样数也配置成16位进行传输
case 16:
Mode |= (0x0 << 13);
Mode |= (0x0 << 1); // 32fs
break;
case 24:
Mode |= (0x2 << 13);
Mode |= (0x1 << 1); // 48fs
break;
default:
TX_BitLen = 16; // 其它默认为16位
Mode |= (0x0 << 13);
Mode |= (0x0 << 1); // 32fs
break;
}
rIISMOD = Mode;
if (Sample != 0) {
Scaler =(96000000/384+(Sample>>1))/Sample; // codec clcok 384fs
rIISPSR = (1<<15) + ((Scaler-1)<<8); // 预分频输出
}
rDMASKTRIG0 = (1<<1); // IIS TX打开DMA0通道
Debug("Player sampling rate: %dHZ, Bitlength: %d, Channel: ", Sample, BitLen);
Debug((Channel!=1)?"dual\r\n":"single\r\n");
Debug("CodeClk = %dHZ\r\n", 96000000/(((rIISPSR>>8)&0x3f)+1));
}
voidIIS_RxInit(unsigned int Sample, unsigned char BitLen, unsigned char Channel)
{
unsigned int Mode;
unsigned int Scaler;
RX_Channel = Channel; // 录音的声道数
RX_BitLen = BitLen; // 录音的每声道位长
rIISFIC |= (1<<7); // RX fifo flush
rIISFIC &= ~(1<<7);
RxBufferFlag = 0;
RxCount = 0;
// 16bit,Master mode audio clock=96M,IISformat
// codec clcok 256fs,bit clock 32fs
Mode = (0x0<<16) + (1<<10);
if (((rIISMOD>>8) & 0x3) == 0) {
Mode |= (0x2<<8); // 已在发送模式,转到同时接收发送
} else {
Mode |= (0x1<<8); // 接收模式
}
Mode |= (0x0<<5); // // IIS formatformat
Mode |= (0x2<<3); // 384fs
switch (BitLen) {
case 8:
case 16:
Mode |= (0x0 << 13);
Mode |= (0x0 << 1); // 32fs
break;
case 24:
Mode |= (0x2 << 13);
Mode |= (0x1 << 1); // 48fs
break;
default:
RX_BitLen = 16; // 其它默认为16位
Mode |= (0x0 << 13);
Mode |= (0x0 << 1); // 32fs
break;
}
rIISMOD = Mode;
if (Sample != 0) {
Scaler = 96000000/384/Sample; // codecclcok 384fs
rIISPSR = (1<<15) + ((Scaler-1)<<8); // 预分频输出
}
rDMASKTRIG1 = (1<<1); // IIS RX打开DMA1通道
Debug("Recorder sampling rate: %dHZ,Bit length: %d, Channel: ", Sample, BitLen);
Debug((Channel!=1)?"dual\r\n":"single\r\n");
Debug("CodeClk = %dHZ\r\n",96000000/(((rIISPSR>>8)&0x3f)+1));
}
voidIIS_Init()
{
// 配置IIS接口引脚
rGPECON &= ~(0x3ff << 0);
rGPECON |= 0x2aa;
rGPEUDP &= ~(0x3ff << 0); // 禁止上下位
rCLKDIV1 &= ~(0xf<<12);
rCLKDIV1 |= (0<<12); // IIS clock 96M
rIISCON = 0;
rDMAREQSEL0 = (4<<1) | (1<<0);// IIS TX通过DMA0传输
rDMAREQSEL1 = (5<<1) | (1<<0);// IIS RX通过DMA1传输
rDISRC0 = (unsigned int)TxBuffer0; // DMA0源地址
rDISRC1 = (unsigned int)&rIISRXD; //DMA1源地址为RX FIFO寄存器地址
rDISRCC0 = (0<<1) | (0<<0); //DMA0源地址在AHB总线上,DMA访问后地址自增
rDISRCC1 = (1<<1) | (1<<0); //DMA1源地址在APB总线上,地址固定
rDIDST0 = (unsigned int)&rIISTXD; //DMA0目的地址为TX FIFO寄存器地址
rDIDST1 = ((unsigned int)RxBuffer0); // DMA1目的地址
rDIDSTC0 = (0<<2) | (1<<1) |(1<<0); // DMA0目的地址在APB上,固定,计数0中断
rDIDSTC1 = (0<<2) | (0<<1) |(0<<0); // DMA1目的地址在AHB上,自增,计数0中断
// Word transferred, count 16KB/4 word,handshake mode
// no auto reload, single service, enable tcinterrupt,
rDCON0 = 0xe0000000 | (1<<22) |(1<<24) | (2<<20) | ((sizeof(TxBuffer0)/4)<<0);
rDCON1 = 0xe0000000 | (1<<22) |(1<<24) | (2<<20) | ((sizeof(RxBuffer0)/4)<<0);
TxBufferFlag = 0; // 发送缓存0,1需填充
RxBufferFlag = 0;
TxCount = 0;
RxCount = 0;
IRQ_Register(INT_DMA, DMA_IRQ);// DMA中断入口函数加入到向量表
rINTSUBMSK &= ~((1<<SUBINT_DMA0) |(1<<SUBINT_DMA1)); // 开启DMA0与DMA1子中断
rINTMOD1 &= ~(1 << INT_DMA); //DMA IRQ
rINTMSK1 &= ~(1 << INT_DMA); //DMA开启中断
}
IIS模块头文件IIS.h如下:
#ifndef __IIC_H__
#define __IIC_H__
#ifdef __cplusplus
extern "C" {
#endif
#define ArbitrationFailed (1<<3)// 总线仲裁失败
#define AddressMatche (1<<2)// 从机地址相配
#define AddressZeros (1<<1)// 接收的地址为0
#define NoAck (1<<0)// 没有回复信号
extern int IIC_WriteBytes(unsigned char SlaveAddr, unsigned char WriteAddr,
unsigned char *pData, int Length);
extern int IIC_ReadBytes(unsigned char SlaveAddr, unsigned char ReadAddr,
unsigned char *pData, int Length);
extern void IIC_Init(void);
#ifdef __cplusplus
}
#endif
#endif /*__IIC_H__*/
5. 附录
WM8960.rar,包含音频驱动模块相关源码,WM8960.c/WM8960.h为音频编解码器驱动模块,IIS.c/IIS.h为音频接口IIS驱动模块,IIC.c/IIC.h为IIC接口驱动模块(需通过IIC控制WM8960),由于音频驱动是从工程应用角度实现,实现适用于不同采样频率、采样位数、声道数,并通过DMA的方式传输音频,采用双缓存的概念来实现音频的驱动,相对代码较长,但已经是直入主题,尽可能简单明了。