一文看懂DSP的DMA传输(burst、transfer、wrap)

基本概念

先讲一讲基本概念和开发思路。只要把概念和思路理清楚了,就能融会贯通。

DMA

直接存储器访问。这个不用多说了。

burst

关于burst这个词,很多文献中都翻译为“突发”。从中文字面上,不太好理解。本文中还是使用burst这个词。

burst是一次DMA传输中的最小触发单元。每当DMA通道触发时进行的数据传送。触发源可以是外设事件或者软件触发。每次传输最多可以达到32个字。这个数量称为burst数量。(说明,为了理解上的方便,这里先不关心寄存器设置值与概念值的偏差。实际上,burst数量=BURST_SIZE+1。)

burst有个传输计数器(BURST_COUNT),用于表示当前还有多少个字没有完成。burst开始时,将burst数量加载到burst计数器。传输过程中每传一个字,计数器减一,一直减到0,代表burst完成。

burst还有一个状态标志(BURST_STS),burst开始时由硬件置1,完成后归0.

在burst传输过程中,每传输一个字,源地址和目的都会增加一个步长(BURST_STEP)。这个步长是个16位的有符号整数,也就是说,步长既可以是正数,也可以是负数,范围为-4096~+4095. 源地址步长和目的地址步长是分别可设的,对应着2个寄存器:SRC_BURST_STEP和DST_BURST_STEP。

transfer

一次完整的DMA数据传输称为transfer,可以包含多个burst. 所包含的burst数量称为transfer数量。

与burst类似,transfer也有传输计数器(TRANSFER_COUNT)、传输状态(TRANSFER_STS)、传输步长(SRC_TRANSFER_STEP和DST_TRANSFER_STEP)。

另外,一个transfer对应着一次完整的DMA传输,可以使能本通道的DMA中断。中断可以发生在transfer的开始,也可以发生在transfer结束。

DMA控制

有2个标志位用于控制DMA通道的传输:RUN和HALT。都是写1有效。当RUN写入1时,启动DMA传输,RUNSTS标志位由硬件置1,表示DMA正在运行。当HALT写入1时,停止DMA传输,RUNSTS标志位清零。

单次模式(ONESHOT)和连续模式(CONTINUOUS)

单次模式针对burst有效。

禁止单次模式时(ONESHOT=0),每一次burst都需要外设事件触发。如果transfer中包含N个burst,则需要N个外设事件的触发脉冲,才能完成一次DMA传输。

使能单次模式时(ONESHOT=1),只要一个外设事件的触发脉冲,就可以完成一次transfer中的N个burst。

连续模式针对transfer有效。

禁止连续模式时(CONTINUOUS=0),当启动DMA传输后,只完成一次transfer。完成后RUNSTS自动清零。要开启下一次transfer,需要再次启动DMA传输(RUN写入1)。

使能连续模式时(CONTINUOUS=1),一旦启动DMA,则RUNSTS一直为1,DMA通道一直在运行(也要等待外设事件触发才会进行数据传输),直到HALT写入1才停止运行。

典型场景

假定现在ADC的转换结果需要由DMA来传送到内存中。

为简单起见,ADC只转换2路。ADC结果分别为ADCResultA和ADCResultB。

简单用法

简单用法:每次DMA传输时,只需要将两个ADC结果输送到内存中。

一文看懂DSP的DMA传输(burst、transfer、wrap)

 在此场景中,需要配置的有:

burst数量为2;burst源地址步长SRC_BURST_STEP为1;burst目的地址步长DST_BURST_STEP为1.

burst传输过程分为2步:第1步将ADC结果A保存到内存A中;然后源址加1,指向ADC结果B,目的地址也加1,指向内存B;再将ADC结果B保存到内存B中。

 

高级用法

为了方便滤波,每次DMA传输时,需要对A和B进行4次采样,并把采样结果(共2*4=8个字)传送到内存里8个字的数组中。

在此场景中,需要配置的有:

burst数量为2;burst源地址步长SRC_BURST_STEP为1;burst目的地址步长DST_BURST_STEP为4. 因为burst传输的第2步中,内存要增加4个字(比如从A1到B1)。

transfer数量为4;

传输过程如下图所示:

一文看懂DSP的DMA传输(burst、transfer、wrap)

第1个burst传输时,把ADC的结果保存到A1和B1;

第2个burst传输时,把ADC的结果保存到A2和B2;

第3个burst传输时,把ADC的结果保存到A3和B3;

第4个burst传输时,把ADC的结果保存到A4和B4。

地址WRAP

问题的提出

在上述的传输过程中,如果仔细分析的话:

