DSP 28335 ADC DMA

Yokin 2024年6月19日, 转帖, 由于原贴是好贴但是有些小错误(比较影响理解), 因此修正并简化并作为学习笔记.

原贴地址如下: DSP28335入门教程:ADC to DMA 前篇(官方例程Example_2833xAdcToDMA的分析)

 DSP28335入门教程:ADC to DMA 后篇

主题


本文主要基于Example_2833xAdcToDMA这个例程,讲解DMA配置中比较费解的下面三个函数:

    DMACH1BurstConfig(3,1,10);          //burst传输
    DMACH1TransferConfig(9,1,0);        //transfer传输
    DMACH1WrapConfig(1,0,0,1);          //wrap传输


正文


原理图如下:

部分程序如下:


#define BUF_SIZE   40    // Sample buffer size
volatile Uint16 DMABuf1[BUF_SIZE];
 
volatile Uint16 *pDMADest;
volatile Uint16 *pDMASource;

void main(void)
{ 
    ...
    // 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<BUF_SIZE; i++)        //缓冲清零。注:用DMABuf1[40]={0}定义不能初始化为0
    {
        DMABuf1[i] = 0;
    }
 
    // Configure DMA Channel
    pDMADest   = &DMABuf1[0];            //Point DMA destination to the beginning of the array
    pDMASource = &AdcMirror.ADCRESULT0;  //Point DMA source to ADC result register base
    DMACH1AddrConfig(pDMADest,pDMASource);
    DMACH1BurstConfig(3,1,10);          //burst传输
    DMACH1TransferConfig(9,1,0);        //transfer传输
    DMACH1WrapConfig(1,0,0,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; // Start SEQ1
 
    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
    }                                         //For this example will re-start manually
}
 
// INT7.1
__interrupt void local_DINTCH1_ISR(void)     // DMA Channel 1
{
    // 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
    __asm ("      ESTOP0");
    for(;;);
}


通过仿真器运行程序,四通道都采样10次,分别放到DMABuf1[0-9]、DMABuf1[10-19]、DMABuf1[20-29]、DMABuf1[30-39]。流程主要如下:

A.采集四通道的ADC,从CONV00-CONV03,对应的管脚依次是ADCINA0、ADCINA1、ADCINB0、ADCINB1。

B.通过DMA存入DMABuf1[40]中,循环10次总共40个数据。循环和数据存放是这样的:

第1帧: ADCRESULT0->DMABuf1[0],ADCRESULT1->DMABuf1[10],ADCRESULT2->DMABuf1[20],ADCRESULT3->DMABuf1[30]
第2帧: ADCRESULT4->DMABuf1[1],ADCRESULT5->DMABuf1[11],ADCRESULT6->DMABuf1[21],ADCRESULT7->DMABuf1[31]
第3帧: ADCRESULT0->DMABuf1[2],ADCRESULT1->DMABuf1[12],ADCRESULT2->DMABuf1[22],ADCRESULT3->DMABuf1[32]
第4帧: ADCRESULT4->DMABuf1[3],ADCRESULT5->DMABuf1[13],ADCRESULT6->DMABuf1[23],ADCRESULT7->DMABuf1[33]
第5帧: ADCRESULT0->DMABuf1[4],ADCRESULT1->DMABuf1[14],ADCRESULT2->DMABuf1[24],ADCRESULT3->DMABuf1[34]
第6帧: ADCRESULT4->DMABuf1[5],ADCRESULT5->DMABuf1[15],ADCRESULT6->DMABuf1[25],ADCRESULT7->DMABuf1[35]
第7帧: ADCRESULT0->DMABuf1[6],ADCRESULT1->DMABuf1[16],ADCRESULT2->DMABuf1[26],ADCRESULT3->DMABuf1[36]
第8帧: ADCRESULT4->DMABuf1[7],ADCRESULT5->DMABuf1[17],ADCRESULT6->DMABuf1[27],ADCRESULT7->DMABuf1[37]
第9帧: ADCRESULT0->DMABuf1[8],ADCRESULT1->DMABuf1[18],ADCRESULT2->DMABuf1[28],ADCRESULT3->DMABuf1[38]
第10帧:ADCRESULT4->DMABuf1[9],ADCRESULT5->DMABuf1[19],ADCRESULT6->DMABuf1[29],ADCRESULT7->DMABuf1[39]
进入local_DINTCH1_ISR中断

注:此处"->"表示值存放的位置,“帧(burst)”的意思下文讲解。

