DMA


摘自:
http://blog.sina.com.cn/s/blog_a7c071b30102wxaz.html
http://blog.csdn.net/ocean_ele/article/details/51417908


一.内存与外设进行数据交换的方式

a.中断方式:每传输一次数据,就必须经历中断处理的全部步骤,而且一般需要借助CPU内部的寄存器作为中介,也就是说CPU需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方,在这个时间中,CPU对于其他的工作来说就无法使用。

b.DMA模式:不用CPU的寄存器作为传输中介,完成存储器和外设间、存储器和存储器间的直接传输,需要注意的是,在DMA工作的时候,CPU必须将系统总线的控制权让给DMAC。

二.DMA操作流程

a. 外设可通过DMA控制区想CPU发出DMA请求.
b. CPU响应DMA请求,系统转变为DMA方式,并把总线控制权交给DMA控制器.
c. 由DMA控制器发送存储器地址,并决定数据块的长度.
d. 执行DMA传送.
e. DMA操作结束,并把总线控制权交给CPU.

附:微机知识
这里写图片描述

三.28335 DMA配置函数

DMACHx(DMA Channel):x=1~6

void DMACHxAddrConfig(volatile Unit16 *DMA_Dest,volatile Unit16 *DMA_Source)

//配置DMA的数据目的地地址和源地址(跟函数中参数的排序相同,下同)

解释: 源地址有两个,一个A为用于传输时(随每个字节递增),另一个B作为返回的备份(当一帧结束后,重新装载入A)
目的地址有两个,一个A为用于传输时(随每个字节递增),另一个B作为返回的备份(当一帧结束后,重新装载入A)
每次启动DMA相应通道,都会把B装载入A

void DMACHxBurstConfig(Unit16 bsize,int16 srcbstep,int16 desbstep)   

//帧循环、内循环:配置每帧多少字(word)、帧内源地址增加偏移和帧内目的地地址增加偏移。地址增加偏移就是指传输一个字的地址增量

void DMACHxTransferConfig(Unit16 tsize,int16 srctstep,int16 deststep)  

//传送循环、外循环:配置每次触发DMA转移多少帧,帧间源地址增加偏移和帧间目的地地址增加偏移。地址增加偏移就是指传输一个帧的地址增量

void DMACHxWrapConfig(Unit16 srcwsize,int16 srcwstep,Unit16 deswsize,int16 deswstep) 

//“打包”过程配置:配置源地址“打包”过程每个“打包”所含帧的个数、源地址每个“打包”完成之后的地址偏移增量、配置目的地址“打包”过程每个“打包”所含帧的个数、目的地址每个“打包”完成之后的地址偏移增量。

解释: Srcwsize:当已经传递的脉冲数为srcwsize+1的整数倍时,源地址(B)增加srcwstep(常为0),并装载入源地址A
Deswsize:当已经传递的脉冲数为deswsize+1的整数倍时,目的地址(B)增加deswstep(常为0),并装载入目的地址A

void DMACHxModeConfig(Unit16 persel,Unit16 perinte,Unit16 oneshot,Unit16 cont,Unit16 synce,Unit16 syncsel,Unit16 ovrinte,Unit16 datasize,Unit16 chintmode,Unit16 chinte)  

//DMA模式寄存器:配置外设触发事件源选择、外设触发事件使能、ONESHOT使能、连续模式使能、外设同步使能、同步模式选择(源同步或目的地同步)、超载中断使能、数据传送模式选择(16位或32位)、通道中断模式选择(开始或结束)、通道中断使能

添注:
这里写图片描述

persel--选择触发源,值为下列选项
DMA_SEQ1INT--------ADC
DMA_SEQ2INT--------ADC 
DMA_XINT1  --------外部中断
DMA_XINT2  --------外部中断 
DMA_XINT3  --------外部中断 
DMA_XINT4  --------外部中断 
DMA_XINT5  --------外部中断 
DMA_XINT6  --------外部中断 
DMA_XINT7  --------外部中断 
DMA_XINT13 --------外部中断 
DMA_TINT0  --------CPU时钟 
DMA_TINT1  --------CPU时钟 
DMA_TINT2  --------CPU时钟 
DMA_MXEVTA  --------McBSP-A
DMA_MREVTA  --------McBSP-A
DMA_MXREVTB --------McBSP-B
DMA_MREVTB  --------McBSP-B