首先,在第1个burst传输的第1小步,源地址指向ADCResultA,目的地址指向A1;数据从A保存到A1;

然后,源地址加一,指向ADCResultB;目的地址加4,指向B1;数据从B保存到B1.

第一次burst传输完成。

再然后呢?

再然后就是第二次burst传输了。但是,源地址怎么才能再一次指向A呢?目标地址也应该回到A2啊。怎么办?

方案一:使用transfer步长

配置源地址transfer步长SRC_TRANSFER_STEP为-1(0xFFFF);配置目的地址transfer步长DST_TRANSFER_STEP为 -3(0xFFFD).

方案二:使用地址WRAP

DSP提供了一种“回绕”或者叫做“换行”的机制:Wrap。

源地址和目的地址可以分别配置WRAP_SIZE和WRAP_STEP。

WRAP_SIZE对应的是burst数量。当完成WRAP_SIZE个burst传输时,就发生地址WRAP。此时,在起始地址的基础上增加WRAP_STEP,重新使用该地址作为下一次burst传输的地址。这里的“起始地址”就是第一次burst的地址。

比如,上例中,可以配置源地址和目的地址的wrap_size都为1,也就是每完成一次burst传输都发生一次地址wrap。源地址的wrap_step为0,也就是回到第一次的源地址ADCResultA。目的地址的wrap_step为1,也就是说,在第一次的目的地址A1的基础上加1,变成A2.

地址指针及传输控制

涉及到的地址寄存器有8个。源地址和目的地址各4个。两种地址的处理逻辑完全相同,下面只选一种来讲述。

这4个地址中,有2个是活动地址(当前正在使用的有效地址),另2个是影子地址。又可以分为当前地址和起始地址。它们之间的关系如下:

一文看懂DSP的DMA传输(burst、transfer、wrap)

一共是4个地址和3个步长(burst步长、transfer步长和wrap步长)

 影子地址

首先看影子地址。这2个寄存器一般在程序初始化时由软件写入,指向外设或者内存的起始位置(比如,源地址指向ADC_ResultA,目的地址指向内存数组的开头)。在程序运行过程保持不变。

配置这个地址的库函数为:

//
// DMACH1AddrConfig - DMA Channel 1 Address Configuration
//
void DMACH1AddrConfig(volatile Uint16 *DMA_Dest,volatile Uint16 *DMA_Source)
{
    EALLOW;

    //
    // Set up SOURCE address:
    //
    DmaRegs.CH1.SRC_BEG_ADDR_SHADOW = (Uint32)DMA_Source;   // Point to
                                                            // beginning of
                                                            // source buffer
    DmaRegs.CH1.SRC_ADDR_SHADOW =     (Uint32)DMA_Source;

    //
    // Set up DESTINATION address:
    //
    DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)DMA_Dest;  // Point to
                                                         // beginning of
                                                         // destination buffer
    DmaRegs.CH1.DST_ADDR_SHADOW =     (Uint32)DMA_Dest;

    EDIS;
}

可以看到,配置过程中,源地址的影子地址和影子起始地址为同一个数值。目的地址也一样,影子地址和影子起始地址也相同。

加载影子地址

在启动DMA传输(即transfer开始时,图中的“start"),由影子寄存器加载到对应的活动寄存器。

burst步长和transfer步长

前面已经说过了。在burst传输过程中,每完成一个字的传输就增加burst步长;在transfer传输过程中,每完成一个burst传输就增加transfer步长。

wrap事件

在完成wrap_size个burst传输时,发生wrap事件。此时,活动的起始地址会增加wrap_step,并将增加后的值加载到当前活动地址。比如源地址的处理过程为:

SRC_BEG_ADDR_ACTIVE = SRC_BEG_ADDR_ACTIVE + SRC_WRAP_STEP
SRC_ADDR_ACTIVE = SRC_BEG_ADDR_ACTIVE

需要注意的是,当发生wrap时,transfer步长无效。当前活动地址不再是增加transfer步长后的结果,而是起始地址再加上wrap步长的结果。

总结

每一次DMA传输对应一个transfer。

可以在transfer开始或者结束时触发中断。

每个transfer会传输M个burst. 每个burst完成后地址会递增一个burst步长。

每个burst会传输N个字。每个字传输完成后地址会递增一个transfer步长。

Burst是内环,transfer是外环。

完成K个burst后,可以发生地址wrap。新的地址为起始地址+wrap步长。

附上DMA状态图:

一文看懂DSP的DMA传输(burst、transfer、wrap)

 

上一篇:[转载]Ocelot简易教程(五)之集成IdentityServer认证以及授权


下一篇:纯 CSS 实现下拉菜单尖角图标(实心+空心)