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 with ADCs



I am using TM4C1294 connected launchpad eval board.  I'd like to sample an ADC at 1Msps and DMA ping pong 1024 samples into a buffer.  I am testing this out using the Temp sensor.

Here is my code.  It keeps interrupting the ADCseq0Handler but not with

ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP or

ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP

#include <stdbool.h>
#include <stdint.h>
#include <string.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 "inc/hw_uart.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"
#include "utils/uartstdio.h"


//*****************************************************************************
//
// 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
uint8_t pui8ControlTable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(pui8ControlTable, 1024)
uint8_t pui8ControlTable[1024];
#else
uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
#endif

//DMA bus error count
uint32_t g_ui32uDMAErrCount = 0;

#define ADC_DMA_SIZE 1024
uint16_t adcbuffer_ping[ADC_DMA_SIZE];
uint16_t adcbuffer_pong[ADC_DMA_SIZE];

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void __error__(char *pcFilename, uint32_t ui32Line)
{
	while (1);
}
#endif

//*****************************************************************************
//
// Configure the UART and its pins.  This must be called before UARTprintf().
//
//*****************************************************************************
void ConfigureUART(uint32_t sysclock)
{
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);

    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, sysclock);
}

void ConfigureADC(void)
{

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    SysCtlDelay(10);

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_EIGHTH, 30);

	//ADCHardwareOversampleConfigure(ADC0_BASE,64); //here you chose how many oversamples
    ROM_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_TS );
    ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_TS | ADC_CTL_IE |ADC_CTL_END);
    ROM_ADCSequenceEnable(ADC0_BASE, 0);


    //
    // Enable the uDMA controller error interrupt.  This interrupt will occur
    // if there is a bus error during a transfer.
    //
    ROM_IntEnable(INT_UDMAERR);

    //
    // Enable the uDMA controller.
    //
    ROM_uDMAEnable();
    //
    // Point at the control table to use for channel control structures.
    //
    ROM_uDMAControlBaseSet(pui8ControlTable);

	ROM_ADCSequenceDMAEnable(ADC0_BASE, 0);

 // disable some bits
	ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC0, UDMA_ATTR_ALTSELECT /*start with ping-pong PRI side*/ |
		UDMA_ATTR_HIGH_PRIORITY /*low priority*/ | UDMA_ATTR_REQMASK /*unmask*/);
	// enable some bits
	ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC0, UDMA_ATTR_USEBURST /*only allow burst transfers*/);


    // set dma params on PRI_ and ALT_SELECT
    ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1024);
    ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1024);
    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_ping[0], 1024);
    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_pong[0], 1024);
    ROM_IntEnable(INT_ADC0SS0);

    ROM_ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    ROM_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)
{
    uint32_t ui32Status;

    //
    // Check for uDMA error bit
    //
    ui32Status = ROM_uDMAErrorStatusGet();

    //
    // If there is a uDMA error, then clear the error and increment
    // the error counter.
    //
    if(ui32Status)
    {
        ROM_uDMAErrorStatusClear();
        g_ui32uDMAErrCount++;
    }
    while(1);
}

void ADCseq0Handler()
{

    uint32_t ui32Status = ADCIntStatus(ADC0_BASE, 0, false);

	ROM_ADCIntClear(ADC0_BASE, 0);

	if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP) {
		// re-set up a DMA transfer to the buffer
	    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_ping[0], 1024);
		return;
	}

	if (ROM_uDMAChannelModeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP) {
		ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_pong[0], 1024);
		return;
	}

}