perinte--使能触发源,值为PERINT_DISABLE或PERINT_ENABLE
oneshot--使能oneshot模式,值为ONESHOT_DISABLE或ONESHOT_ENABLE。此模式下,一次触发完成全部burst。
cont--使能Continuous模式,值为CONT_DISABLE或CONT_ENABLE。此模式下,传送完毕后DMA重新被初始化,并等待触发源。
synce--使能外围设备同步,值为SYNC_DISABLE或SYNC_ENABLE。
syncsel--同步选择。值为SYNC_SRC或SYNC_DST。
ovrinte--使能溢出中断。值为OVRFLOW_DISABLE或OVEFLOW_ENABLE。
datasize--每次传送位数。值为SIXTEEN_BIT或THIRTYTWO_BIT。
chintmode--通道中断产生模式。CHINT_BEGIN:传送开始发中断。CHINT_END:传送结束发中断。
chinte--使能通道中断。值为CHINT_DISABLE或CHINT_ENABLE。

MODE寄存器
这里写图片描述
这里写图片描述

CONTROL寄存器
这里写图片描述
这里写图片描述


/*----------------代码原型--------------------*/

void DMACH1ModeConfig(Uint16 persel, Uint16 perinte, Uint16 oneshot, Uint16 cont, Uint16 synce, Uint16 syncsel, Uint16 ovrinte, Uint16 datasize, Uint16 chintmode, Uint16 chinte)
{
    EALLOW;

    // Set up MODE Register:
    DmaRegs.CH1.MODE.bit.PERINTSEL = persel;        // Passed DMA channel as peripheral interrupt source
    DmaRegs.CH1.MODE.bit.PERINTE = perinte;         // Peripheral interrupt enable
    DmaRegs.CH1.MODE.bit.ONESHOT = oneshot;         // Oneshot enable
    DmaRegs.CH1.MODE.bit.CONTINUOUS = cont;         // Continous enable
    DmaRegs.CH1.MODE.bit.SYNCE = synce;             // Peripheral sync enable/disable
    DmaRegs.CH1.MODE.bit.SYNCSEL = syncsel;         // Sync effects source or destination
    DmaRegs.CH1.MODE.bit.OVRINTE = ovrinte;         // Enable/disable the overflow interrupt
    DmaRegs.CH1.MODE.bit.DATASIZE = datasize;       // 16-bit/32-bit data size transfers
    DmaRegs.CH1.MODE.bit.CHINTMODE = chintmode;     // Generate interrupt to CPU at beginning/end of transfer
    DmaRegs.CH1.MODE.bit.CHINTE = chinte;           // Channel Interrupt to CPU enable

    // Clear any spurious flags:
    DmaRegs.CH1.CONTROL.bit.PERINTCLR = 1;          // Clear any spurious interrupt flags
    DmaRegs.CH1.CONTROL.bit.SYNCCLR = 1;            // Clear any spurious sync flags
    DmaRegs.CH1.CONTROL.bit.ERRCLR = 1;             // Clear any spurious sync error flags

    // Initialize PIE vector for CPU interrupt:
    PieCtrlRegs.PIEIER7.bit.INTx1 = 1;              // Enable DMA CH1 interrupt in PIE

    EDIS;
}
/*----------------其他的---------------*/

void DMAInitialize(void)
{
    EALLOW;

    // Perform a hard reset on DMA
    DmaRegs.DMACTRL.bit.HARDRESET = 1;

    // Allow DMA to run free on emulation suspend
    DmaRegs.DEBUGCTRL.bit.FREE = 1;

    EDIS;
}
void StartDMACH1(void)
{
    EALLOW;
    DmaRegs.CH1.CONTROL.bit.RUN = 1;
    EDIS;
}

四.详解
1) 28335DMA模块具有独立PIE中断的6个通道 ,字长度为16位或32位(McBSPs限制为16位)。其中DMA总线包含22位的地址线,32位的读总线和32位的写总线。连接到DMA总线上的存储器和寄存器通过接口与CPU存储器或外设总线共享资源。

2) DMA状态机是两级嵌套循环。

BURST_SIZE寄存器定义帧的长度大小(每帧最多32个16位字),内循环;
TRANSFER_SIZE寄存器定义整个传送过程中共传送多少个这样的帧数据,外循环;每次传送可以产生一个CPU中断(若中断使能),该中断可以通过MODE.CHx[CHINTMODE]位配置为在每次传送开始或结束时刻产生。

