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 uDMA interrupt not being cleared

Hello, I'm using uDMA to transfer samples from an ADC to memory in pong pong mode.
I want the ADC to generate an interrupt when the uDMA completes a transfer, i.e. interrupt after 10000 samples.
However, I can't seem to clear the interrupt status ADC_INT_DMA_SS0.
Is it something in the ADC/uDMA settings that I did wrong which disallows me to clear that interrupt?

/* * myadcpingpong.c */ #include <stdint.h> #include <stdbool.h> #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_ints.h" #include "inc/hw_adc.h" #include "driverlib/pin_map.h" #include "driverlib/sysctl.h" #include "driverlib/gpio.h" #include "driverlib/timer.h" #include "driverlib/interrupt.h" #include "driverlib/uart.h" #include "driverlib/adc.h" #include "driverlib/udma.h" #include "utils/uartstdio.h" #define ADC_BUF_SIZE 1024 #define MODE_IDLE 0 #define MODE_UDMA 1 #define MODE_PRINT 2 volatile uint8_t mode; uint32_t ui32Ms; uint32_t ui32ClockFreq; uint8_t pui8DMAControlTable[1024]; uint16_t pui16AdcPriBuffer[ADC_BUF_SIZE]; uint16_t pui16AdcAltBuffer[ADC_BUF_SIZE]; uint16_t counter = 0; void print_ADC_buffer(void) { int i; for (i = 0; i < ADC_BUF_SIZE; i++) { UARTprintf("%d\n", pui16AdcPriBuffer[i]); } for (i = 0; i < ADC_BUF_SIZE; i++) { UARTprintf("%d\n", pui16AdcAltBuffer[i]); } } void delay_ms(uint32_t ms) { uint32_t ui32End; // Time when delay loop finishes ui32End = ui32Ms + ms; // Delay until ui32Ms is >= ui32End while (ui32End > ui32Ms); } void timer_int_handler(void) { TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); ui32Ms++; } void adc_int_handler(void) { uint32_t ui32uDMAChannelMode; uint32_t ui32ADCIntStatus; uint32_t ui32ADCIntStatus_true; // Clear the interrupt flag of ADC0 sequence 3 //HWREG(ADC0_BASE + 0x008) = 0; ui32ADCIntStatus = ADCIntStatus(ADC0_BASE, 0, false); ui32ADCIntStatus_true = ADCIntStatus(ADC0_BASE, 0, true); //ADCIntClear(ADC0_BASE, 3); //ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0); //ADCIntClearEx(ADC0_BASE, 0xFFFFFFFF); *(uint32_t *) (ADC0_BASE + ADC_O_ISC) = ADC_INT_DMA_SS0; //while(1); //HWREG(ADC0_BASE + 0x008) = (1 << 11); UARTprintf("S\n"); // Check the mode of the uDMA control structures ui32uDMAChannelMode = uDMAChannelModeGet(UDMA_CHANNEL_ADC3 | UDMA_PRI_SELECT); if ((ui32uDMAChannelMode == UDMA_MODE_STOP) && (mode == MODE_UDMA)) { // Check alt control structure ui32uDMAChannelMode = uDMAChannelModeGet(UDMA_CHANNEL_ADC3 | UDMA_ALT_SELECT); if ((ui32uDMAChannelMode == UDMA_MODE_STOP) && (mode == MODE_UDMA)) { UARTprintf("Everything done\n"); mode = MODE_PRINT; } } } void init_clock(void) { ui32ClockFreq = SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, 16000000); } void init_uart(void) { // Enable UART0 clock SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0)); // Enable GPIOA clock SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA)); // Enable UART UARTEnable(UART0_BASE); // Configure port A pins 0 and 1 to be UART0 RX and TX GPIOPinConfigure(GPIO_PA1_U0TX); GPIOPinConfigure(GPIO_PA0_U0RX); GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_1 | GPIO_PIN_0); // Configure parameters of the UART UARTStdioConfig(0, 9600, ui32ClockFreq); } void init_timer(void) { // Enable timer peripheral clock and check that the peripheral is ready SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0)); // Configure the timer TimerConfigure(TIMER0_BASE, TIMER_CFG_A_PERIODIC_UP); TimerLoadSet(TIMER0_BASE, TIMER_A, (ui32ClockFreq / 1000)); // Enable timer interrupt TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); IntEnable(INT_TIMER0A); // Enable timer to trigger ADC TimerControlTrigger(TIMER0_BASE, TIMER_A, true); // Enable timer TimerEnable(TIMER0_BASE, TIMER_A); } void init_adc(void) { // Enable GPIO peripheral (ADC input) clock and check that it's ready SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)); // Set PE5 to be ADC input GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_5); // Enable ADC peripheral clock and check that the peripheral is ready SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); SysCtlPeripheralReset(SYSCTL_PERIPH_ADC0); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)); // Disable before configuring ADCSequenceDisable(ADC0_BASE, 0); /* * ADC0 * Sequencer 0, which has FIFO depth of 8 * Triggered by processor * Highest priority */ ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); /* * ADC0 * Sequencer 0 * Step 0 * Config: Channel 9 (PE4), Last step in sequence, Triggers interrupt */ ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH9 | ADC_CTL_END /*| ADC_CTL_IE*/); // Set reference to be internal 3V ADCReferenceSet(ADC0_BASE, ADC_REF_INT); // Register and enable ADC interrupt //ADCIntEnable(ADC0_BASE, 3); // Set to use uDMA interrupt // The uDMA will send a dma_done mesage to the adc, which will trigger interrupt ADCIntEnableEx(ADC0_BASE, 0);//ADC_INT_DMA_SS3); ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0); //ADCIntDisableEx(ADC0_BASE, ADC_INT_SS3 | ADC_INT_SS2 | ADC_INT_SS1 | ADC_INT_SS0); IntEnable(INT_ADC0SS0); // Enable DMA //ADCSequenceDMAEnable(ADC0_BASE, 3); // Enable ADC ADCSequenceEnable(ADC0_BASE, 0); } void init_uDMA(void) { // Enable uDMA peripheral clock and check that it's ready SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)); uDMAEnable(); // Enable uDMA uDMAControlBaseSet(&pui8DMAControlTable[0]); // Set the base for channel control table uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALL); // Disable all attributes of the ADC uDMA channel (not needed) /* * Set the ADC0 sequencer 3 primary control structure * Size of each transfer will be 16 bits long * Source address does not increment because sequencer 3 has a FIFO of size 1 (uint32_t) * Destination address increments by 16 bits because our word sizes are 16 bits (althogh we only use 12) * Arbitration size is 1 because we are only sending 1 item each time (again, since the FIFO is size 1) */ uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); /* Set the ADC0 sequencer 3 alternate control structure */ /* Setting should be identical to that of the primary structure */ uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1); uDMADisable(); } void start_uDMA(void) { UARTprintf("Beginning uDMA...\n"); // Disable the channel for configuration uDMAChannelDisable(UDMA_CHANNEL_ADC0); /* * Set the ADC0 sequencer 3 primary channel transfer parameters * Ping Pong mode * Source address is the address of the FIFO of sequencer 3 of ADC0 * Destination address is the buffer used to store the samples * The number of items transferred in total is given in ADC_BUF_SIZE */ uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), pui16AdcPriBuffer, ADC_BUF_SIZE); /* Do the same for the alternate structure */ uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), pui16AdcAltBuffer, ADC_BUF_SIZE); // Enable the channel uDMAChannelEnable(UDMA_CHANNEL_ADC0); // Enable the adc for this ADCSequenceDMAEnable(ADC0_BASE, 0); uDMAEnable(); } void init_hardware(void) { // Configure on-board led SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION); // Wait for the peripheral to be ready, for debugging while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION)); // Set LED pin as output GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0); init_clock(); init_uart(); init_timer(); init_adc(); init_uDMA(); } int main(void) { init_hardware(); mode = MODE_IDLE; while (1) { // Idle mode if (mode == MODE_IDLE) { // Wait for char input to trigger uDMA while (!UARTCharsAvail(UART0_BASE)); UARTCharGetNonBlocking(UART0_BASE); start_uDMA(); mode = MODE_UDMA; } // Printing mode if (mode == MODE_PRINT) { // Print the contents of the ADC buffer print_ADC_buffer(); mode = MODE_IDLE; } } }

  • Hello user4633873,

    Which TM4C device are you using?

    Also in the DMA the maximum transfer size is 1024. How do you plan to get an interrupt at 10000, when it will generate at 1024?

    Regards
    Amit
  • Hi Amit,

    Pardon - are you stating that (only) that "maximum µDMA transfer size" (1024B) will generate an interrupt?

    If that's the case - is not the utility of the µDMA greatly reduced? Thank you.
  • Hello cb1,

    The user has programmed 1024 elements but has mentioned that 10000 element transfer is required. I want to highlight that with a single program of the DMA control structure it is not possible.

    Also a re-look of the code shows the manner in which ADC DMA Done Interrupt is being handler is not clear...

    // Clear the interrupt flag of ADC0 sequence 3
    //HWREG(ADC0_BASE + 0x008) = 0;
    ui32ADCIntStatus = ADCIntStatus(ADC0_BASE, 0, false);
    ui32ADCIntStatus_true = ADCIntStatus(ADC0_BASE, 0, true);
    //ADCIntClear(ADC0_BASE, 3);
    //ADCIntClearEx(ADC0_BASE, ADC_INT_DMA_SS0);
    //ADCIntClearEx(ADC0_BASE, 0xFFFFFFFF);
    *(uint32_t *) (ADC0_BASE + ADC_O_ISC) = ADC_INT_DMA_SS0;

    And another suspicion is in the sequence where IE bit is not being set for trigger generation which goes to DMA as well

    ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH9 | ADC_CTL_END /*| ADC_CTL_IE*/);


    Regards
    Amit
  • Hi Amit,

    Thank you. Still - for others & myself - is it possible to generate an interrupt in smaller sizes - as dictated by transfer size?   (I imagine (or hope) that's the case - but such has not (yet) been made clear.  (at least not clear to me...))

  • Hello cb1

    Yes. The interrupt for a control structure is generated when the Transfer size becomes 0.

    Regards
    Amit
  • Hi Amit and cb1,

    The 10000 was just an example.
    I'm using the TM4c1294XL board.

    It may be better to describe the task I'm trying to do instead of posting incomplete code, sorry about that.

    I want to continuously receive adc samples using uDMA ping pong, and I want to received an interrupt every time the transfer is complete.
    So for instance, in:

    uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
    UDMA_MODE_PINGPONG,
    (void *)(ADC0_BASE + ADC_O_SSFIFO0),
    pui16AdcPriBuffer,
    ADC_BUF_SIZE);

    I had ADC_BUF_SIZE to be 1024, so I would like an interrupt after 1024 samples.

    Previously, (not shown in code) I've had to generate an interrupt for every single ADC sample and then inside the interrupt, check whether the uDMA has finished by seeing whether the mode has switched from UDMA_MODE_PINGPONG to UDMA_MODE_STOP.
    But I would like the interrupt to be generated only once per ADC_BUF_SIZE samples.

    I would like to know if this is possible.
    Thanks
  • Hello user463873,

    Thanks for clarifying the TM4C129x device that you are using. Some further items to consider

    1. The Timer being used to initialize is being started before the ADC
    2. The UDMA channel is being configured after the ADC is configured is enabled

    This is not the sequence I would expect. First uDMA is configured, then ADC and finally Timer. If you still continue with the sequence, I can say with a sense of certainty, it is going to be a tough debug road ahead.

    Also, one final configuration step to perform...

    TimerADCEventSet(TIMER0_BASE, TIMER_ADC_TIMEOUT_A);

    Regards
    Amit
  • Hi Amit, I've made the changes you suggested, I've also uncommented the "ADC_CTL_IE" in the ADC initialization code.

    However, I am still unable to clear the ADC_RIS_DMAINR0 bit.

    I am attempting to clear the bit using

    *(uint32_t *) (ADC0_BASE + ADC_O_ISC) = ADC_INT_DMA_SS0;

    But to no avail

  • Hello user4633873,

    Can you please zip and attach your ccs project to the forum post.

    Regards
    Amit
  • myadcpingpong.rar

    Hi Amit,

    Thanks for the prompt reply, attached is the project.

  • Hello user4633873,

    The issue in the code is that when the ADC DMA Done interrupt is asserted, the timer triggers are not disabled. As a result the Timer keeps on triggering the ADC and the ADC keeps on converting the data, which generates DMA request. Since the DMA does not have a new buffer available, it will give a DONE to the ADC causing continuous interrupts.

    I add the following to the start of the ADC Interrupt Handler and it now works

    TimerDisable(TIMER0_BASE, TIMER_A);

    Summary: You need to better structure the code, if the Timer has to never been stopped then ensure that either of Ping or Pong buffer is always available.

    Regards
    Amit
  • Hi Amit,

    Thank you for your help so far, but now I have trouble maintaining the availability of the ping pong buffers.

    Currently, I have a uDMA interrupt for calling uDMAChannelTransferSet in order to reset the primary and alternate control structures when they are done.

    In the interrupt, I am testing whether the channel has finished by calling uDMAChannelModeGet and testing whether it is equal to UDMA_MODE_STOP. If the mode is UDMA_MODE_STOP, then I would call uDMAChannelModeGet for that particular channel/control structure.

    However, during debug, I've noticed that the mode of both the primary and alternate control structures are UDMA_MODE_STOP for the first interrupt, whereas I was expecting the interrupt to occur when either the ping or pong buffers have been filled.

    Furthermore, for subsequent interrupts, uDMAChannelModeGet have been returning UDMA_MODE_PINGPONG, which shouldn't be the case considering the interrupt should only occur when a uDMA transfer finishes.

    I've attached my current code and would appreciate further help, thanks!

    mysampler.rar