新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > s3c2440的DMA應用

        s3c2440的DMA應用

        作者: 時間:2016-11-19 來源:網絡 收藏
        DMA(Direct Memory Access,直接內存訪問)是一種不經過CPU而直接從內存存取數據的數據交換模式。在需要進行大量數據交換的場合,用好DMA,可以大大提高系統的性能,因為DMA操作幾乎不占用CPU資源。

        s3c2440提供了4個通道的DMA,它們不僅可以實現內存之間的數據交換,還可以實現內存與外設,以及外設與外設之間的數據交換。要用好s3c2440的DMA,關鍵是配置好它的源、目的寄存器,和必要的控制寄存器。寄存器DISRCn是初始DMA源寄存器,它是用于設置DMA數據傳輸的源基址,而寄存器DIDSTn是初始DMA目的寄存器,它是用于設置DMA數據傳輸的目的基址。初始DMA源控制寄存器DISRCCn的第1位用于選擇源的總線(系統總線AHB還是外設總線APB),第0位用于設置源基址在數據傳輸過程中是遞增還是固定不變。初始DMA目的控制寄存器DIDSTCn的低兩位與寄存器DISRCCn相識,但它是用來設置目的基址,而第2位用于設置是在傳輸完數據之后中斷還是在自動重載后中斷。DMA控制寄存器DCONn用于控制數據的DMA傳輸,第31位用于設置傳輸協議是需求模式還是握手模式,第30位用于選擇同步時鐘是PCLK還是HCLK,第29位用于設置DMA中斷是否發生,第28位用于選擇傳輸大小是單元傳輸還是突發傳輸,第27位用于選擇服務模式是單步模式還是完全模式,第24位到第26位用于設置DMA的請求源,第23位用于設置DMA的源是軟件還是硬件,第22位用于設置是否需要重載傳輸的目的和源基址,第20位和第21位用于設置數據傳輸的數據大小(字節、半字還是字),低20位用于初始化傳輸數據的個數。而通過讀取DMA狀態寄存器DSTATn的低20位可以獲知當前的傳輸的計數。DMA掩碼觸發寄存器DMASKTRIGn的第2位可以終止當前DMA操作,第1位可以用于開啟DMA通道,第0位則表示在軟件請求模式下觸發DMA通道。

        下面我們就用DMA的方式來實現音頻的播放。由于是用DMA的方式,因此在播放的過程中不占用系統資源,我們可以很容易的實現聲音的各種操作而絲毫不影響播放的效果,如音量的提高和降低、靜音、暫停等。在這里,還需要強調一點,利用DMA傳輸數據,一次最多可以傳輸的字節大小為:DSZ×TSZ×TC,DSZ表示的是數據大小(字節、半字還是字,即是1、2還是4),TSZ表示的是傳輸大小(單元傳輸還是突發傳輸,即1還是4),TC表示傳輸計數值(即寄存器DCONn的低20位存放的數據),因此如果需要傳輸的字節大小超出了這三個參數乘積的大小,則還要進一步處理,在我們給出的程序中,我們就考慮了這方面的問題。下面就是具體的程序,其中我們是利用UART來實現音頻信號的播出、停止、暫停、靜音、音量的提高和降低的。


        …………
        //一段純音頻數據數組
        unsigned char music[] = {
        0xF9, 0xFF, 0xF5, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xF9, 0xFF,
        0xF6, 0xFF, 0xF6, 0xFF, 0xFA, 0xFF, 0xFD, 0xFF, 0xFA, 0xFF, 0xFA, 0xFF, 0xF7, 0xFF, 0xF6, 0xFF,
        …………
        };

        int result;
        int remainder;
        char flag;
        char cmd;
        char play_state;

        void __irq uartISR(void)
        {
        char ch;
        rSUBSRCPND |= 0x1;
        rSRCPND |= 0x1<<28;
        rINTPND |= 0x1<<28;
        ch=rURXH0;

        switch(ch)
        {
        case 0x55://播放
        cmd = 1;
        break;
        case 0x1://靜音
        cmd = 0x11;
        break;
        case 0x2://音量提高
        cmd = 0x12;
        break;
        case 0x3://音量降低
        cmd = 0x13;
        break;
        case 0x66://停止
        cmd = 0x2;
        break;
        case 0x77://暫停
        cmd = 0x3;
        break;
        }
        rUTXH0=ch;
        }

        //放音子程序
        void playsound(unsigned char *buffer,int length)
        {
        //用于計算音頻數據的長度是否超過DMA所能傳輸的字節數范圍
        //這里音頻數據的通道位數為16位,因此需要length除以2
        remainder = (length>>1) & 0xfffff;//余數
        result = (length>>1) / 0x100000;//商

        play_state = 1;//置播放標志

        rGPBDAT = rGPBDAT & ~(L3M|L3C|L3D) |(L3M|L3C);

        //配置1341,詳細講解請看上一篇文章
        WriteL3(0x14 + 2,1);
        WriteL3(0x60,0);

        WriteL3(0x14 + 2,1);
        WriteL3(0x10,0);

        WriteL3(0x14 + 2,1);
        WriteL3(0xc1,0);

        //配置IIS
        rIISPSR= 3<<5|3;
        rIISCON= (1<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);//發送IIS的DMA使能
        rIISMOD= (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
        rIISFCON = (1<<15)|(1<<13); //發送FIFO為DMA

        //配置DMA
        rDISRC2 = (U32)buffer;//DMA的源基址為音頻數據數組的首地址
        rDISRCC2 = (0<<1)|(0<<0);//AHB,源地址遞增
        rDIDST2 = (U32)IISFIFO;//DMA的目的基址為IIS的FIFO
        rDIDSTC2 = (0<<2)| (1<<1)|(1<<0);//當傳輸計數值為0時中斷,APB,目的地址不變
        if (result == 0)//所傳輸的字節數沒有超出DMA的最大傳輸范圍
        {
        flag = 0;//清標志,表示沒有超出范圍,進入DMA中斷后結束DMA操作
        //握手模式,PCLK同步,傳輸計數中斷,單元傳輸,單步服務模式,IISSDO,
        //硬件請求模式,非自動重載,半字,
        rDCON2 = (1<<31) | (0<<30) | (1<<29) | (0<<28) | (0<<27) | (0<<24) | (1<<23) | (1<<22) | (1<<20) | (remainder);

        }
        else//所傳輸的字節數超出了DMA的最大傳輸范圍
        {
        flag = 1;//置標志,表示超出范圍
        rDCON2 = (1<<31) | (0<<30) | (1<<29) | (0<<28) | (0<<27) | (0<<24) | (1<<23) | (1<<22) | (1<<20) | (0xfffff);
        }
        rDMASKTRIG2=(0<<2)|(1<<1)|0;//不停止DMA,DMA通道開啟,非軟件觸發

        //啟動IIS
        rIISCON |= 0x1;
        }


        void __irq DMA_end(void)
        {
        rSRCPND |= 0x1<<19;
        rINTPND |= 0x1<<19;

        if (flag == 0)//DMA傳輸完畢
        {
        rIISCON = 0x0;//關閉IIS
        rIISFCON = 0x0;//清IIS的FIFO
        rDMASKTRIG2=1<<2;//停止DMA
        play_state = 0;//清播放標志
        }
        else//DMA沒有傳輸完畢,繼續傳輸
        {
        result --;//商遞減
        rDISRC2 += 0x200000;//DMA源基址遞增。因為傳輸的數據是半字,所以這里遞增0x200000
        if (result == 0 )//只剩下余數部分需要傳輸
        {
        rDCON2=(rDCON2&(~0xfffff))|(remainder);//需要重新設置傳輸計數值
        flag=0;//清標志
        }
        rDMASKTRIG2=(0<<2)|(1<<1)|0;//需要重新設置DMA通道的開啟
        }
        }

        void Main(void)
        {

        char mute;
        char volume;

        …………

        rSRCPND = (0x1<<19)|(0x1<<28);
        rSUBSRCPND = 0x1;
        rINTPND = (0x1<<19)|(0x1<<28);
        rINTSUBMSK = ~(0x1);
        rINTMSK = ~((0x1<<19)|(0x1<<28));//開啟DMA2中斷屏蔽
        pISR_UART0 = (U32)uartISR;
        pISR_DMA2=(U32)DMA_end;

        result=0;
        remainder=0;
        flag=0;
        cmd=0;
        play_state =0;

        while(1)
        {
        switch(cmd)
        {
        case 0x1://播放
        if (play_state==0)
        {
        volume = 0;//音量清零
        mute=0xa0;//初始化靜音
        playsound(music,sizeof(music));
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        case 0x2://停止
        if (play_state==1)
        {
        rIISCON = 0x0;//停止IIS
        rIISFCON = 0x0;//清IIS的FIFO
        rDMASKTRIG2=1<<2;//終止DMA2
        flag = 0;
        play_state = 0;
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        case 0x3://暫停,
        if(play_state == 1)
        {
        rIISCON ^= 0x1;//異或
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        case 0x11://靜音
        if (play_state==1)
        {
        mute ^= 0x4;
        WriteL3(0x14 + 0,1);//DATA0 (000101xx+00)
        WriteL3(mute,0);//10,1,00,x,00:x,靜音
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        case 0x12://音量遞增
        if (play_state==1)
        {
        if(volume>0)
        {
        volume --;
        WriteL3(0x14 + 0,1);//DATA0 (000101xx+00)
        WriteL3(volume,0);//音量提高
        }
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        case 0x13://音量遞減
        if (play_state==1)
        {
        if(volume<61)
        {
        volume++;
        WriteL3(0x14 + 0,1);//DATA0 (000101xx+00)
        WriteL3(volume,0);//音量降低
        }
        }
        else
        {
        while(!(rUTRSTAT0 & 0x2))
        ;
        rUTXH0=0xff;
        }
        cmd = 0;
        break;
        }
        }
        }


        關鍵詞: s3c2440DMA應

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 铁岭县| 保德县| 安吉县| 达日县| 芜湖市| 秦安县| 保山市| 鄂温| 武陟县| 从江县| 师宗县| 怀仁县| 娱乐| 赤城县| 林西县| 汾阳市| 五寨县| 博湖县| 富源县| 醴陵市| 江阴市| 洞头县| 兰坪| 壤塘县| 江陵县| 兴城市| 景东| 垦利县| 临漳县| 大埔县| 巴楚县| 公安县| 隆德县| 长子县| 平远县| 昭苏县| 兰西县| 龙海市| 南丰县| 凤凰县| 平顶山市|