在MODE.CHx[ONESHOT]位默认设置下,DMA在每接收一个中断触发事件信号时传送一帧数据。此时若单个触发事件要求输送的数据大于允许传送字的最大值,这是不允许某一触发事件独占DMA总线。此时可以通过配置MODE.CHx[ONESHOT]位配置来完成整个数据帧的传递任务。需要注意的是,这种模式下可能会发生某一触发事件独占大部分DMA带宽的情况。

3) 每个DMA通道包含了源地址(SRC_ADDR)和目的地址(DST_ADDR)的映射地址指针。在每次传送开始时,映射(shadow)寄存器中的地址会复制到相应的当前工作(active)寄存器中。

在帧循环(BURST_LOOP)中,每个字传送完毕后,源地址和目的地址的BURST_STEP寄存器中的值会加到当前工作的SRC/DAT_ADDR上,用以修改当前工作的地址指针。
在传送循环(TRANSFER_LOOP)中,每一帧传送完毕后,有两种方法修改当前工作地址指针:

方法一(默认):将SRC/DST_TRANSFER_STEP寄存器中的值加到相应的地址指针上

方法二:“Wrapping(打包)”的过程。该方法中,一个数据打包的地址装在到当前工作的地址指针中(即赋值)。当一个打包过程发生后,相应的SRC/DST_TRANSFER_STEP寄存器内容将被忽略。当SRC/DST_TRANSFER_SIZE所定义的一定数量的帧数据传送完毕后,地址打包过程发生。每个DMA通道包含了两个打包地址指针:SRC_BEG_ADDR和DST_BEG_ADDR。这两个指针已被映射,源打包地址和目的打包地址可以独立配置。与SRC_ADDR和DST_ADDR寄存器一样,在每个传送的开始,当前工作的SRC_BEG_ADDR和DST_BEG_ADDR寄存器将载入与之相对应的映射寄存器的内容。当一定数量的帧数据传送完毕之后,“打包”过程分两步发生,首先当前工作寄存器SRC/DST_BEG_ADDR按照SRC/DST_WARP_STEP寄存器中的定义值增加;然后新的当前工作寄存器SRC/DST_BEG_ADDR内容被加载到SRC/DST_ADDR寄存器中。此外,数据打包计数器(SRC/DST_WRAP_COUNT)寄存器重新载入SRC/DST_WARP_SIZE的值,启动下一个“打包”周期。

在地址指针中,DMA分别包含了当前工作(active)和映射(shadow)寄存器组。当DMA传送开始时,映射寄存器组的内容复制到当前工作的寄存器组。这就允许用户在DMA工作于当前工作的寄存器组时,对映射寄存器组编程,为下次传送做准备。

五.栗子

#include "DSP2833x_Device.h"     // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h"   // DSP2833x Examples Include File


#define BUF_SIZE   1024  // Sample buffer size


#pragma DATA_SECTION(DMABuf1,"DMARAML4");
#pragma DATA_SECTION(DMABuf2,"ZONE6DATA");

volatile Uint16 DMABuf1[BUF_SIZE];
volatile Uint16 DMABuf2[BUF_SIZE];

volatile Uint16 *DMADest;
volatile Uint16 *DMASource;

interrupt void local_DINTCH1_ISR(void);
void init_zone6(void);


