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.

UDMA + ADC - some questions to increase performance

Hello Guys,

I am using the TM4C129 (www.ti.com/.../launchpads-connected-ek-tm4c1294xl.html have uDMA working with the ADC. 


I have setup the ADC on sample sequencer 0 i.e.

ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_ALWAYS, 3 /*priority*/); // SS0-SS3 priorities must always be different
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 0, ADC_CTL_TS); // ADC_CTL_TS = read temp sensor
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 1, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 2, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 3, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 4, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 5, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 6, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 7, ADC_CTL_TS | ADC_CTL_END | ADC_CTL_IE); 


I have setup the UDMA like So:

uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_256);

uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(ADC0_BASE + ADC_O_SSFIFO0),
g_ui8RxBufA, MEM_BUFFER_SIZE);

static uint32_t g_ui8RxBufA[some size];

My understanding of this is: 32 bits are transferred from the ADC, to g_ui8RxBufA.

Question 1

But I was thinking, because the ADC sequencer is 32 bits wide and 8 deep. can I do something like this?

uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_SIZE_256 | UDMA_SRC_INC_NONE | UDMA_DST_INC_256 | UDMA_ARB_256);

i.e. 32*8=256 bits are transferred at once?

Question 2
Forget about Question 1 for a second.
Because I know that only 12 bits of ADC are actually used,
can I do something like this?

uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_256);

static uint16_t g_ui8RxBufA[some size];

This would save lots of RAM


Thanks Guys

