打印
[MCU]

官方例程Example_2833xAdcToDMA的分析

[复制链接]
1245|34
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tfqi|  楼主 | 2019-8-5 14:47 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
沙发
tfqi|  楼主 | 2019-8-5 14:48 | 只看该作者
主题
本文就说说Example_2833xAdcToDMA这个例程,有些地方它默认你都懂了,没有举更多的例子来帮助理解,恰恰是这些地方就可能让人百思不得解,就比如接下来要登场的这三个函数:

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

使用特权

评论回复
板凳
tfqi|  楼主 | 2019-8-5 14:48 | 只看该作者
正文

涉及到硬件电路的连接,老笨习惯先上原理图:



使用特权

评论回复
地板
tfqi|  楼主 | 2019-8-5 14:48 | 只看该作者
好,按照常规操作,接下来再看程序,先看全局再看细节,先上完整的程序:

#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);

使用特权

评论回复
5
tfqi|  楼主 | 2019-8-5 14:49 | 只看该作者

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<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
}

使用特权

评论回复
6
tfqi|  楼主 | 2019-8-5 14:49 | 只看该作者
// 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(;;);
}

使用特权

评论回复
7
tfqi|  楼主 | 2019-8-5 14:50 | 只看该作者

通过仿真器运行程序,直接RUN,我们来观察这个DMABuf1这个变量:

看图大家应该也明白DMABuf1[40]放的是什么东西了。没错,就是四通道都采样10次,分别放到DMABuf1[0-9]、DMABuf1[10-19]、DMABuf1[20-29]、DMABuf1[30-39]。

使用特权

评论回复
8
tfqi|  楼主 | 2019-8-5 14:50 | 只看该作者
好了,简要说说流程:

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.ADCRESULT0->DMABuf1[1],ADCRESULT1->DMABuf1[11],ADCRESULT2->DMABuf1[21],ADCRESULT3->DMABuf1[31]
3.ADCRESULT0->DMABuf1[2],ADCRESULT1->DMABuf1[12],ADCRESULT2->DMABuf1[22],ADCRESULT3->DMABuf1[32]
...
注:此处"->"表示值存放的位置。

使用特权

评论回复
9
tfqi|  楼主 | 2019-8-5 14:50 | 只看该作者
本文重点
程序大体上不难理解,但是以下这三个函数DMACH1BurstConfig、DMACH1TransferConfig、DMACH1WrapConfig有点难以理解。

    DMACH1BurstConfig(3,1,10);          //burst传输
    DMACH1TransferConfig(9,1,0);        //transfer传输
    DMACH1WrapConfig(1,0,0,1);          //wrap传输
好,接下来一个一个来,以下我们把Burst称为帧。

使用特权

评论回复
10
tfqi|  楼主 | 2019-8-5 14:51 | 只看该作者
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;
}

使用特权

评论回复
11
tfqi|  楼主 | 2019-8-5 14:51 | 只看该作者
结合实例

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]】

使用特权

评论回复
12
tfqi|  楼主 | 2019-8-5 14:51 | 只看该作者
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;
}

使用特权

评论回复
13
tfqi|  楼主 | 2019-8-5 14:51 | 只看该作者
结合实例

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

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

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

使用特权

评论回复
14
tfqi|  楼主 | 2019-8-5 14:52 | 只看该作者
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;
}

使用特权

评论回复
15
tfqi|  楼主 | 2019-8-5 14:52 | 只看该作者
结合实例

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。

使用特权

评论回复
16
tfqi|  楼主 | 2019-8-5 14:52 | 只看该作者
到此,现在可以解释【注解2】和【注解3】

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

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

依次从DMABuf1[0]到DMABuf1[9]。

使用特权

评论回复
17
tfqi|  楼主 | 2019-8-5 14:53 | 只看该作者
柳暗花明
到此,三个函数全部分析完。现在再回头看看:

    DMACH1BurstConfig(3,1,10);          //burst传输
    DMACH1TransferConfig(9,1,0);        //transfer传输
    DMACH1WrapConfig(1,0,0,1);          //wrap传输
是不是就明白了。

本篇只是分析了例程,并没有举例子来深入理解,鉴于**太长,老笨打算在下一篇继续讲完。

使用特权

评论回复
18
tfqi|  楼主 | 2019-8-5 14:54 | 只看该作者
好,我们继续,后篇主要是举例子加以理解这三个函数:

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


使用特权

评论回复
19
tfqi|  楼主 | 2019-8-5 14:54 | 只看该作者
还是上一个程序:

#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);

使用特权

评论回复
20
tfqi|  楼主 | 2019-8-5 14:55 | 只看该作者
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<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
}



使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

56

主题

3316

帖子

4

粉丝