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.

TM4C123GXL: Difficulty triggering uDMA with ADC interrupts

Other Parts Discussed in Thread: EK-TM4C123GXL

I wish to configure one of the ADCs to read several values, then trigger the uDMA to store those values in memory.

After reading the datasheet on triggering uDMA transfers, it seemed that all I needed to do was to enable the ADC1 uDMA channel with some basic configurations to get the uDMA to trigger from the interrupt flags generated by an ADC sequence.

I have had success with getting software initiated requests to work, as well as UART requests triggered by an empty TxBuffer.

If I set up the uDMA and ADC as shown below, transfers occur as desired when initiated by a software request, but do not automatically get triggered by the ADC.

I should also mention that the ADC seems to be working as desired: when 

ADCSequenceDataGet(ADC1_BASE, 0, adc1value);

is uncommented, the function

Print_ADC1_indefinitely(void)

prints the expected values.

Here is the most relevant code:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//*****************************************************************************
//
// Initialize ADC1
//
//*****************************************************************************

void
Initialize_ADC1(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ADCHardwareOversampleConfigure(ADC1_BASE, 4);
GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_4);
ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
//ADCIntEnable(ADC1_BASE, 0);


ADCSequenceStepConfigure(ADC1_BASE, 0, 0, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 1, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 2, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 3, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 4, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 5, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 6, ADC_CTL_CH10);
ADCSequenceStepConfigure(ADC1_BASE, 0, 7, ADC_CTL_CH10 | ADC_CTL_IE | ADC_CTL_END);

ADCSequenceEnable(ADC1_BASE, 0);

ADCIntClear(ADC1_BASE, 0);
}

//*****************************************************************************
//
// Initialize ADC1 for DMA operation
//
//*****************************************************************************
void
Initialize_ADC1_DMA(void)
{
int i;
for (i=0;i<8;i++){
DMA_SrcBuf[i] = i+50;
}

//
// Configure ADC1 channel
//
ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_32 |
UDMA_ARB_8);


//
// Pull values from a visible source buffer
//
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, (void *) DMA_SrcBuf,
(void *) adc1value,
8);

//
// TODO: Pull values from SSFIFO0 instead
//
/*
ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, (void *)(ADC1_BASE + ADC_O_SSFIFO0),
(void *) adc1value,
8);
*/

//
// Enable ADC1 channel, and perform 1 request just to prove the channel can transfer correctly
//
ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC1);
ROM_uDMAChannelRequest(UDMA_CHANNEL_ADC1);
}

