摘自:
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");
}