DSP

DSP28335入门教程:ADC to DMA 后篇

2019-07-13 15:05发布

好,我们继续,后篇主要是举例子加以理解这三个函数: 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循环处,表明没有进入中断。

DMACH1BurstConfig

因为只进行了一次ADC转换,所以也就只触发传输了一帧,共4个WORD的数据。所以由 DMACH1BurstConfig(3,1,1); //burst传输 这个函数我们知道,1帧传输数据是这样的: ADCRESULT0->DMABuf1[0],ADCRESULT1->DMABuf1[1],ADCRESULT2->DMABuf1[2],ADCRESULT3->DMABuf1[3]

DMACH1TransferConfig

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中断。

DMACH1WrapConfig

我们继续,仅改成如下 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篇的教程就到结束了。