好,接着ADC to DMA 前篇我们继续,后篇主要是举例子加以理解这三个函数:
DMACH1BurstConfig(3,1,10); //burst传输
DMACH1TransferConfig(9,1,0); //transfer传输
DMACH1WrapConfig(1,0,0,1); //wrap传输
还是上一个程序:
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
#define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
#define ADC_SHCLK 0xf // S/H width in ADC module periods = 16 ADC clocks
#define BUF_SIZE 40 // Sample buffer size
// Global variable for this example
Uint16 j=0;
#pragma DATA_SECTION(DMABuf1,"DMARAML4");
volatile Uint16 DMABuf1[BUF_SIZE];
volatile Uint16 *pDMADest;
volatile Uint16 *pDMASource;
__interrupt void local_DINTCH1_ISR(void);
void main(void)
{
Uint16 i;
// step 1. 时钟配置
InitSysCtrl(); //初始系统时钟
EALLOW; //允许编辑受保护的寄存器
SysCtrlRegs.HISPCP.all = 0x3; //配置高速外设时钟 HSPCLK = SYSCLKOUT/6 = 25M
EDIS; //禁止编辑,与EALLOW成对出现
// Step 2. 初始化GPIO
//本例不需要
// Step 3. CUP和PIE中断配置
DINT; //禁用CPU中断
InitPieCtrl(); //寄存器复位置零
IER = 0x0000; //清除CPU中断标识
IFR = 0x0000;
InitPieVectTable(); //初始中断向量表
EALLOW;
PieVectTable.DINTCH1= &local_DINTCH1_ISR;
EDIS;
IER = M_INT7 ; //Enable INT7 (7.1 DMA Ch1)
EnableInterrupts(); //使能PIE和CPU中断
// Step 4. 初始化外设
InitAdc(); //使能ADC时钟和校准
//配置ADC
AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;
AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;
AdcRegs.ADCTRL1.bit.SEQ_CASC = 0; // 0 Non-Cascaded Mode
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 0x1;
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 0x1;
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; //选择管脚ADCINA0
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; //ADCINA1
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x8; //ADCINB0
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x9; //ADCINB1
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x0;
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x1;
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x8;
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x9;
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3; // Set up ADC to perform 4 conversions for every SOC
//Step 5. User specific code
DMAInitialize(); //DMA初始化
for (i=0; i
我们现在来作如下改动,其他地方不变:
DMACH1BurstConfig(3,1,1); //burst传输
DMACH1TransferConfig(9,1,0); //transfer传输
DMACH1WrapConfig(100,0,100,1); //wrap传输
DMACH1ModeConfig(DMA_SEQ1INT,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE,SYNC_DISABLE,SYNC_SRC,
OVRFLOW_DISABLE,SIXTEEN_BIT,CHINT_END,CHINT_ENABLE);
StartDMACH1();
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 0x1; // 触发一次ADC转换
for(;;){} // 在此停住
for(i=0;i<10;i++)
{
for(j=0;j<1000;j++){}
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1; //Normally ADC will be tied to ePWM, or timed routine
}
现在来看看程序运行的结果:
我们可以看到,DMABuf1只有四个数据,分别存了四通道的转换值。并且暂停后程序停在了for循环处,表明没有进入中断。
因为只进行了一次ADC转换,所以也就只触发传输了一帧,共4个WORD的数据。所以由
DMACH1BurstConfig(3,1,1); //burst传输
这个函数我们知道,1帧传输数据是这样的:
ADCRESULT0->DMABuf1[0],ADCRESULT1->DMABuf1[1],ADCRESULT2->DMABuf1[2],ADCRESULT3->DMABuf1[3]
DMACH1TransferConfig(9,1,0); //transfer传输
只传输了1帧,没有达到第一个参数指定的10帧,所以不会触发中断。第二个参数1,第三个参数0,所以,现在源数据的指针指向的是ADCRESULT4,目的指针指向的还是上一帧后的DMABuf1[3]。
因为只传了一帧,所以第二个参数在这里起不到任何作用。
我们把9改成0再运行看看:
DMACH1TransferConfig(0,1,0); //transfer传输
没错,进入了中断。
我们改到传2帧,DMACH1TransferConfig(1,-3,1);
DMACH1TransferConfig(1,-3,1); //transfer传输
//...
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 0x1; // 触发一次ADC转换
for(j=0;j<1000;j++){} //延迟
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 0x1; // 再触发一次ADC转换
for(;;){}
//...
看结果
-3的偏移导致源指针从ADCRESULT3回到了ADCRESULT0。而目的地址指针从DMABuf1[3]偏移+1移到了DMABuf1[4],第二帧的目的地址就从DMABuf1[4]开始了。还发现两帧之后也进入了DMA中断。
我们继续,仅改成如下
DMACH1BurstConfig(3,1,1); //没变
DMACH1TransferConfig(100,0,0); //不起作用
DMACH1WrapConfig(0,0,0,4); //实现循环
再看看结果:
没错,效果和和上面的DMACH1TransferConfig(1,-3,1);完全一样!
每1帧后,源数据指针等于&ADCRESULT0往后累加0,所以每帧后都回到ADCRESULT0。每1帧后,目的地址指针等于&DMABuf1[0]往后累加4。(即第一帧后指向DMABuf1[4],第二帧后指向DMABuf1[8],第三帧后指向DMABuf1[12]……以此类推)
还有一点要说的是 ,
DMACH1TransferConfig(9,0,0); //transfer传输
DMACH1WrapConfig(0,0,0,4); //wrap传输
这两个函数都会导致地址偏移,但是不会同时发生,可以在DMACH1TransferConfig函数原型中看到TRANSFER_STEP is ignored when WRAP occurs,就是说,不管是源地址和目的地址,如果发生了DMACH1WrapConfig回绕,那么DMACH1TransferConfig的第二和第三个参数就被忽略掉。
如果不想要DMACH1WrapConfig进行循环呢,我要怎么关闭这个功能?这个功能没有所谓的disable,把wrapsize位字段设置为大于transfersize位字段就行。官方手册解释如下:
好了,例子就举这么多,不知道各位明白没有,原谅老笨的文采太差。
前篇的分析中还有一点没有交代:
//...
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; //选择管脚ADCINA0
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; //ADCINA1
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x8; //ADCINB0
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x9; //ADCINB1
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x0;
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x1;
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x8;
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x9;
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3; // Set up ADC to perform 4 conversions for every SOC
//...
DMACH1WrapConfig(1,0,0,1); //wrap传输
//...
为什么CONV00-CONV07都设置了通道,最后却只开了4个转换(conversion)?
不知道你发现没有,CONV04-CONV07的通道设置和CONV00-CONV03是重复的,依次是通道0x0,0x1,0x2,0x8,0x9。
实际上只是转换了通道0x0,0x1,0x2,0x8,0x9,因为通道一样,所以CONV04-CONV07和CONV00-CONV03一样,都是
通道0x0,0x1,0x2,0x8,0x9的转换结果。
DMACH1WrapConfig(1,0,0,1)第一个参数表示2帧后回绕源地址,所以源地址在ADCRESULT0-ADCRESULT7循环,结果也是正确的。
到这里有人就会想到的了,那我设置成DMACH1WrapConfig(0,0,0,1),让源地址在ADCRESULT0-ADCRESULT3循环不是也行?没错,
第一个参数改成0也行,那是必然的,至于例程为什么这么做,估计是在尽量短的例程里展现最全的用法。
以下是改成DMACH1WrapConfig(0,0,0,1)的结果:
好了,ADC to DMA篇的教程就到结束了。