void main(void)
{
   Uint16 i;
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
   InitSysCtrl();


// Step 2. Initialize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio();  // Skipped for this example

// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
   DINT;

// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
   InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
   IER = 0x0000;
   IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example.  This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
   InitPieVectTable();

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
   EALLOW;  // Allow access to EALLOW protected registers
   PieVectTable.DINTCH1= &local_DINTCH1_ISR;
   EDIS;   // Disable access to EALLOW protected registers
   IER = M_INT7 ;                                //Enable INT7 (7.1 DMA Ch1)
   EnableInterrupts();
   CpuTimer0Regs.TCR.bit.TSS  = 1;               //Stop Timer0 for now


//Step 5. User specific code, enable interrupts:
  // Initialize DMA 
    DMAInitialize();
    init_zone6();

    // Initialize Tables
   for (i=0; i<BUF_SIZE; i++)
   {
     DMABuf1[i] = 0;
     DMABuf2[i] = i;
   }


// Configure DMA Channel
    DMADest   = &DMABuf1[0];
    DMASource = &DMABuf2[0];
    DMACH1AddrConfig(DMADest,DMASource);
    DMACH1BurstConfig(31,2,2);         //Will set up to use 32-bit datasize, pointers are based on 16-bit words
    DMACH1TransferConfig(31,2,2);      //so need to increment by 2 to grab the correct location
    DMACH1WrapConfig(0xFFFF,0,0xFFFF,0);
    //Use timer0 to start the x-fer.  
    //Since this is a static copy use one shot mode, so only one trigger is needed
    //Also using 32-bit mode to decrease x-fer time
    DMACH1ModeConfig(DMA_TINT0,PERINT_ENABLE,ONESHOT_ENABLE,CONT_DISABLE,SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,THIRTYTWO_BIT,CHINT_END,CHINT_ENABLE); 





    StartDMACH1();


   //Init the timer 0

   CpuTimer0Regs.TIM.half.LSW = 512;    //load low value so we can start the DMA quickly
   CpuTimer0Regs.TCR.bit.SOFT = 1;      //Allow to free run even if halted
   CpuTimer0Regs.TCR.bit.FREE = 1; 
   CpuTimer0Regs.TCR.bit.TIE  = 1;      //Enable the timer0 interrupt signal
   CpuTimer0Regs.TCR.bit.TSS  = 0;      //restart the timer 0
   for(;;);

}

// INT7.1
interrupt void local_DINTCH1_ISR(void)     // DMA Channel 1
{
   Uint16 i;  
  // To receive more interrupts from this PIE group, acknowledge this interrupt 
   PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;

  // Next two lines for debug only to halt the processor here
  // Remove after inserting ISR Code
   for (i=0; i<BUF_SIZE; i++)
   {
     if(DMABuf1[i] != i)
     {
        asm ("      ESTOP0");//error
        break;
     }
   }
   asm ("      ESTOP0");//ok
   for(;;);
}


// Configure the timing paramaters for Zone 6.
// Notes: 
//    This function should not be executed from XINTF
//    Adjust the timing based on the data manual and
//    external device requirements.     
void init_zone6(void)
{
    EALLOW;
    // Make sure the XINTF clock is enabled
    SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 1;
    EDIS; 
    // Configure the GPIO for XINTF with a 16-bit data bus
    // This function is in DSP2833x_Xintf.c
    InitXintf16Gpio();


    // All Zones---------------------------------
    // Timing for all zones based on XTIMCLK = SYSCLKOUT 
    EALLOW;
    XintfRegs.XINTCNF2.bit.XTIMCLK = 0;
    // Buffer up to 3 writes
    XintfRegs.XINTCNF2.bit.WRBUFF = 3;
    // XCLKOUT is enabled
    XintfRegs.XINTCNF2.bit.CLKOFF = 0;
    // XCLKOUT = XTIMCLK 
    XintfRegs.XINTCNF2.bit.CLKMODE = 0;   

    // Zone 6------------------------------------
    // When using ready, ACTIVE must be 1 or greater
    // Lead must always be 1 or greater
    // Zone write timing
    XintfRegs.XTIMING6.bit.XWRLEAD = 1;
    XintfRegs.XTIMING6.bit.XWRACTIVE = 2;
    XintfRegs.XTIMING6.bit.XWRTRAIL = 1;
    // Zone read timing
    XintfRegs.XTIMING6.bit.XRDLEAD = 1;
    XintfRegs.XTIMING6.bit.XRDACTIVE = 3;
    XintfRegs.XTIMING6.bit.XRDTRAIL = 0;

    // don't double all Zone read/write lead/active/trail timing 
    XintfRegs.XTIMING6.bit.X2TIMING = 0;

    // Zone will not sample XREADY signal  
    XintfRegs.XTIMING6.bit.USEREADY = 0;
    XintfRegs.XTIMING6.bit.READYMODE = 0;

    // 1,1 = x16 data bus
    // 0,1 = x32 data bus
    // other values are reserved
    XintfRegs.XTIMING6.bit.XSIZE = 3;
    EDIS; 
   //Force a pipeline flush to ensure that the write to 
   //the last register configured occurs before returning.  
   asm(" RPT #7 || NOP"); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值