本文重点


程序大体上不难理解,但是以下这三个函数DMACH1BurstConfig、DMACH1TransferConfig、DMACH1WrapConfig有点难以理解。

DMACH1BurstConfig(3,1,10);          //burst传输
DMACH1TransferConfig(9,1,0);        //transfer传输
DMACH1WrapConfig(1,0,0,1);          //wrap传输


好,接下来一个一个来,以下我们把Burst称为帧。

DMACH1BurstConfig

函数原型:

/*
* burst传输:burst传输是由每一个ADC中断标志触发,ADC每次转化完成,该传输模式启动。
* void DMACH1BurstConfig(Uint16 bsize, int16 srcbstep, int16 desbstep)
* bsize    :一帧传输的字数
* srcbstep :源地址步长。每次传输完一个字后增加一个步长。
* desbstep :目的地址步长。每次传输完一个字后增加一个步长。
*/
void DMACH1BurstConfig(Uint16 bsize, int16 srcbstep, int16 desbstep)
{
    EALLOW;
    // Set up BURST registers:
    DmaRegs.CH1.BURST_SIZE.all = bsize;   // Number of words(X-1) x-ferred in a burst
    DmaRegs.CH1.SRC_BURST_STEP = srcbstep;// Increment source addr between each word x-ferred
    DmaRegs.CH1.DST_BURST_STEP = desbstep;// Increment dest addr between each word x-ferred
    EDIS;
}

结合实例

DMACH1BurstConfig(3,1,10);

bsize = 3:表示一帧传输3+1=4个字(WORD),比如说例程一帧:

//第一个WORD            第二个WORD               第三个WORD              第四个WORD
ADCRESULT0->DMABuf1[0],ADCRESULT1->DMABuf1[10],ADCRESULT2->DMABuf1[20],ADCRESULT3->DMABuf1[30]

srcbstep = 1:表示每个WORD传输完成后源地址的偏移+1,实例中ADCRESULT0往后偏移1后就是ADCRESULT1。此时pDMASource = pDMASource + 1。【注解1:为方便理解,此处pDMASource表示当前源指针位置,下同,不等同于&ADCRESULT0】

desbstep = 10:表示每个WORD传输完成后目的地址的偏移+10,实例中DMABuf1[0]往后偏移10就是DMABuf1[10]。此时pDMADest = pDMADest + 10。【注解1:此处pDMADest 表示当前目的指针位置,下同,不等同于&DMABuf1[0]】

DMACH1TransferConfig

该函数是控制DMA中断的,函数原型:

/*
 * transfer传输
 * 在上一次burst传输完成后,源和目的地址的基础上进行偏移。
 * tsize    :每tsize+1帧传输后中断一次
 * srctstep :每 一 帧 传 输 后,源地址偏移,可以为负数,负增长。
 * deststep :每 一 帧 传 输 后,目的地偏移,可以为负数,负增长。
 */
void DMACH1TransferConfig(Uint16 tsize, int16 srctstep, int16 deststep)
{
    EALLOW;
    // Set up TRANSFER registers:
    DmaRegs.CH1.TRANSFER_SIZE = tsize; // Number of bursts per transfer, DMA interrupt will occur after completed transfer
    DmaRegs.CH1.SRC_TRANSFER_STEP = srctstep; // TRANSFER_STEP is ignored when WRAP occurs
    DmaRegs.CH1.DST_TRANSFER_STEP = deststep; // TRANSFER_STEP is ignored when WRAP occurs
    EDIS;
}


结合实例

DMACH1TransferConfig(9,1,0);        //transfer传输

tsize = 9:表示每 9+1 = 10 帧传输后中断一次。结合实例,在刚好10帧共40个数据后,DMA中断,程序停留在中断中。

srctstep = 1:表示每一帧后,在前一帧的源地址上偏移+1。结合实例,pDMASource会从ADCRESULT0逐步偏移到ADCRESULT7【注解2: 此处只是让pDMASource增加1,不减小,它是如何回头的,请看下个函数】。

deststep = 0:表示每一帧后,在前一帧的目的地址上偏移+0,就是无偏移。【注解3: 为什么目的地址不偏移还能填满DMABuf1[40]?不着急,它不是在这里偏移的,请看下一个函数】

DMACH1WrapConfig


此函数用于控制循环,函数原型:

/*
 * wrap传输,实现循环传输
 * srcwsize :传输srcwsize+1帧后,pDMA_Source = pDMA_Source + srcwstep;
 * srcwstep :源地址回绕步长。
 * deswsize :传输deswsize+1帧后,pDMA_Dest = pDMA_Dest + deswstep;
 * deswstep :目的地址回绕步长。
 */
void DMACH1WrapConfig(Uint16 srcwsize, int16 srcwstep, Uint16 deswsize, int16 deswstep)
{
    EALLOW;
    // Set up WRAP registers:
    DmaRegs.CH1.SRC_WRAP_SIZE = srcwsize; // Wrap source address after N bursts
    DmaRegs.CH1.SRC_WRAP_STEP = srcwstep; // Step for source wrap
 
    DmaRegs.CH1.DST_WRAP_SIZE = deswsize; // Wrap destination address after N bursts
    DmaRegs.CH1.DST_WRAP_STEP = deswstep; // Step for destination wrap
    EDIS;
}


结合实例

DMACH1WrapConfig(1,0,0,1);          //wrap传输

分析之前,我们先来假想有这两个指针变量SRC_ADDR_SHADOW、DST_ADDR_SHADOW,它们的初值分别为:

SRC_ADDR_SHADOW = &AdcMirror.ADCRESULT0;

DST_ADDR_SHADOW = &DMABuf1[0];

没错,就是源数据首地址和目的缓冲区的首地址。

我们再来看参数:

srcwsize = 1:表示每1 + 1 = 2帧后,源地址回绕。回绕时,SRC_ADDR_SHADOW += srcwstep; pDMASource = SRC_ADDR_SHADOW。

srcwstep = 0:表示发生回绕时 SRC_ADDR_SHADOW += 0,pDMASource = SRC_ADDR_SHADOW。

deswsize = 0:表示每0 + 1 = 1帧后,目的地址回绕。回绕时,DST_ADDR_SHADOW += deswstep; pDMASource = SRC_ADDR_SHADOW。

deswstep = 1:表示发生回绕时 SRC_ADDR_SHADOW += 1,pDMASource = SRC_ADDR_SHADOW。

到此,现在可以解释【注解2】和【注解3】

【注解2】因为DMACH1WrapConfig(1,0,0,1)的第一个参数指明了每2帧回绕源地址,第二个参数指明了回绕后pDMASource = ADCRESULT0。

【注解3】因为DMACH1WrapConfig(1,0,0,1)的第三个参数指明了每1帧回绕目的地址,所以,每一帧之后,pDMADest都会+1。

进一步理解


我们现在来作如下改动,其他地方不变:

    ...
    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传输

没错,此时进入了「__interrupt void local_DINTCH1_ISR(void)」中断。

接着,我们改到传2帧,DMACH1TransferConfig(1,-3,1); 并去掉「for(;;){} // 在此停住!!!」循环

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位字段就行。官方手册解释如下:

DSP28335是一款数字信号处理器,DMA(Direct Memory Access)是一种数据传输方式。在DSP28335中,DMA可以用来实现高速数据传输,提高系统的效率和性能。 引用\[1\]提到了DSP28335的官方例程,官方例程是指由DSP28335官方提供的示例代码,这些代码包含了各种功能的实现方法。官方例程可以帮助开发者快速上手DSP28335开发,了解各个功能的使用方法。如果你想下载DSP28335的官方例程,可以参考《DSP28335入门教程:官方例程的下载》。 引用\[2\]和引用\[3\]提到了关于DMADSP28335中的应用。DMA可以用来实现数据的高速传输,提高系统的效率。在这个例子中,DMA被用来将四个通道的采样数据存放到DMABuf1数组中。每个通道采样10次,分别存放在DMABuf1\[0-9\]、DMABuf1\[10-19\]、DMABuf1\[20-29\]、DMABuf1\[30-39\]中。具体的存放方式是通过循环,每一帧将ADCRESULT0、ADCRESULT1、ADCRESULT2、ADCRESULT3的值存放到对应的DMABuf1位置中。 综上所述,DSP28335结合DMA可以实现高速数据传输,提高系统的效率和性能。官方例程可以帮助开发者快速上手DSP28335开发,了解各个功能的使用方法。如果你想下载DSP28335的官方例程,可以参考《DSP28335入门教程:官方例程的下载》。 #### 引用[.reference_title] - *1* *2* *3* [DSP28335入门教程:ADC to DMA 前篇(官方例程Example_2833xAdcToDMA的分析)](https://blog.csdn.net/qq_40692629/article/details/88622324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值