This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADC to DMA transfer

Hello,

I'm trying to setup dma to take value from the adc fifo registry but have been only been able to do so if i setup the adc everytime i use it. Is there a way to continuously push values from adc to the dma so that i can use it later?

Barebones code can be found at http://pastebin.com/2u9n6PJ1

tDMAControlTable ui8ControlTable[64];
uint32_t DMAdata[4];
 
 
int main(void) {
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
 
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2);
 
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
 
        ADCSequenceDisable(ADC0_BASE, 2);
 
        ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PWM1, 0x00);
        HWREG(0x4003801C) |= 0x1000;
 
        ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH0);
 
        ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH1);
 
        ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH0);
 
        ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH1|ADC_CTL_IE);
 
        ADCSequenceEnable(ADC0_BASE, 2);
        ADCIntEnable(ADC0_BASE, 2);
        IntEnable(INT_ADC0SS2);
        IntPrioritySet(INT_ADC0SS2, 0x00);
 
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
 
        uDMAEnable();
        uDMAControlBaseSet(ui8ControlTable);
        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC2, UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |(UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK));
        uDMAChannelControlSet(UDMA_PRI_SELECT|UDMA_CHANNEL_ADC2, UDMA_SIZE_32|UDMA_SRC_INC_NONE|UDMA_DST_INC_32|UDMA_ARB_4);
        uDMAChannelTransferSet(UDMA_PRI_SELECT|UDMA_CHANNEL_ADC2, UDMA_MODE_BASIC, (void *)(ADC0_BASE + ADC_O_SSFIFO2), DMAdata, 4);
        uDMAChannelEnable(UDMA_CHANNEL_ADC1);
while(1)
        {
 
        }
return 0;
}
 
void PWM1IntHandler(void)
{
        PWMGenIntClear(PWM1_BASE, PWM_GEN_1, PWM_INT_CNT_LOAD|PWM_TR_CNT_LOAD);
        IntPendClear(INT_PWM1_1);
}
 
void ADC0SS2IntHandler(void)
{
        ADCIntClear(ADC0_BASE, 2);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1)^0x02);
        uDMAChannelEnable(UDMA_CHANNEL_ADC2);
        IntPendClear(INT_ADC0SS2);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1)^0x02);
}

I'm using the tiva launchpad.