int main(void)
{
    uint32_t sysclock;

    do {
        sysclock = SysCtlClockFreqSet(SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480, 120*1000*1000);
    } while (!sysclock);


    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    ROM_SysCtlDelay(1);

    ConfigureUART(sysclock);
    ConfigureADC();




	while(1)
	{
		UARTprintf("ADCtemp: test");
		SysCtlDelay(20*1000*1000);
	}
}

  • Hello Bryce,

    Can you replace UDMA_CHANNEL_ADC0 with UDMA_CH14_ADC0_0 as that is the correct format for TivaWare. Secondly the uDMAChannelAssign API is also missing in the code. Can you please add the same after ROM_uDMAEnable API call.

    Regards

    Amit

  • Hi Amit, 

    Thanks for the help.

    I did see I was missing the uDMAChannelAssign.  I added that and changed UDMA_CHANNEL_ADC0  to UDMA_CH14_ADC0_0.  Both UDMA_CH14_ADC0_0  and UDMA_CHANNEL_ADC0 are defined as 14, so I don't think that would change anything.

     

    It seems that I'm stuck in the ADCseq0Handler over and over.  It never gets to the while loop in main.

     

    In the startup_ccs.c:  I have line with // ADC Sequence 0 set to ADCseq0Handler. Is that correct?  I don't need to interrupt off a seperate uDMA interrupt?

  • One issue I found is I want the ADCSequenceConfigure to be ADC_TRIGGER_ALWAYS instead of PROCESSOR.

    After I modified that I got one PING and one PONG.  Then it kept staying in the ADCseq0Handler without getting the UDMA_MODE_STOP on either the ping or the pong.

    Do I get a interrupt for every sequence that is completed or am I only suppose to get an interrupt when the DMA is filled up?

  • Hello Bryce,

    The interrupt should come only twice. Once when the PING is over and second time when PONG is over. Can you check what is the MIS status when it stays in the Interrupt Handler.

    Regards

    Amit

  • You mean the return of ADCIntStatus(ADC0_BASE, 0, false);?

    When I break on the next line.  It is showing a 1.  Sometimes a 0.

  • Hello Bryce

    The return value of the API with the last field as true. Using false will only read the RIS that cannot generate an interrupt. It is the MIS which generates the interrupt

    Regards

    Amit

  • I'm not sure if I understand what I'm looking for? Where can I read the MIS?

  • Hello Bryce,

    My apologies. I should have mentioned the full register name as ADCISC at offset of 0x00C of the ADC Address Space.

    Regards

    Amit

  • Hi Amit,

    I am seeing 0x00000100 in the register.  That looks like it is the ADC_INT_DMA_SS0 register.  Does that mean I'm not clearing out the interrupt?  Or am I triggering too fast?

    Thanks

    Bryce

  • Amit,

    I may have it working now, just testing it now.  

    Could it be that putting breakpoints, or pausing the debugger causes the DMA to stop interrupting after I unpause it?  When I quit doing that and added logging out the Vcom port it seems that the Ping/Pongs are going back and forth.

    Bryce

  • Hello Bryce,

    Both...

    1. There is a bug in the ADCIntClear because of which it is not clearing the Interrupt Status bit. What you can do is for the moment use the return value of the ADCIntStatus and perform a clear operation as below

    HWREG(ADC0_BASE+ADC_O_ISC) = ui32Status;

    2. Enable the ADC after the uDMA channel has been configured.

    3. Every time you get an interrupt, retrigger the ADC Conversion using ADCProcessorTrigger API call which is missing in the code for a processor trigger

    Regards

    Amit

  • Thanks Amit!  Glad I am getting data out!

    1)  Can I use this instead as well?  

    ROM_ADCIntClearEx(ADC0_BASE, ui32Status);

    Or did that have the same issue?

    2. I moved it as you suggested

    3. Do I need to do this if I switched it to ADC_TRIGGER_ALWAYS?

    Here is my code if you wanted to see it the changes.  I'm just printing the first sample of the 1024 in the buffer to make sure it is working.  Going to trigger a pin and scope it next to make sure the timing is working like I think it is.

    #include <stdbool.h>
    #include <stdint.h>
    #include <string.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 "inc/hw_uart.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "drivers/pinout.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"
    #include "utils/uartstdio.h"
    
        uint32_t count = 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
    uint8_t pui8ControlTable[1024];
    #elif defined(ccs)
    #pragma DATA_ALIGN(pui8ControlTable, 1024)
    uint8_t pui8ControlTable[1024];
    #else
    uint8_t pui8ControlTable[1024] __attribute__ ((aligned(1024)));
    #endif
    
    //DMA bus error count
    uint32_t g_ui32uDMAErrCount = 0;
    
    #define ADC_DMA_SIZE 1024
    static uint16_t adcbuffer_ping[ADC_DMA_SIZE+1];
    static uint16_t adcbuffer_pong[ADC_DMA_SIZE+1];
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void __error__(char *pcFilename, uint32_t ui32Line)
    {
    	while (1);
    }
    #endif
    
    //*****************************************************************************
    //
    // Configure the UART and its pins.  This must be called before UARTprintf().
    //
    //*****************************************************************************
    void ConfigureUART(uint32_t sysclock)
    {
        //
        // Enable the GPIO Peripheral used by the UART.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Enable UART0
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Configure GPIO Pins for UART mode.
        //
        ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
        ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, sysclock);
    }
    
    void ConfigureADC(void)
    {
    
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        SysCtlDelay(10);
    
        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 30);
    
        ROM_ADCHardwareOversampleConfigure(ADC0_BASE,5); //here you chose how many oversamples
        ROM_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_TS );
        ROM_ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);
        ROM_ADCSequenceEnable(ADC0_BASE, 0);
    
    
    
    
        //
        // Enable the uDMA controller.
        //
        ROM_uDMAEnable();
        ROM_uDMAChannelAssign(UDMA_CH14_ADC0_0);
        //
        // Point at the control table to use for channel control structures.
        //
        ROM_uDMAControlBaseSet(pui8ControlTable);
    
    	ROM_ADCSequenceDMAEnable(ADC0_BASE, 0);
    
     // disable some bits
    	ROM_uDMAChannelAttributeDisable(UDMA_CH14_ADC0_0, UDMA_ATTR_ALTSELECT /*start with ping-pong PRI side*/ |
    		UDMA_ATTR_HIGH_PRIORITY /*low priority*/ | UDMA_ATTR_REQMASK /*unmask*/);
    	// enable some bits
    	ROM_uDMAChannelAttributeEnable(UDMA_CH14_ADC0_0, UDMA_ATTR_USEBURST /*only allow burst transfers*/);
    
    
        // set dma params on PRI_ and ALT_SELECT
        ROM_uDMAChannelControlSet(UDMA_CH14_ADC0_0 | UDMA_PRI_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1024);
        ROM_uDMAChannelControlSet(UDMA_CH14_ADC0_0 | UDMA_ALT_SELECT, UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1024);
        ROM_uDMAChannelTransferSet(UDMA_CH14_ADC0_0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_ping[0], 1024);
        ROM_uDMAChannelTransferSet(UDMA_CH14_ADC0_0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_pong[0], 1024);
    
    
        ROM_uDMAChannelEnable(UDMA_CH14_ADC0_0);
    
        ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC0);
    
        ROM_IntEnable(INT_UDMAERR);
        ROM_IntEnable(INT_ADC0SS0);
    
        ROM_ADCIntEnableEx(ADC0_BASE, ADC_INT_DMA_SS0);
    
    }
    
    //*****************************************************************************
    //
    // 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)
    {
        uint32_t ui32Status;
    
        //
        // Check for uDMA error bit
        //
        ui32Status = ROM_uDMAErrorStatusGet();
    
        //
        // If there is a uDMA error, then clear the error and increment
        // the error counter.
        //
        if(ui32Status)
        {
            ROM_uDMAErrorStatusClear();
            g_ui32uDMAErrCount++;
        }
        while(1);
    }
    
    void ADCseq0Handler()
    {
    
        uint32_t ui32Status = ADCIntStatus(ADC0_BASE, 0, false);
    
        //ROM_ADCIntClear(ADC0_BASE, 0);
        //ROM_ADCIntClearEx(ADC0_BASE, ui32Status);
        HWREG(ADC0_BASE+ADC_O_ISC) = ui32Status;
    
    	if (ROM_uDMAChannelModeGet(UDMA_CH14_ADC0_0 | UDMA_PRI_SELECT) == UDMA_MODE_STOP) {
    		// re-set up a DMA transfer to the buffer
    		ROM_uDMAChannelTransferSet(UDMA_CH14_ADC0_0 | UDMA_PRI_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_ping[0], 1024);
    		UARTprintf("P:%d ",adcbuffer_ping[0]);
    		return;
    	}
    
    	if (ROM_uDMAChannelModeGet(UDMA_CH14_ADC0_0 | UDMA_ALT_SELECT) == UDMA_MODE_STOP) {
    		ROM_uDMAChannelTransferSet(UDMA_CH14_ADC0_0 | UDMA_ALT_SELECT, UDMA_MODE_PINGPONG, (void *)(ADC0_BASE + ADC_O_SSFIFO0), &adcbuffer_pong[0], 1024);
    		UARTprintf("p:%d \n",adcbuffer_pong[0]);
    		return;
    	}
    
    }
    
    int main(void)
    {
        uint32_t sysclock;
    
    	sysclock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);
    
        ROM_SysCtlDelay(5);
    
        PinoutSet(true,false);
        ConfigureUART(sysclock);
        ConfigureADC();
    
    
    
    
    	while(1)
    	{
    		count++;
    		//UARTprintf("Count: %d\n",count++);
    		//SysCtlDelay(20*1000*1000);
    	}
    }
    

  • Hello Bryce,

    1. Yes that would do

    3. No, it would not be needed if in always trigger mode

    Regards

    Amit

  • Amit,

    Thanks for you help getting this going!  It appears to work well.  Scoping it out with a blinking led, I'm on the right rate to get 1Msps.

    Bryce