s3c2440的DMA应用

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;

              }           

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值