S3c2440A 支持位于系统总线和外设总线之间的4 个通道的控制器。每个DMA 控制器通道
无限制地执行系统总线上的设备或外设总线上的设备之间数据搬移。换句话说,就是每个
通道都操作一下四种情况:
(1)源和目的设备都在系统总线上
(2)源设备在系统总线上,目的设备在外设总线上
(3)源设备在外设总线上,目的设备在系统总线上
(4)源设备和目的设备都在外设总线上
DMA 的主要有点就是其传输数据不需要CPU 的干涉。DMA 操作可由软件或来自内设或外
部请求引脚来初始化。
DMA每次传送2个字节,放到FIFO中,IIS就播放,FIFO空,导致DMA被再次触发,直到传输计数器为0,产生DMA中断,CPU进入中断处理程序。在DMA传送的时候,CPU可以处理其他事情。提高了系统效率。
下面的程序使用IIS播放声音同时跑流水灯。如果不用DMA,直接用上一篇的IIS程序,会发现声音断断续续,因为IIS和流水灯一起分CPU的时间。如果使用DMA,则IIS需要的数据由DMA负责传送,而CPU可以执行流水灯程序,互相不耽误。当传输计数器为0时,产生DMA中断,CPU进入中断处理程序。其他时候,CPU可以执行流水灯。这样就实现了流水灯和IIS放音同时执行。
#include "2440addr.h"
#include "music.h"
#define L3MODE 1<<2
#define L3DATA 1<<3
#define L3CLOCK 1<<4
#define _ISR_STARTADDRESS 0x33ffff00
#define U32 unsigned int
U32 flag, result, remain;
void Delay(){
int i, j, k;
for(i = 0; i < 0xff; i++)
for(j = 0; j < 0xff; j++)
for(k = 0; k < 0xff; k++)
;
}
void Led(){
int i;
for(i = 3; i < 7; i++){
rGPFDAT &= "(1<<i);
Delay();
rGPFDAT = 0xff;
}
}
void WriteL3(unsigned char data, unsigned int mode){
//mode = 0,地址模式;mode = 1,数据传输模式
int i, k;
if(mode == 0){
rGPBDAT = rGPBDAT & "(L3MODE|L3DATA|L3CLOCK )|L3CLOCK;
}
else{
rGPBDAT = rGPBDAT & "(L3MODE|L3DATA|L3CLOCK)|(L3CLOCK|L3MODE);
}
for(k = 0; k < 5; k++)
;
for(i = 0; i < 8; i++){
if(data & 0x1){
rGPBDAT &= "L3CLOCK;
rGPBDAT |= L3DATA;
for(k = 0; k < 5; k++)
;
rGPBDAT |= L3CLOCK;
rGPBDAT |= L3DATA;
for(k = 0; k < 5; k++)
;
}
else{
rGPBDAT &= "L3CLOCK;
rGPBDAT &= "L3DATA;
for(k = 0; k < 5; k++)
;
rGPBDAT |= L3CLOCK;
rGPBDAT &= "L3DATA;
for(k = 0; k < 5; k++)
;
}
data >>= 1;
}
rGPBDAT = rGPBDAT & "(L3MODE|L3DATA|L3CLOCK)|(L3CLOCK|L3MODE);
} [page]
void PlayMusic(unsigned char buffer[], unsigned int length){
result = (length>>1)/0x100000;
remain = (length>>1)&0xfffff;
//UDA1341
//STATUS模式
rGPBDAT = rGPBDAT & "(L3MODE|L3DATA|L3CLOCK)|(L3CLOCK|L3MODE);
WriteL3(0x14+2,0);
//复位
WriteL3(0x60,1);
WriteL3(0x14+2,0);
//00010000 系统时钟频率384fs
WriteL3(0x10,1);
WriteL3(0x14+2,0);
//11000001 输出增益,ADC关,DAC开
WriteL3(0xc1,1);
//IIS
//DMA开启,在接受空闲状态,不产生IISLRCK信号,IIS预分频使能
rIISCON = (1<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);
//主设备时钟PCLK,主设备模式,发送模式,串行数据16位,主时钟是384fs,串行位时钟32fs
rIISMOD = (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
//预分频都是N=3
rIISPSR = (3<<5)|3;
//发送FIFO用DMA模式,发送FIFO使能
rIISFCON = (1<<15)|(1<<13);
//DMA
rDISRC2 = (U32)buffer; //DMA2初始源地址
rDISRCC2 = (0<<1)|(0<<0); //源在系统总线上,地址增加
rDIDST2 = (U32)IISFIFO; //DMA2初始目的地址
rDIDSTC2 = (0<<2)|(1<<1)|(1<<0); //当TC为0时,中断发生,源在外围总线上,地址固定,一直为IISFIFO的0x55000010(小端)
if(result == 0){
flag = 0;
//握手模式,与APB时钟同步,当所有的传输结束,中断请求生成,单元传送,单服务模式,当传输计数器为0时,DMA通道关闭
rDCON2 = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(remain<<0);
}
else{
flag = 1;
rDCON2 = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(0xfffff<<0);
}
rDMASKTRIG2 = (0<<2)|(1<<1)|(0<<0); //DMA通道开启
rIISCON |= 0x1; //IIS开启
}
void __irq DMA2_ISR(void){
rSRCPND |= 1<<19;
rINTPND |= 1<<19;
if(flag == 0){
rIISCON = 0x0; //关闭IIS
rIISFCON = 0x0; //关闭IISFIFO的DMA模式
rDMASKTRIG2 = 1<<2; //关闭DMA
}
else{
result--;
rDISRC2 += 0x200000; //因为是半字
if(result == 0){
flag = 0;
rDCON2 = (rDCON2 & ("0xfffff)) | (remain);
}
rDMASKTRIG2 = (0<<2)|(1<<1)|(0<<0);
}
}
int Main(){
rGPBUP = rGPBUP & "(0x7<<2) | (0x7<<2); //The pull up function is disabled GPB[4:2] 1 1100
rGPBCON = rGPBCON & "(0x3f<<4) | (0x15<<4); //GPB[4:2]=Output(L3CLOCK):Output(L3DATA):Output(L3MODE)
rGPBDAT = 0x1ec;
rGPEUP = rGPEUP & "(0x1f) | 0x1f; //The pull up function is disabled GPE[4:0] 1 1111
rGPECON = rGPECON & "(0x3ff) | 0x2aa; //GPE[4:0]=I2SSDO:I2SSDI:CDCLK:I2SSCLK:I2SLRCK
rMPLLCON = (150<<12)|(5<<4)|(0<<0);
rSRCPND |= 1<<19;
rINTPND |= 1<<19;
rINTMSK &= "(1<<19);
rGPFCON = 0xd57f;
rGPFUP = 0x87;
pISR_DMA2 = (U32)DMA2_ISR;
PlayMusic(music, sizeof(music));
while(1){
Led(); //占用CPU
}
return 0;
}