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.

Continuous ADC to DMA transfer

Hello, I want to transfer sampled data from the ADC to a variable, through DMA, without CPU intervention. Thus the ADC should trigger the DMA unit whenever sampled data are available. I utilize sequencer 3 which has a FIFO depth of 1. The sampled data should be stored in the variable ADC_value. Additionally I use the INT_UDMAERR ISR to verify if an error occurs. 

According to the reference manual:

BASIC mode can be programmed to ignore when XFERSIZE reaches 0x000 and continue copying

on request until the channel is stopped manually. If the NXTUSEBURST bit in the uDMA Channel

Control Word (DMACHCTL) register is set while in BASIC mode and the XFERSIZE reaches 0x000

and is not written back, transfers continue until the request is deasserted by the peripheral.”

Thus I utilize UDMA_NEXT_USEBURST and UDMA_MODE_BASIC. What does it mean “and is not written back”? Is there something else to consider?

Below you can see my uDMA configuration:

uint32_t ADC_value;

uint8_t DMA_controlTable[1024];

 

SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA);

SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

uDMAEnable();

uDMAControlBaseSet(DMA_controlTable);

uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC3, UDMA_ATTR_ALL);

uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC3, UDMA_ATTR_USEBURST);

uDMAChannelControlSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,

UDMA_SIZE_32 |

UDMA_SRC_INC_NONE |

UDMA_NEXT_USEBURST |

UDMA_ARB_8 |

UDMA_DST_INC_NONE);

uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,

UDMA_MODE_BASIC,

(void *)(ADC0_BASE + ADC_O_SSFIFO3),

&ADC_value,

8);

uDMAChannelEnable(UDMA_CHANNEL_ADC3);

IntEnable(INT_UDMAERR);

Unfortunately, the transfer is only executed once. I check within the while loop weather the DMA has been stopped or the channel has been disabled.

while(1)

{

if((stateX = uDMAChannelModeGet(UDMA_CHANNEL_AC3) == UDMA_MODE_STOP)

{

uDMA_Channel_Configuration();

}

if((stateY = uDMAChannelIsEnabled(UDMA_CHANNEL_ADC3) ) == false)

{

stateY = 1;

}

}

After the fist execution the uDMA channel is disabled and stateY = 1. Then the fist if statement gets true. After an additional call of uDMA_Channel_Configuration the ADC_value is update with a new value.

Are additional or other steps necessary to achieve the DMA transfer without the permanent reconfiguration?

Thank you in advance for all the help!

  • Hello Markus

    The uDMA in basic mode will transfer data and then STOP. What you need is to use PING-PONG mechanism where there is always a second buffer available for transfer. Also what is the trigger mode to the ADC?

    Regards
    Amit
  • Hello Amit

    Thank you for the prompt reply!

    The ADC is triggered through: ADC_TRIGGER_ALWAYS.

    ADC configuration:

    SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
    GPIOPinTypeADC(GPIO_PORTK_BASE, GPIO_PIN_3);

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 15);

    ADCSequenceDisable(ADC0_BASE, 3);

    ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_ALWAYS, 0);

    ADCSequenceStepConfigure(ADC0_BASE, 3, 0, ADC_CTL_CH19 | ADC_CTL_IE | ADC_CTL_END);

    ADCSequenceDMAEnable(ADC0_BASE, 3);

    ADCSequenceEnable(ADC0_BASE, 3);

    IntEnable(INT_ADC0SS3);
    ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS3);

    I changed the DMA configuration to the following:

    uDMAChannelControlSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
    UDMA_SIZE_32 |
    UDMA_SRC_INC_NONE |
    UDMA_NEXT_USEBURST |
    UDMA_ARB_1024 |
    UDMA_DST_INC_32);

    uDMAChannelControlSet(UDMA_CHANNEL_ADC3 | UDMA_ALT_SELECT,
    UDMA_SIZE_32 |
    UDMA_SRC_INC_NONE |
    UDMA_NEXT_USEBURST |
    UDMA_ARB_1024 |
    UDMA_DST_INC_32);

    uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT,
    UDMA_MODE_PINGPONG,
    (void *)(ADC0_BASE + ADC_O_SSFIFO3),
    ADC_value,
    1024);

    uDMAChannelTransferSet(UDMA_CHANNEL_ADC3 | UDMA_ALT_SELECT,
    UDMA_MODE_PINGPONG,
    (void *)(ADC0_BASE + ADC_O_SSFIFO3),
    ADC_value1,
    1024);

    Unfortunately, the DMA transfer is only executed once. After the transfer the ADC_INT_DMA_SS3 interrupt occurs. To trigger a following DMA transfer it is necessary to re-initialize the channel via the function uDMAChannelTransferSet(). Additionally the DMA channel needs to be enabled again.

    I want to avoid these steps to guarantee the DMA transfer without any CPU intervention. Is there something else to consider?
  • Hello Markus,

    When the DMA Done interrupt comes the PING transfer would have completed. The Ping buffer needs to be reinitialized while the next set of DMA transfers would be now be done by the Pong buffer. I would suggest using processor trigger so that first you can test the UDMA+ADC integration before moving it to the Always Trigger Mode.

    Regards
    Amit
  • Hello Amit,

    thank you very much for your help, everything works fine! I did not know that I have to re-initialize the DMA transfer within the DMA-ISR. I thought the re-initialization is done automatically and can therefore be skipped.

    Regards
    Markus

  • Hello Markus,

    The reinit can be done by the DMA using the more complicated Peripheral Scatter Gather. We are working on an application note on UDMA which would be useful for future reference on the complicated schemes.

    Regards
    Amit
  • Amit,

    I've got a similar problem to Markus. I've been using the 123x Launchpad to do some prototyping and I've managed to successfully use a timer interrupt to trigger ADC sample sequences which are collated by uDMA into a buffer. This worked well. I've now migrated to the DK-TM4C129x dev kit and when I run the same code on the new target, with all of the compiler/target settings changed, I get continuous ADC sampling.

    I've tried a number of workarounds which all give me the same behaviour.

    I'd be grateful for any advice you can give,

    Regards,

    Dave
  • Amit,

    I managed to find a solution to the problem. It seems like the DMAIN0 and DMAIN1 bit in ADCISC for each ADC was never being cleared. I've added ADCIntClearEx to clear the relevant bits and it seems to work now.

    Do you know of any TivaWare-specific function, in the same vein as ADCIntClear, that will clear these bits?

    Thanks in advance,

    Dave
  • Hello Dave,

    ADCIntClearEx is the correct function to use for clearing the DMA Interrupt Status bits.

    Regards
    Amit
  • Thanks Amit,

    Does this apply to all DMA interrupts? I'm also using DMAs for UART transmissions and receptions.

    Thanks,

    Dave
  • Hello David,

    There is only DMA Done interrupt mapped in the Peripheral interrupt. For some peripherals it is integrated in IntClear API while some use IntClearEx.

    Regards
    Amit