Thank you in advance for all the help
  • When a uDMA transfer is completed the channel is automatically disabled by the uDMA controller.  

    If you wanted the DMA engine to continuously DMA the latest ADC samples you'd have to set the ui32TransferSize (in the uDMAChannelTransferSet() call) to something larger than 4 and your buffer for samples will have to grow accordingly.

    OR you could use scatter-gather mode to configure an endless uDMA operation loop.  See section 9.2.6.5 of the data sheet for more details.

    --Miles

  • hi,

      I have a doubt in configuring the DMA for ADC. I am using TIVA tm4c1294 launch pad.I have added my code

    /*  DMA configuration

    */

    SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA);
        uDMAEnable();
        uDMAControlBaseSet(pui8ControlTable);

        uDMAChannelAssign(UDMA_CH14_ADC0_0);

        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0,UDMA_ATTR_ALL);

        uDMAChannelControlSet((UDMA_CHANNEL_ADC0|UDMA_PRI_SELECT),(UDMA_ARB_8|UDMA_SRC_INC_NONE|UDMA_DST_INC_32|UDMA_SIZE_32));

        uDMAChannelTransferSet((UDMA_CHANNEL_ADC0|UDMA_PRI_SELECT),UDMA_MODE_BASIC, (void*)(ADC0_BASE+ADC0_SSFIFO0),&value,8);


        uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0,UDMA_ATTR_USEBURST);

         uDMAChannelEnable(UDMA_CHANNEL_ADC0);

    /*  ADC configuration

    */

        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
        GPIOPinTypeADC(GPIO_PORTE_BASE,GPIO_PIN_0);
        ADCClockConfigSet(ADC0_BASE,ADC_CLOCK_SRC_PLL,ADC_CLOCK_RATE_FULL);
        ADCSequenceConfigure(ADC0_BASE,0,ADC_TRIGGER_TIMER,0);
        ADCSequenceStepConfigure(ADC0_BASE,0,0,ADC_CTL_CH3|ADC_CTL_END|ADC_CTL_IE);
        ADCSequenceDMAEnable(ADC0_BASE,0);
        ADCSequenceEnable(ADC0_BASE,0);
        ADCIntEnableEx(ADC0_BASE,ADC_INT_DMA_SS0);
        IntEnable(INT_ADC0SS0);

    The problem is I am getting the ADC_DMA interrupt but the data are not being transfered.

  • Hello Gunasekran,

    While the code post is useful, there could be other issues:

    1. Is the startup_ccs.c containing the Interrupt Handler Mapped?

    2. When the ADC_DMA interrupt is received how is it being checked: (a) Interrupt Handler or (b) some polling in the code?

    3. What is the expected ADC data and what is the actual ADC data?

    Regards

    Amit

  • Sorry to resurrect this thread, but I have solved this issue basing it off the udma_adc_pin_sg.c found in another thread.

    #include <stdbool.h>
    #include <stdint.h>
    
    #include "inc/hw_adc.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_gpio.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_udma.h"
    
    #include "driverlib/adc.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/pwm.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/udma.h"
    
    #include "utils/uartstdio.h"
    
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    static volatile uint32_t g_ui32ADC0Reading = 9999;
    
    //*****************************************************************************
    //
    // Counters used to count how many times certain ISR event occur.
    //
    //*****************************************************************************
    static uint32_t g_ui32uDMAErrCount = 0;
    
    //*****************************************************************************
    //
    // The control table used by the uDMA controller.  This table must be aligned
    // to a 1024 byte boundary.
    //
    //*****************************************************************************
    #if defined(ewarm)
    #pragma data_alignment=1024
    tDMAControlTable sControlTable[1024];
    #elif defined(ccs)
    #pragma DATA_ALIGN(sControlTable, 1024)
    tDMAControlTable sControlTable[1024];
    #else
    tDMAControlTable sControlTable[1024] __attribute__ ((aligned(1024)));
    #endif
    
    //*****************************************************************************
    //
    // Declaration of preload structure, referred to by the task list.
    // It will be defined later.
    //
    //*****************************************************************************
    tDMAControlTable g_sADC0TaskListPreload;
    
    //*****************************************************************************
    //
    // This is the task list that defines the DMA scatter-gather operation.
    // Each "task" in the task list gets copied one at a time into the alternate
    // control structure for the channel, where it is then executed.  Each time
    // the DMA channel is triggered, it executes these tasks.
    //
    //*****************************************************************************
    tDMAControlTable g_ADC0TaskTable[] =
    {
        //
        // Task 1: transfer ADC data
        // Copy available data from ADC FIFO to buffer
        //
        {
          (void*) (ADC0_BASE + ADC_O_SSFIFO0),              // src addr is ADC0SS0 FIFO
          (void*) &g_ui32ADC0Reading,                       // dst addr ADC read var
          UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_NONE |  // src is a 32 bit word and there is no increment
          UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_NONE |  // dst is a 32 bit word and there is no increment
          UDMA_CHCTL_ARBSIZE_1 |                            // arb size is 1
          ((1 - 1) << UDMA_CHCTL_XFERSIZE_S) |              // transfer size is 1
          UDMA_CHCTL_XFERMODE_MEM_SGA                       // memory scatter-gather
        },
    
        //
        // Task 2: reset task list
        // Reprogram this DMA channel by reloading the primary structure with
        // the pointers needed to copy the task list for SG mode.
        // Only the control word actually needs to be reloaded.
        // This will allow another DMA transfer to start on this channel the
        // next time a peripheral request occurs.
        //
        {
          &(g_sADC0TaskListPreload.ui32Control),           // src addr is the task reload control word
          &(sControlTable[UDMA_CHANNEL_ADC0].ui32Control), // dst addr is the ADC0 primary control word in the master uDMA control table
          UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_NONE | // src is a 32 bit word and there is no increment
          UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_NONE | // dst is a 32 bit word and there is no increment
          UDMA_CHCTL_ARBSIZE_1 |                           // arb size is 1
          ((1 - 1) << UDMA_CHCTL_XFERSIZE_S) |             // transfer size is 1
          UDMA_CHCTL_XFERMODE_PER_SGA                      // peripheral scatter-gather
        }
    };
    
    //*****************************************************************************
    //
    // This is the preloaded channel control structure for the ADC channel.  The
    // values in this structure are configured to perform a memory scatter-gather
    // operation whenever a request is received.  This structure is copied over
    // into the DMA control table by software at the beginning of this app.  After
    // that, it is reloaded into the control table by a scatter-gather operation.
    //
    // Transfer is 2 uDMA tasks, comprising 4 32-bit words each, for a total of 8 transfers
    // Source of the scatter gather transfer is the start of the task list.
    // Note that it must point to the last location to copy.
    // Destination is the alternate structure for the ADC channel
    // Setup transfer parameters for peripheral scatter-gather,
    // The arb size is set to 8 to allow the full transfer to happen in one
    // transaction.
    //
    //****************************************************************************
    tDMAControlTable g_sADC0TaskListPreload =
    {
        &(g_ADC0TaskTable[1].ui32Spare),                                  // src addr is the end of the ADC0 read task list
        &(sControlTable[UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT].ui32Spare),  // dst addr is the end of the ADC0 channel in the master uDMA control table
        UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_32 |                    // src is 32 bit words
        UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_32 |                    // dest is 32 bit words
        UDMA_CHCTL_ARBSIZE_4 |                                            // arb size is 4
        ((8 - 1) << UDMA_CHCTL_XFERSIZE_S) |                              // transfer size is 8
        UDMA_CHCTL_XFERMODE_MEM_SG                                        // memory scatter-gather
    };
    
    void
    ConfigADC0(void)
    {
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
      GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
    
      SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
      SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
      ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
      ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);
      ADCSequenceEnable(ADC0_BASE, 0);
    }
    
    //*****************************************************************************
    //
    // This function loads the channel control structure with the starting values,
    // and then enables the DMA channel.
    //
    //*****************************************************************************
    void
    ConfigADC0SGTaskList(void)
    {
        //
        // Copy the value of the preload task list into the primary structure
        // to initialize it for the first time.  After the DMA starts it will be
        // re-initialized each time by the third s-g task.
        //
        sControlTable[UDMA_CHANNEL_ADC0].pvDstEndAddr = g_sADC0TaskListPreload.pvDstEndAddr;
        sControlTable[UDMA_CHANNEL_ADC0].pvSrcEndAddr = g_sADC0TaskListPreload.pvSrcEndAddr;
        sControlTable[UDMA_CHANNEL_ADC0].ui32Control = g_sADC0TaskListPreload.ui32Control;
    
        //
        // Enable the ADC DMA channel.  This will allow it to start running when
        // it receives a request from the peripheral
        //
        uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    }
    
    //*****************************************************************************
    //
    // The interrupt handler for uDMA errors.  This interrupt will occur if the
    // uDMA encounters a bus error while trying to perform a transfer.  This
    // handler just increments a counter if an error occurs.
    //
    //*****************************************************************************
    void
    uDMAErrorHandler(void)
    {
        //
        // Check for uDMA error bit
        //
        uint32_t ui32Status = uDMAErrorStatusGet();
    
        //
        // If there is a uDMA error, then clear the error and increment
        // the error counter.
        //
        if(ui32Status) {
            uDMAErrorStatusClear();
            g_ui32uDMAErrCount++;
        }
    }
    
    int
    main(void)
    {
        //
        // Set the clocking to run directly from the PLL.  Run at 50 MHz to
        // provide plenty of bus cycles for the DMA operations.
        //
        SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
        UARTStdioConfig(0, 115200, SysCtlClockGet());
        UARTprintf("\ec\e[?25l");
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        uDMAEnable();
        uDMAControlBaseSet(sControlTable);
        uDMAIntRegister(INT_UDMAERR, uDMAErrorHandler);
        IntEnable(INT_UDMAERR);
    
        ConfigADC0();
        ConfigADC0SGTaskList();
    
        while (true) {
          UARTprintf("\e[H%4u\nErr: %u", g_ui32ADC0Reading, g_ui32uDMAErrCount);
        }
    
        return(0);
    }
    

  • Hi,
    Is this code working? Because I can't seem to make it work :/
    Thanks

  • What's the problem? Are any errors being thrown?

    You can find a full CCS project here:

    Or here:

    Depending upon your needs, the first one is a continuous transfer to memory at the sample rate of the ADC. The second utilizes a pin to trigger the transfer. Both transfers are by nature atomic operations.

  • Hi,

    I want to acquire 8 ADC channels at a time, with the sampling being triggered from the PWM module. The problem is that I cannot seem to make a DMA transfer with the ADC peripheral request, it only transfers with the "MEM_SGA". But what I want is to make a transfer only when the ADC has the 8 channels in the FIFO.

    In short, it only works with UDMA_CHCTL_XFERMODE_MEM_SGA and not with UDMA_CHCTL_XFERMODE_PER_SGA.

  • Line 28 becomes

    static volatile uint32_t g_pui32ADC0Readings[7] = {9999, };

    Modify any references from 

    g_ui32ADC0Reading

    to

    g_pui32ADC0Readings[/*Index*/]

    Lines 71-83 become

    //
    // Task 1: transfer ADC data
    // Copy available data from ADC FIFO to buffer
    //
    {
       (void*) (ADC0_BASE + ADC_O_SSFIFO0),              // src addr is ADC0SS0 FIFO
       (void*) &g_pui32ADC0Readings,                     // dst addr is ADC readings array
       UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_NONE |  // src is a 32 bit word and there is no increment
       UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_NONE |  // dst is a 32 bit word and there is no increment
       UDMA_CHCTL_ARBSIZE_1 |                            // arb size is 1
       ((8 - 1) << UDMA_CHCTL_XFERSIZE_S) |              // transfer size is 8 (8 samples to collect)
       UDMA_CHCTL_XFERMODE_MEM_SGA                       // memory scatter-gather
    },

    Lines 132-143 become

    void
    ConfigADC0(void)
    {
      // Enable the ADC channels
      SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
      GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
    
      //**********************************************
      /* TODO:  Add the rest of the channels *
      //**********************************************
      SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
    
      ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH3); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH4); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH5); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_CH6); 
      ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH7 | ADC_CTL_IE | ADC_CTL_END); 
    
      ADCSequenceEnable(ADC0_BASE, 0); 
    }
    

    
    

    That should work. This is untested though.

  • Hi,

    That is exactly what I did, the problem is that the DMA doesn't seem to trigger from the peripheral.