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); } 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; } 作者:李萬鵬 |