Dan

  • I like your curious nature - but "not so much" your "lack of simple experimentation" - in the attempt to "tease out" your answer. (or - at minimum - expand your knowledge & investigative techniques...)   The desire to "save" (by not wasting) RAM is excellent.

    I can report that my tech firm - not under µDMA - saves ADC values into 16 bit memory "chunks" - logically packing the 2nd ADC value (16 bits) w/in 32 bit RAM - w/out distressing the (first) 16 bit ADC value.

    Not always will arrive an, "Amit-like figure" able, willing & always (riding) to your rescue (well describes/defines Amit) - thus the suggestion to "experiment first" - call for help, second...

  • Hello Dan,

    The second approach will save RAM for sure. But i do not understand How you arrived at the equation

    "i.e. 32*8=256 bits are transferred at once?" when the Arbitration Size is set to 256 "UDMA_ARB_256"?

    Regards
    Amit
  • Amit Ashara said:
    But i do not understand How you arrived at the equation "i.e. 32*8=256 bits are transferred at once?"

    Nor did we - thus the suggestion, "Experiment prior to "Calling for help.""  Good as you are - you cannot (always) rescue (everyone!)  

    Independent experimentation & investigation builds character & resourcefulness - "calling for help" (minus that effort) - not so much...

  • Hello Amit,

    My understanding is that if the ADC sample sequencer 0 is used. It has a FIFO of 8.
    So my thinking is something like this:

    ADC returns 8 samples
    DMA interrupts
    ADC returns 8 samples
    DMA interrupts
    etc

    so every DMA interrupt, DMA would read 8 samples, which is 32 bits wide each, which is 256 bits.

    Thanks
    Daniel
  • Hello Daniel

    This interpretation is wrong. The DMA uses element size. It does not matter if it is 8, 16 or 32-bit. Each transfer is treated as an element. So if the FIFO depth is 8, the number of transfers is 8 (elements = 8) and hence Transfer Size and ARBSIZE must be 8.

    Regards
    Amit
  • Hello Amit,

    So are you saying something like this?

    uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
    UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
    
    UDMA_ARB_8);

    you mentioned transfer size, I am not sure where I change that, do you mean here?

    MEM_BUFFER_SIZE = 8;
    
    uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
    UDMA_MODE_PINGPONG,
    
    (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
    g_ui8RxBufA, MEM_BUFFER_SIZE);

    MEM_BUFFER_SIZE  was orignally 256, in the code above. 

    Thanks

    Daniel

  • Hello Daniel,

    The buffer size can be upto 1024 elements but since ADC will send data in chunks of 8, the ARBSIZE must be 8

    Regards
    Amit
  • Hello Amit,

    Thanks for your help.But the DMA is not working as expected. As you can see from the video https://www.youtube.com/watch?v=GpIlqW_1ka0    . In the DMA interrupt, the ping and pong are both asserted. I thought either the ping, or pong would be asserted alternatively. The code attached is stripped down. I am using the same buffer for both ping and pong, as this is what I need for my application.

    Here is my project code:

    DMA-notworkingzip.zip

    or, here:

    #include <stdbool.h>
    
    
    #include <stdint.h>
    
    
    
    #include "inc/hw_ints.h"
    
    
    #include "inc/hw_memmap.h"
    
    
    #include "inc/hw_adc.h"
    
    
    #include "inc/hw_types.h"
    
    
    #include "inc/hw_udma.h"
    
    
    #include "inc/hw_emac.h"
    
    
    #include "driverlib/debug.h"
    
    
    #include "driverlib/gpio.h"
    
    
    #include "driverlib/interrupt.h"
    
    
    #include "driverlib/pin_map.h"
    
    
    #include "driverlib/rom.h"
    
    
    #include "driverlib/sysctl.h"
    
    
    #include "driverlib/uart.h"
    
    
    #include "driverlib/adc.h"
    
    
    #include "driverlib/udma.h"
    
    
    #include "driverlib/emac.h"
    
    
    
    uint32_t gui32ADC0Value[2];
    
    uint32_t gui32ADC1Value[2];
    
    volatile uint32_t gui32ADCIntDone[2];
    
    
    #pragma DATA_ALIGN(pui8ControlTable, 1024)
    
    uint8_t pui8ControlTable[1024];
    
    
    
    #define MEM_BUFFER_SIZE         256
    
    static uint32_t g_ui8RxBufA[MEM_BUFFER_SIZE*5];
    
    static uint32_t g_ui8RxBufB[MEM_BUFFER_SIZE*5];
    
    
    
    //*****************************************************************************
    
    //
    
    //! \addtogroup adc_examples_list
    
    //! <h1>Single Ended ADC (single_ended)</h1>
    
    //!
    
    //
    
    //*****************************************************************************
    
    void
    
    uDMAErrorHandler(void)
    
    {
    
        uint32_t ui32Status;
    
    
        //
    
        // Check for uDMA error bit
    
        //
    
        ui32Status = uDMAErrorStatusGet();
    
    
        //
    
        // If there is a uDMA error, then clear the error and increment
    
        // the error counter.
    
        //
    
        if(ui32Status)
    
        {
    
            uDMAErrorStatusClear();
    
    
        }
    
    }
    
    
    
    
    volatile int counter= 0;
    
    void ADCseq0Handler()
    
    {
        uint32_t ui32Status;
        uint32_t ui32Mode;
    
        ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    
    
        ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT);
    
    
        if(ui32Mode == UDMA_MODE_STOP)
    
        {
    
    
            uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    
                                       UDMA_MODE_PINGPONG,
    
                                       (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
                                       g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
    
    
        }
    
    
    
        ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT);
    
    
        if(ui32Mode == UDMA_MODE_STOP)
    
        {
    
            uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
                                        UDMA_MODE_PINGPONG,
    
                                        (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
    									g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
        }
    
        uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    
    
    }
    
    
    
    
    void
    
    InitUART1Transfer(uint32_t sysclock)
    
    {
    
        uint32_t div;
    
    
    
    
    
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
        SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_ADC0);
    
    
    
    
        ADCClockConfigSet(ADC0_BASE,         ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
       // ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 30);
    
    
    
    
    
        ADCSequenceConfigure(ADC0_BASE, 0 /*SS0*/, ADC_TRIGGER_ALWAYS, 3 /*priority*/);  // SS0-SS3 priorities must always be different
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 0, ADC_CTL_TS);  // ADC_CTL_TS = read temp sensor
    
    
        ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 1, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 2, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 3, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 4, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 5, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 6, ADC_CTL_TS);
    
    
         ADCSequenceStepConfigure(ADC0_BASE, 0 /*SS0*/, 7, ADC_CTL_TS | ADC_CTL_END | ADC_CTL_IE);   // ADC_CTL_IE fires every 8 samples
    
    
    
         ADCSequenceEnable(ADC0_BASE, 0);
    
    
        ADCSequenceDMAEnable(ADC0_BASE, 0);
    
    
    
    
    
    
        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0,
    
                                        UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
    
                                        UDMA_ATTR_HIGH_PRIORITY |
    
                                        UDMA_ATTR_REQMASK);
    
    
    
    
    
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    
                                UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
    
                                  UDMA_ARB_8);
    
    
    
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
                                UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
    
    							UDMA_ARB_8);
    
    
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    
                                   UDMA_MODE_PINGPONG,
    
                                   (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
                                   g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
                                    UDMA_MODE_PINGPONG,
    
                                    (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
    								g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
    
    
    
    
    
    
    
        uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    
    
        ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    
        IntEnable(INT_ADC0SS0);
    
    
    
    
    
    
    }
    
    
    
    int
    
    main(void)
    
    {
    
    
    
            uint32_t sysclock;
    
    
    
            sysclock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
    
                                                  SYSCTL_OSC_MAIN |
    
                                                  SYSCTL_USE_PLL |
    
                                                  SYSCTL_CFG_VCO_480), 120000000);
    
    
         SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    
         SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
    
         IntMasterEnable();
    
          IntEnable(INT_UDMAERR);
    
          uDMAEnable();
    
    
    
          uDMAControlBaseSet(pui8ControlTable);
    
    
    
          InitUART1Transfer(sysclock);
    
    
    
    
    
        while(1)
    
        {
    
    
        }
    
    }
    

  • Hello Daniel,

    Add the function after configuration of the UDMA, since you want to use the ADC Ping Pong.

    uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0,
    UDMA_ATTR_ALTSELECT |
    UDMA_ATTR_HIGH_PRIORITY);

    Regards
    Amit
  • Hello Amit, 

    you are a legend mate, thanks so much for your help so far. Do you mean something like this?

        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0,
    
                                        UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
    
                                        UDMA_ATTR_HIGH_PRIORITY |
    
                                        UDMA_ATTR_REQMASK);
    
    
    
    
    
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    
                                UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
    
                                  UDMA_ARB_256);
    
    
    
        uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
                                UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 |
    
    							UDMA_ARB_256);
    
    
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    
                                   UDMA_MODE_PINGPONG,
    
                                   (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
                                   g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
    
                                    UDMA_MODE_PINGPONG,
    
                                    (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    
    								g_ui8RxBufA, MEM_BUFFER_SIZE);
    
    
        uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0,
        UDMA_ATTR_ALTSELECT |
        UDMA_ATTR_HIGH_PRIORITY);
    
        uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    
    
        ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    

    the uDMAChannelAttributeEnable only seems to be enabling the alternate control structure, how do I enable the primary control structure?

    Also, my application needs to stop sampling the ADC (i.e. disable the DMA). Is the best way of doing this using

    ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0); and

    ADCIntDisableEx(ADC0_BASE, ADC_INT_DMA_SS0);

    thanks

    Daniel

  • Hello Daniel,

    The code is correct except for UDMA_ARB_256 which must be UDMA_ARB_8 or the ARB SIZE as per the number of samples to be read out from the FIFO in power of 2.

    Regards
    Amit