//*****************************************************************************
//
// Triggers ADC1, waits til it's ready, prints it. repeat.
//
//*****************************************************************************
void
Print_ADC1_indefinitely(void)
{
while(1) {
ADCProcessorTrigger(ADC1_BASE, 0);
while(!ADCIntStatus(ADC1_BASE, 0, false)) { }
//ADCSequenceDataGet(ADC1_BASE, 0, adc1value);
ADCIntClear(ADC1_BASE, 0);
UARTprintf("%4d\n", adc1value[0]);
UARTprintf("%4d\n", adc1value[1]);
UARTprintf("%4d\n", adc1value[2]);
UARTprintf("%4d\n", adc1value[3]);
UARTprintf("%4d\n", adc1value[4]);
UARTprintf("%4d\n", adc1value[5]);
UARTprintf("%4d\n", adc1value[6]);
UARTprintf("%4d\n", adc1value[7]);
}
}

  • Here is the entirety of my code, in case it is useful:

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //*****************************************************************************
    //
    // udma_demo.c - uDMA example.
    //
    // Copyright (c) 2012-2013 Texas Instruments Incorporated. All rights reserved.
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    // This is part of revision 2.0.1.11577 of the EK-TM4C123GXL Firmware Package.
    //
    //*****************************************************************************

    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "inc/hw_adc.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "utils/cpu_usage.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "driverlib/adc.h"
    #include "driverlib/rom_map.h"


    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>uDMA (udma_demo)</h1>
    //!
    //! This example application demonstrates the use of the uDMA controller to
    //! transfer data between memory buffers, and to transfer data to and from a
    //! UART. The test runs for 10 seconds before exiting.
    //!
    //! UART0, connected to the FTDI virtual COM port and running at 115,200,
    //! 8-N-1, is used to display messages from this application.
    //
    //*****************************************************************************

    //*****************************************************************************
    //
    // The number of SysTick ticks per second used for the SysTick interrupt.
    //
    //*****************************************************************************
    #define SYSTICKS_PER_SECOND 100

    //*****************************************************************************
    //
    // The size of the memory transfer source and destination buffers (in words).
    //
    //*****************************************************************************
    #define MEM_BUFFER_SIZE 1024

    //*****************************************************************************
    //
    // The size of the UART transmit and receive buffers. They do not need to be
    // the same size.
    //
    //*****************************************************************************
    #define UART_TXBUF_SIZE 256
    #define UART_RXBUF_SIZE 256

    //*****************************************************************************
    //
    // The count of uDMA errors. This value is incremented by the uDMA error
    // handler.
    //
    //*****************************************************************************
    static uint32_t g_ui32uDMAErrCount = 0;

    //*****************************************************************************
    //
    // Holds the ADC1 sample values
    //
    //*****************************************************************************
    uint32_t adc1value[8];

    //*****************************************************************************
    //
    // Source buffer for trying out uDMA transfers
    //
    //*****************************************************************************
    uint32_t DMA_SrcBuf[8];

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

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

    //*****************************************************************************
    //
    // SysTick timer interrupt handler. Don't currently need it
    //
    //*****************************************************************************
    void
    SysTickHandler(void)
    {
    }

    //*****************************************************************************
    //
    // 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++;
    }
    UARTprintf("uDMA error\n");
    }

    //*****************************************************************************
    //
    // uDMAIntHandler. Don't currently need to use it
    //
    //*****************************************************************************
    void
    uDMAIntHandler(void)
    {
    UARTprintf("unexpected uDMA interrupt\n");
    }

    //*****************************************************************************
    //
    // Package all the uDMA-related initialization functions into this routine
    //
    //*****************************************************************************
    void
    Initialize_uDMA(void)
    {
    //
    // Enable the uDMA controller at the system level. Enable it to continue
    // to run while the processor is in sleep.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);

    //
    // 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(ui8ControlTable);
    }
    //*****************************************************************************
    //
    // UART1IntHandler. Left this in for the compiler
    //
    //*****************************************************************************
    void
    UART1IntHandler(void)
    {
    UARTprintf("unexpected UART1 interrupt\n");
    }

    //*****************************************************************************
    //
    // Configure the UART and its pins. This must be called before UARTprintf().
    //
    //*****************************************************************************
    void
    ConfigureUART(void)
    {
    //
    // 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);

    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

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

    //*****************************************************************************
    //
    // Initialize ADC1
    //
    //*****************************************************************************
    void
    Initialize_ADC1(void)
    {
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    ADCHardwareOversampleConfigure(ADC1_BASE, 4);
    GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_4);
    ADCSequenceConfigure(ADC1_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    //ADCIntEnable(ADC1_BASE, 0);


    ADCSequenceStepConfigure(ADC1_BASE, 0, 0, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 1, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 2, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 3, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 4, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 5, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 6, ADC_CTL_CH10);
    ADCSequenceStepConfigure(ADC1_BASE, 0, 7, ADC_CTL_CH10 | ADC_CTL_IE | ADC_CTL_END);

    ADCSequenceEnable(ADC1_BASE, 0);

    ADCIntClear(ADC1_BASE, 0);
    }


    //*****************************************************************************
    //
    // Initialize ADC1 for DMA operation
    //
    //*****************************************************************************void
    void
    Initialize_ADC1_DMA(void)
    {
    int i;
    for (i=0;i<8;i++){
    DMA_SrcBuf[i] = i+50;
    }

    //
    // Configure ADC1 channel
    //
    ROM_uDMAChannelControlSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_32 |
    UDMA_ARB_8);


    //
    // Pull values from a visible source buffer
    //
    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    UDMA_MODE_BASIC, (void *) DMA_SrcBuf,
    (void *) adc1value,
    8);

    //
    // TODO: Pull values from SSFIFO0 instead
    //
    /*
    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
    UDMA_MODE_BASIC, (void *)(ADC1_BASE + ADC_O_SSFIFO0),
    (void *) adc1value,
    8);
    */

    //
    // Enable ADC1 channel, and perform 1 request just to prove the channel can transfer correctly
    //
    ROM_uDMAChannelEnable(UDMA_CHANNEL_ADC1);
    ROM_uDMAChannelRequest(UDMA_CHANNEL_ADC1);
    }

    //*****************************************************************************
    //
    // Triggers ADC1, waits til it's ready, prints it. repeat.
    //
    //*****************************************************************************
    void
    Print_ADC1_indefinitely(void)
    {
    while(1) {
    ADCProcessorTrigger(ADC1_BASE, 0);
    while(!ADCIntStatus(ADC1_BASE, 0, false)) { }
    //ADCSequenceDataGet(ADC1_BASE, 0, adc1value);
    ADCIntClear(ADC1_BASE, 0);
    UARTprintf("%4d\n", adc1value[0]);
    UARTprintf("%4d\n", adc1value[1]);
    UARTprintf("%4d\n", adc1value[2]);
    UARTprintf("%4d\n", adc1value[3]);
    UARTprintf("%4d\n", adc1value[4]);
    UARTprintf("%4d\n", adc1value[5]);
    UARTprintf("%4d\n", adc1value[6]);
    UARTprintf("%4d\n", adc1value[7]);
    }
    }
    //*****************************************************************************
    //
    // Initialize interrupts, peripherals, gpio, ADC1, uDMA, and UART0.
    // In a loop, trigger the collection of 8 samples and attempt to store them with
    // the uDMA
    //
    //*****************************************************************************
    int
    main(void)
    {
    int32_t i=0;

    //
    // Enable lazy stacking for interrupt handlers. This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();

    //
    // Set the clocking to run from the PLL at 50 MHz.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
    SYSCTL_XTAL_16MHZ);

    //
    // Enable peripherals to operate when CPU is in sleep.
    //
    ROM_SysCtlPeripheralClockGating(true);

    //
    // Enable the GPIO port that is used for the on-board LED.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    //
    // Enable the GPIO pins for the LED (PF2).
    //
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);

    //
    // Initialize the UART.
    //
    ConfigureUART();

    //
    // Configure SysTick to occur 100 times per second, to use as a time
    // reference. Enable SysTick to generate interrupts.
    //
    ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / SYSTICKS_PER_SECOND);
    ROM_SysTickIntEnable();
    ROM_SysTickEnable();

    //
    // Initialize the CPU usage measurement routine.
    //
    CPUUsageInit(ROM_SysCtlClockGet(), SYSTICKS_PER_SECOND, 2);

    //
    // Initialize uDMA
    //
    Initialize_uDMA();

    //
    // Initialize ADC1
    //
    Initialize_ADC1();
    Initialize_ADC1_DMA();


    for(i=0;i<1000;i++);
    /*
    for (i=0;i<8;i++){
    adc1value[i] = 23;
    }
    */
    //
    // Attempt to print ADC values
    //
    Print_ADC1_indefinitely();


    // NOTE: UNREACHABLE
    // Loop forever
    //
    while(1);
    }

  • I would also like to mention that I followed the advice of the following threads:

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/154188.aspx

    and, in particular,

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/225998.aspx

    which included this sample code from Stellaris Joe:

        //
        // Configure control parameters for DMA channel
        //
        MAP_uDMAChannelControlSet(g_pTestCase->ulChan | UDMA_PRI_SELECT,
                                  UDMA_SIZE_32 | UDMA_SRC_INC_NONE |
                                  UDMA_DST_INC_32 | ulArbSize);

        //
        // Set up the transfer for the DMA channel
        //
        MAP_uDMAChannelTransferSet(g_pTestCase->ulChan | UDMA_PRI_SELECT,
                                   UDMA_MODE_BASIC,
                                   (void *)(g_pADC->ulBase + ADC_O_SSFIFO0 + (0x20 * g_pTestCase->ulSeq)),
                                   g_ulAdcBuf, ADC_DSTBUF_SIZE);

     

    As far as I can tell, my set-up code is very similar to the code above, except that the source address points to an array in memory (rather than SSFIFO0). There is a typo in the code in my first post however. Since then, I have changed the ui32TransferSize parameter in ROM_uDMAChannelTransferSet from 8 to 256 to allow for multiple requests per transfer.

    Currently, if I make a software request for a transfer (with ROM_uDMAChannelRequest(UDMA_CHANNEL_ADC1); ), 8 bytes will transfer correctly from my source array to my destination array (DMA_SrcBuf[] to adc1value[]), and each repeated software request will result in additional correct 8-byte transfers. However, I still have been unable to use the ADC to make the requests.

    Here is the modified TransferSet function:

    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_ADC1 | UDMA_PRI_SELECT,
                                                       UDMA_MODE_BASIC, (void *) DMA_SrcBuf,
                                                       (void *) adc1value,
                                                       256);

    Just to be clear, I do still wish to eventually use the uDMA to transfer from SSFIFO0 to memory, I am just doing memory to memory transfers at the moment to provide additional clarity to myself as I debug.

    Thanks for reading!

     

  • Nobody has any idea? I'm still having problems with this.

  • Hello Robert,

    I do not see that you have called uDMAChannelAssign API to assign the ADC DMA Request to the UDMA

    Regards

    Amit