Date: 7-Dec-2011
By: Calvinlee1984@163.com
1.DMA介绍
高速外设(I/O)与系统内存(Memory)或者在系统内存不同区域之间进行大量数据的快速传送时,轮询方式和中断方式可能无法满足传送要求。采用直接存储器存取(Direct Memory Access)方式,在某一时间段内,DMAC取代CPU,获得总线控制权,负责数据的传送,因此省去了CPU取指令、取数、送数等操作。在数据传送过程中,没有保存/恢复现场之类的工作,内存地址修改,传送字计数等也是用硬件线路直接实现的,所以DMA方式能满足高速I/O设备的请求,也有利于CPU效率的发挥。
1)DMA系统组成
DREQ(DMA Request): DMA请求输入信号,是外部电路为取得DMA服务而送到各DMA通道的请求信号,在相应的DACK信号产生前DREQ必须维持有效;
HRQ(Hold Request): 保持请求信号,通常接到CPU的HOLD引脚,用来向CPU请求对系统总线的控制权。在HRQ有效之后,至少等待一个时钟周期,HLDA信号才会有效;
HLDA(Hold Acknowledge):保持响应信号,来自CPU的同意让出总线响应信号,表示CPU已经让出对总线控制权而交给DMAC;
DACK(DMA acknowledge):DMA响应输出信号,DMAC利用这个信号通知外部设备已经被授予一个DMA周期了,可利用有效的DACK信号作为I/O接口的选通信号。
2)DMA工作过程:
◆外部I/O设备向DMAC发出DMA传送请求;
◆DMAC通过连接到CPU的HOLD信号向CPU提出DMA传送请求;
◆CPU在完成当前总线操作后立即对DMA请求做出响应,放弃总线控制权并将有效的HLDA信号加到DMAC上,以通知DMAC CPU已经放弃总线控制权;
◆DMAC接管系统总线的控制权,并向外部I/O设备送出DMA的应答信号;
◆DMAC送出地址和控制信号,实现外设与内存或内存之间数据的快速传送;
◆DMAC将规定的数据传送完之后,通过向CPU发HOLD信号,撤消对CPU的DMA请求。CPU收到此信号,一方面使HLDA无效,另一方面又重新开始控制总线。
3)DMAC与CPU分时使用内存的方式:
◆停止CPU访问内存
当外围设备要求传送一批数据时,由DMC发一个请求信号给CPU,要求CPU放弃对地址总线、数据总线和有关控制总线的使用权。DMAC获得控制权后,开始进行数据传送,传送完毕后,DMAC通知CPU可以使用内存,并把总线控制权交还给CPU。外围设备传送两个数据之间的间隔一般总是大于内存存储周期,例如软盘读出一个8位二制数大约需要32us,而半导体内存的存储周期小于0.5us,因此在DMAC访问阶段,许多空闲的存储周期不能被CPU利用,内在的效能没有充分发挥。
◆周期挪用
I/O设备无DMA请求时,CPU按程序要求访问内存,一旦I/O设备有DMA请求,则由I/O设备挪用一个或几个内存周期。I/0设备要求DMA传送可能遇到两种情况1)CPU不需要访内,如CPU正在执行乘法指令,此时I/O设备挪用一二个内存周期对CPU执行程序没有任何影响。2)I/O设备要求访内时CPU也要求访内,产生访内冲突,这种情况下I/O设备访内优先,因为I/O访内有时间要求,前一个I/O数据必须在下一个访内请求到来之前存取完毕。这种情况下I/O设备挪用一二个内存周期,意味着CPU延缓了对指令的执行,或者说在CPU执行访内指令的过程中插入DMA请求,挪用了一二个内存周期。
◆DMA与CPU交替访内
如果CPU的工作周期比内存存取周期长很多,此时采用交替访内的方法可以使DMA和CPU同时发挥最高的效率。一个CPU工作周期被分为C1及C2分周期,C1供DMAC访内,C2专供CPU访内。该种方式不需要总线使用权的申请、建立及归还过程,总线使用权是通过C1,C2分时制的。CPU及DMAC各有自己的访内地址寄存器、数据寄存器和读、写信号等控制寄存器。对于总线控制权的转移是通过硬件实现的,几乎不需要什么时间,所以对DMA传送来讲效率是很高的。在该种DMA方式下工作,CPU既不停止主程序的运行也不进入等待状态,是一种高效的工作方式。当然,相应的硬件逻辑也更加复杂。
2.S3C2440A DMA控制器
S3C2440A支持4通道DMA控制器,每个DMA通道都能没有约束地实现系统总线或者外围总线之间的数据传输。
1)DMA请求源
2)DMA传输方式
◆单步模式(Single service):有两个DMA应答周期分别指示读或写,这种模式通常用在测试或者调试的时候,因为在读与写之间系统总线占有权可以交给其它的主控制器。在读写周期之间,总线控制器重新估计优先级来决定新的总线控制者;
◆完整服务模式(Whole servie):如果DMA传输数量太大的话,由于其它的总线服务不能进行,此时长时间的总线占用可能会带来一些问题。为了解决这类问题,提出了完整服务模式,DMA在每个UNIT传输完成时释放总线控制权,此时其他的总线控制器(比如CPU,其他的DMA和外部总线控制器)将会获得总线的控制权。完整服务模式的这个特征可以提供最佳的总线分配状况,防止总线的使用权被某个DMA所垄断。
3)外部DMA 请求/应答协议
◆基本时序
◆需求模式及握手模式比较
需求模式(Demand mode):只要DMA请求信号持续,就会有连续的DMA传输周期。在这种模式下,在一个DMA周期中,将不允许把总线使用权交给其他提出DMA请求的优先级高的总线控制器,也就是任何其他总线控制器都不能获得总线使用权;
握手模式(Handshake mode):一个单独的DMA请求信号对应一个DMA应答信号,在该模式下,一次DMA操作意味着在DMA操作中的一对或不可分的读和写周期。由nXDREQ产生的DMA请求引起一个字节、半字或一个字被传送出去,在握手模式下,每一个数据的传送都需要DMA请求。
4)DMA传输数据类型
◆UNIT:每个DMA请求恰好发生一个DMA读/写周期,即一个字的读,然后一个字的写;
◆Burst 4:4个字突发读,然后4个字突发写。
5)请求/应答协议示例
◆Single service in Demand Mode with Unit Transfer Size
◆Single service in Handshake Mode with Unit Transfer Size
◆Whole service in Handshake Mode with Unit Transfer Size
3.本文在上一篇《FL2440无操作系统应用程序编写测试011——IIS_AUDIO》基础上利用DMA方式传输音频数据,实现在播放音乐的过程中,CPU也可执行其他程序
4.DMA寄存器设置
static void DMA2_Port_Init(void){
//DMA2
rDISRC2 = (U32)WindowsXP_Wav; //Start address
rDISRCC2 = (0<<1)|(0<<0); //Source in AHB,Increment
//Destination address
//#define IISFIFO ((volatile unsigned short *)0x55000010)
rDIDST2 = (U32)IISFIFO;
//Destination in APB,Fixed,Interrupt will occur when TC reaches 0
rDIDSTC2 = (0<<2)|(1<<1)|(1<<0);
}
5.代码分析 DMA.c
#include "2440addr.h"
#include "2440lib.h"
#include "def.h"
#include "option.h"
extern unsigned char WindowsXP_Wav[]; //243552Bytes
U32 wav_length_result, wav_length_remain;
U8 flag;
#define L3MODE (1<<2) //GPB2
#define L3DATA (1<<3) //GPB3
#define L3CLOCK (1<<4) //GPB4
//UDA131TS L3总线接口写入函数
//address为0地址模式,否则为数据传输模式
//data为传输数据
static void WriteL3(U8 address,U8 data){
int i, j;
if(!address){ //地址模式
//L3CLOCK=High,L3MODE=Low
rGPBDAT = rGPBDAT & ~(L3MODE|L3DATA|L3CLOCK )|L3CLOCK;
}else{ //数据传输模式
//L3CLOCK=High,L3MODE=High
rGPBDAT=rGPBDAT & ~(L3MODE|L3DATA|L3CLOCK)|(L3CLOCK|L3MODE);
}
for(j = 0; j < 5; j++); //delay
//将字节数据按串行格式发送,低位在前,高位在后
for(i = 0; i < 8; i++){
if(data & 0x1){ //Bit[i]=1
rGPBDAT &= ~L3CLOCK; //L3CLOCK=Low
rGPBDAT |= L3DATA; //L3DATA =High
for(j = 0; j < 5; j++);
rGPBDAT |= L3CLOCK; //L3CLOCK=High
rGPBDAT |= L3DATA; //L3DATA =High
for(j = 0; j < 5; j++);
}else{ //Bit[i]=0
rGPBDAT &= ~L3CLOCK; //L3CLOCK=Low
rGPBDAT &= ~L3DATA; //L3DATA =High
for(j = 0; j < 5; j++);
rGPBDAT |= L3CLOCK; //L3CLOCK=High
rGPBDAT &= ~L3DATA; //L3DATA =Low
for(j = 0; j < 5; j++);
}
data >>= 1; //Next bit
}
}
//UDA1341TS L3 controller Initialize
static void UDA1341TS_L3_Init(void){
//Set GPB[4:2] as output and Disable pull-up function
rGPBCON = rGPBCON & ~(0x3f<<4) | (0x15<<4);
rGPBUP = rGPBUP & ~(0x7<<2) | (0x7<<2);
//配置UDA1341TS L3
//状态模式
WriteL3(0,0x14+2);
WriteL3(1,0x40); //0100_0000 Reset
WriteL3(0,0x14+2);
WriteL3(1,0x10); //0001_0000 System Clock 384fs,No DC filter,I2S-bus
WriteL3(0,0x14+2);
WriteL3(1,0xc1); //1100_0001 DAC Output gain 6dB,ADC off,DAT on
}
//IIS Port Initialize
static void IIS_Port_Init(void){
//Set GPE[4:0] as IIS Port and Disable pull-up function
rGPECON = rGPECON & ~(0x3ff) | 0x2aa;
rGPEUP = rGPEUP & ~(0x1f) | 0x1f;
//DMA开启,在接受空闲状态,不产生IISLRCK信号,IIS预分频使能
rIISCON = (1<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);
//主设备时钟PCLK,主设备模式,发送模式,IIS总线格式
//串行数据(量化位数)16位,主时钟是384fs,串行位时钟32fs
rIISMOD = (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
//fs=22.050Khz,CODECLK=384fs=8.4672Mhz
//预分频N=PCLK/CODECLK-1)=33.8571/8.4672-1=2.9986
//取N=3
rIISPSR = (3<<5)|3;
//DMA方式发送FIFO,发送FIFO使能
rIISFCON = (1<<15)|(1<<13);
}
static void DMA2_Port_Init(void){
//DMA2
rDISRC2 = (U32)WindowsXP_Wav; //Start address
rDISRCC2 = (0<<1)|(0<<0); //Source in AHB,Increment
//Destination address
//#define IISFIFO ((volatile unsigned short *)0x55000010)
rDIDST2 = (U32)IISFIFO;
/Destination in APB,Fixed,Interrupt will occur when TC reaches 0
rDIDSTC2 = (0<<2)|(1<<1)|(1<<0);
}
//DMA2中断处理程序
static void __irq DMA2_ISR(void){
U32 r;
EnterCritical(&r);
ClearPending(BIT_DMA2); //清除中断信号源
if(flag){
//IIS close
rIISCON = 0x0;
//DMA2 Turn off
rDMASKTRIG2 = (1<<2)|(0<<1)|(0<<0);
}else{
wav_length_result--;
rDISRC2 += 0x200000;
if(!wav_length_result){
flag = 1;
rDCON2 = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(wav_length_remain<<0);
}
//DMA2 Turned on again
rDMASKTRIG2 = (0<<2)|(1<<1)|(0<<0);
}
ExitCritical(&r);
}
//PlayWave
static void PlayWave(U8 buffer[], U32 length){
//获取单次DMA传输的TC值
//TC=length/TSZ/DSZ
wav_length_result = (length>>1)>>20;
wav_length_remain = (length>>1)&0xfffff;
if(!wav_length_result){
flag = 1;
//Handshake Mode,Synchronized to PCLK,
//Enable Interrupt,TSZ:unit,Single service
//DMA request source:I2SSDO,H/W request mode,No auto reload
//DSZ:Half word,TC:length/TSZ/DSZ
rDCON2 = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(wav_length_remain<<0);
}else{
flag = 0;
rDCON2 = (1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(0<<24)|(1<<23)|(1<<22)|(1<<20)|(0xfffff<<0);
}
//DMA2 Turn on
rDMASKTRIG2 = (0<<2)|(1<<1)|(0<<0);
//IIS start
rIISCON |= 0x1;
}
void DMA2_IIS_PlayMusic_Test(void){
U32 Save_rMPLLCON = rMPLLCON; //PCLK=50Mhz
UDA1341TS_L3_Init(); //UDA1341TS L3 controller Initialize
IIS_Port_Init(); //IIS Port Initialize
DMA2_Port_Init(); //DMA2 Port Initialize
pISR_DMA2 = (U32)DMA2_ISR; //Set up ISR for DMA2
EnableIrq(BIT_DMA2); //Enable Interrupt of DMA2
rMPLLCON = (150<<12)|(5<<4)|(1<<0); //PCLK= 33.8571Mhz
PlayWave(WindowsXP_Wav, 243552);
rMPLLCON= Save_rMPLLCON; //PCLK=50Mhz
DisableIrq(BIT_DMA2); //Disable Interrupt of DMA2
}
6.测试程序及结果
#include "UART.h"
#include "DMA.h"
int Main(void)
{
UART0_Port_Init(115200); //UART端口初始化
UART0_Printf("Play Music...\n");
DMA2_IIS_PlayMusic_Test(); //播放音频文件
UART0_Printf("End\n");
while(1){
;
}
return 0;
}
注:播放声音的效果与上一篇一样,但输出“End”字符串是与放音同时进行的,上一篇必须结束放音时才输出。
因此用DMA方式传输大量数据,可显著提高CPU的效率。