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.

EK-TM4C1294XL: Keeping the order of ADC Channel in the DMA destination buffer

Part Number: EK-TM4C1294XL

Hello,

I´m using ADC 0 with sample sequencer 0 (6 channels) and ADC 1 with sample sequencer 2 (4 channels). To transfer the data I´m using the DMA with ping pong mode. I re-enable the transfer channels in the corresponding interrupt handler. Now my problem is that the order of the adc channel in the destination buffer of the dma is not the same as in the sample sequencer because it´s changing always. 

Example:

Sample Sequencer     ->          DMA Destination Buffer

1. CH1                                       CH2

2. CH2                                       CH3

3. Ch3                                        CH1

Is there any trick to get the same order in the destination buffer as in the sample sequencer? Because if I wanna process the data I have to know which place in the destination buffer is which channel.

  • Hi,

      I have never seen a problem like this. I have some questions.

      - Does the problem happen to both ADC0 and ADC1 or only one of the ADCs?

      - Do you see the same problem with both the ping-pong buffers? Using your example, you should see CH1>CH2->CH3 in both the buffer A and B. Now you say it is reordered to CH2->CH3->CH1. Do you see the wrong order in both buffer A and buffer B?

      - How do you know for sure the destination buffer has the wrong order compared to the sample sequencer order? Can you do an experiment? Force CH1 to 0V, CH2 to 1.5V and CH3 to 3V? I just wanted to make sure that you didn't misinterpret the order. For example, if all three channels have very similar input range, then how will know for sure which one is which in the destination buffer. 

      - Can you show the code on how you configure the sequencer using ADCSequenceStepConfigure()?

  • Hi Charles,

    - Yes, the problem occurs to both ADC.

    - Yes, the problem occurs to both buffers.

    - I tied one channel to 0,61 V (fourth channel in the sample sequencer) and the others to 1,65 V. As you can see it randomly changes the place in the destination buffer. 

    Here it is now the second place.

    Here it is now the third place.

    - Initialization of the ADC and DMA:

    Thanks for help.

    Kind regards,

    Constantin

  • Hi Constantin,

      I think the problem may lies with the way you trigger the sampling using ADC_TRIGGER_ALWAYS. Can you do an experiment using ADC_TRIGGER_TIMER. Try to create a periodic trigger using a timer and see if that makes a difference. It seems to me that you keep on triggering ADC sampling while the uDMA is still transferring data. Please refer to the adc_udma_pingpong example in the latest TivaWare (2.2.0.295) release where it uses the timer to trigger ADC sampling. 

  • Hello Charles,

    thanks. I won´t be at work next week. I will share the result as soon as I tried it out.

  • Hi Constantin,

      Thanks for the heads up.  Let me know your result. 

  • Hi Charles,

    I created a periodic timer which triggers the ADC every 100 kHz but the result is still the same as I use ADC_TRIGGER_ALWAYS. Is there something else I can try?

    Kind regards,

    Constantin

  • Hi Constantin,

      As I have never seen a problem like this I will need you to some experiments. 

      - I assume if you were to use the CPU to read out the ADC data, they will be read out in the correct order. Is that correct? The reason I'm asking is to isolate the problem between uDMA and ADC. If you are reading wrong order using the CPU then the problem is not the uDMA. However, I tend to think the problem is more likely to be the uDMA.

      - To keep it simple, can you remove the ADCHardareOversampleConfigure() for the time being? The ADCHardareOversampleConfigure() will slow down the ADC sampling rate. Let's keep the ADC running as fast as possible for now. 

      - Try with 3 ADC channels with each channel driven to 0, 1.65V and 3.3V separately and use the uDMA to read them out. I'd like to know if the number of channels in a sequencer has any effect on the result. 

      - If possible, can you try just regular uDMA transfer instead of ping-pong mode. The goal of the experiment is to verify if ping-pong mode has any effect on the ordering. We don't care about uDMA throughput at this moment. 

      

  • Hello Charles,

    - If I use the ADC Clock frequency: 16 MHz with ADC_CLOCK_RATE_HALF, the channels are read in the correct order. But If I increase the frequency or the rate, the order of the channel changes in the second run.

    - It doesn´t change anything.

    - The first run works perfectly fine as in Ping Pong mode. In the second run the order of the channel changes. For this test I used 4 channels with one tied to 1,65 V, two tied to 0 and one to 3,3 V.

    The first run is until the black beam and as you can see during the second run it changes the order. The same applies to three channel. Here is the code if you want to look at it:

    usb_dev_cdcserial.c
    //****************************************************************************
    //
    // usb_dev_cserial.c - Main routines for the USB CDC serial example.
    //
    // Copyright (c) 2019-2020 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.2.0.295 of the EK-TM4C1294XL Firmware Package.
    //
    //****************************************************************************
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/timer.h"
    #include "driverlib/uart.h"
    #include "driverlib/usb.h"
    #include "usblib/usblib.h"
    #include "usblib/usbcdc.h"
    #include "usblib/usb-ids.h"
    #include "usblib/device/usbdevice.h"
    #include "usblib/device/usbdcdc.h"
    #include "utils/ustdlib.h"
    #include "drivers/pinout.h"
    #include "usb_structs.h"
    #include "driverlib/ssi.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/adc.h"
    #include "driverlib/udma.h"
    #include "inc/hw_adc.h"
    
    //****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>USB CDC Serial Device example (usb_dev_cdcserial)</h1>
    //!
    //! This example application turns the EK-TM4C1294XL LaunchPad into a single
    //! port USB CDC device when connected to a USB host system. The application
    //! supports the USB Communication Device Class, Abstract Control Model to
    //! by interacting with the USB host via a virtual COM port. For this example,
    //! the evaluation kit will enumerate as a CDC device with one virtual
    //! serial port that redirects UART0 traffic to and from the USB host system.
    //!
    //! The virtual serial port will echo data to the physical UART0 port on the
    //! device which is connected to the virtual serial port on the ICDI device
    //! on this board. The physical UART0 will also echo onto the virtual serial
    //! device provided by the TM4C controller.
    //!
    //! Assuming you installed TivaWare in the default directory, a driver
    //! information (INF) file for use with Windows XP, Windows Vista, Windows 7,
    //! and Windows 10 can be found in C:/TivaWare_C_Series-x.x/windows_drivers.
    //! For Windows 2000, the required INF file is in
    //! C:/TivaWare_C_Series-x.x/windows_drivers/win2K.
    //
    //****************************************************************************
    
    //****************************************************************************
    //
    // The system tick rate expressed both as ticks per second and a millisecond
    // period.
    //
    //****************************************************************************
    #define SYSTICKS_PER_SECOND 100
    #define SYSTICK_PERIOD_MS (1000 / SYSTICKS_PER_SECOND)
    
    //****************************************************************************
    //
    // Variable to remember our clock frequency
    //
    //****************************************************************************
    uint32_t g_ui32SysClock = 0;
    
    //****************************************************************************
    //
    // Variables tracking transmit and receive counts.
    //
    //****************************************************************************
    volatile uint32_t g_ui32UARTTxCount = 0;
    volatile uint32_t g_ui32UARTRxCount = 0;
    
    //****************************************************************************
    //
    // Default line coding settings for the redirected UART.
    //
    //****************************************************************************
    #define DEFAULT_BIT_RATE        115200
    #define DEFAULT_UART_CONFIG     (UART_CONFIG_WLEN_8 | UART_CONFIG_PAR_NONE | \
                                     UART_CONFIG_STOP_ONE)
    
    //****************************************************************************
    //
    // GPIO peripherals and pins muxed with the redirected UART.  These will
    // depend upon the IC in use and the UART selected in UART0_BASE.  Be careful
    // that these settings all agree with the hardware you are using.
    //
    //****************************************************************************
    #define TX_GPIO_BASE            GPIO_PORTA_BASE
    #define TX_GPIO_PERIPH          SYSCTL_PERIPH_GPIOA
    #define TX_GPIO_PIN             GPIO_PIN_1
    
    #define RX_GPIO_BASE            GPIO_PORTA_BASE
    #define RX_GPIO_PERIPH          SYSCTL_PERIPH_GPIOA
    #define RX_GPIO_PIN             GPIO_PIN_0
    
    //****************************************************************************
    //
    // Flag indicating whether or not we are currently sending a Break condition.
    //
    //****************************************************************************
    static bool g_bSendingBreak = false;
    
    //****************************************************************************
    //
    // Global system tick counter
    //
    //****************************************************************************
    volatile uint32_t g_ui32SysTickCount = 0;
    
    //****************************************************************************
    //
    // Global flag indicating that a USB configuration has been set.
    //
    //****************************************************************************
    static volatile bool g_bUSBConfigured = false;
    
    //****************************************************************************
    //
    // 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
    
    //****************************************************************************
    //
    // This function is called whenever serial data is received from the UART.
    // It is passed the accumulated error flags from each character received in
    // this interrupt and determines from them whether or not an interrupt
    // notification to the host is required.
    //
    // If a notification is required and the control interrupt endpoint is idle,
    // we send the notification immediately.  If the endpoint is not idle, we
    // accumulate the errors in a global variable which will be checked on
    // completion of the previous notification and used to send a second one
    // if necessary.
    //
    //****************************************************************************
    static void
    CheckForSerialStateChange(const tUSBDCDCDevice *psDevice, int32_t i32Errors)
    {
        unsigned short usSerialState;
    
        //
        // Clear our USB serial state.  Since we are faking the handshakes, always
        // set the TXCARRIER (DSR) and RXCARRIER (DCD) bits.
        //
        usSerialState = USB_CDC_SERIAL_STATE_TXCARRIER |
                        USB_CDC_SERIAL_STATE_RXCARRIER;
    
        //
        // Are any error bits set?
        //
        if(i32Errors)
        {
            //
            // At least one error is being notified so translate from our hardware
            // error bits into the correct state markers for the USB notification.
            //
            if(i32Errors & UART_DR_OE)
            {
                usSerialState |= USB_CDC_SERIAL_STATE_OVERRUN;
            }
    
            if(i32Errors & UART_DR_PE)
            {
                usSerialState |= USB_CDC_SERIAL_STATE_PARITY;
            }
    
            if(i32Errors & UART_DR_FE)
            {
                usSerialState |= USB_CDC_SERIAL_STATE_FRAMING;
            }
    
            if(i32Errors & UART_DR_BE)
            {
                usSerialState |= USB_CDC_SERIAL_STATE_BREAK;
            }
    
            //
            // Call the CDC driver to notify the state change.
            //
            USBDCDCSerialStateChange((void *)psDevice, usSerialState);
        }
    }
    
    //****************************************************************************
    //
    // Read as many characters from the UART FIFO as we can and move them into
    // the CDC transmit buffer.
    //
    // \return Returns UART error flags read during data reception.
    //
    //****************************************************************************
    static int32_t
    ReadUARTData(void)
    {
        int32_t i32Char, i32Errors;
        uint8_t ucChar;
        uint32_t ui32Space;
    
        //
        // Clear our error indicator.
        //
        i32Errors = 0;
    
        //
        // How much space do we have in the buffer?
        //
        ui32Space = USBBufferSpaceAvailable((tUSBBuffer *)&g_sTxBuffer);
    
        //
        // Read data from the UART FIFO until there is none left or we run
        // out of space in our receive buffer.
        //
        while(ui32Space && UARTCharsAvail(UART0_BASE))
        {
            //
            // Read a character from the UART FIFO into the ring buffer if no
            // errors are reported.
            //
            i32Char = UARTCharGetNonBlocking(UART0_BASE);
    
            //
            // If the character did not contain any error notifications,
            // copy it to the output buffer.
            //
            if(!(i32Char & ~0xFF))
            {
                ucChar = (uint8_t)(i32Char & 0xFF);
    
                USBBufferWrite((tUSBBuffer *)&g_sTxBuffer,
                               (uint8_t *)&ucChar, 1);
    
                //
                // Decrement the number of bytes we know the buffer can accept.
                //
                ui32Space--;
            }
            else
            {
                //
                // Update our error accumulator.
                //
                i32Errors |= i32Char;
            }
    
            //
            // Update our count of bytes received via the UART.
            //
            g_ui32UARTRxCount++;
        }
    
        //
        // Pass back the accumulated error indicators.
        //
        return(i32Errors);
    }
    
    //****************************************************************************
    //
    // Take as many bytes from the transmit buffer as we have space for and move
    // them into the USB UART's transmit FIFO.
    //
    //****************************************************************************
    static void
    USBUARTPrimeTransmit(uint32_t ui32Base)
    {
        uint32_t ui32Read;
        uint8_t ucChar;
    
        //
        // If we are currently sending a break condition, don't receive any
        // more data. We will resume transmission once the break is turned off.
        //
        if(g_bSendingBreak)
        {
            return;
        }
    
        //
        // If there is space in the UART FIFO, try to read some characters
        // from the receive buffer to fill it again.
        //
        while(UARTSpaceAvail(ui32Base))
        {
            //
            // Get a character from the buffer.
            //
            ui32Read = USBBufferRead((tUSBBuffer *)&g_sRxBuffer, &ucChar, 1);
    
            //
            // Did we get a character?
            //
            if(ui32Read)
            {
                //
                // Place the character in the UART transmit FIFO.
                //
                UARTCharPut(ui32Base, ucChar);
    
                //
                // Update our count of bytes transmitted via the UART.
                //
                g_ui32UARTTxCount++;
            }
            else
            {
                //
                // We ran out of characters so exit the function.
                //
                return;
            }
        }
    }
    
    //****************************************************************************
    //
    // Interrupt handler for the system tick counter.
    //
    //****************************************************************************
    void
    SysTickIntHandler(void)
    {
        //
        // Update our system time.
        //
        g_ui32SysTickCount++;
    }
    
    //****************************************************************************
    //
    // Interrupt handler for the UART which we are redirecting via USB.
    //
    //****************************************************************************
    void
    USBUARTIntHandler(void)
    {
        uint32_t ui32Ints;
        int32_t i32Errors;
    
        //
        // Get and clear the current interrupt source(s)
        //
        ui32Ints = UARTIntStatus(UART0_BASE, true);
        UARTIntClear(UART0_BASE, ui32Ints);
    
        //
        // Are we being interrupted because the TX FIFO has space available?
        //
        if(ui32Ints & UART_INT_TX)
        {
            //
            // Move as many bytes as we can into the transmit FIFO.
            //
            USBUARTPrimeTransmit(UART0_BASE);
    
            //
            // If the output buffer is empty, turn off the transmit interrupt.
            //
            if(!USBBufferDataAvailable(&g_sRxBuffer))
            {
                UARTIntDisable(UART0_BASE, UART_INT_TX);
            }
        }
    
        //
        // Handle receive interrupts.
        //
        if(ui32Ints & (UART_INT_RX | UART_INT_RT))
        {
            //
            // Read the UART's characters into the buffer.
            //
            i32Errors = ReadUARTData();
    
            //
            // Check to see if we need to notify the host of any errors we just
            // detected.
            //
            CheckForSerialStateChange(&g_sCDCDevice, i32Errors);
        }
    }
    
    //****************************************************************************
    //
    // Set the state of the RS232 RTS and DTR signals.  Handshaking is not
    // supported so this request will be ignored.
    //
    //****************************************************************************
    static void
    SetControlLineState(unsigned short usState)
    {
    }
    
    //****************************************************************************
    //
    // Set the communication parameters to use on the UART.
    //
    //****************************************************************************
    static bool
    SetLineCoding(tLineCoding *psLineCoding)
    {
        uint32_t ui32Config;
        bool bRetcode;
    
        //
        // Assume everything is OK until we detect any problem.
        //
        bRetcode = true;
    
        //
        // Word length.  For invalid values, the default is to set 8 bits per
        // character and return an error.
        //
        switch(psLineCoding->ui8Databits)
        {
            case 5:
            {
                ui32Config = UART_CONFIG_WLEN_5;
                break;
            }
    
            case 6:
            {
                ui32Config = UART_CONFIG_WLEN_6;
                break;
            }
    
            case 7:
            {
                ui32Config = UART_CONFIG_WLEN_7;
                break;
            }
    
            case 8:
            {
                ui32Config = UART_CONFIG_WLEN_8;
                break;
            }
    
            default:
            {
                ui32Config = UART_CONFIG_WLEN_8;
                bRetcode = false;
                break;
            }
        }
    
        //
        // Parity.  For any invalid values, we set no parity and return an error.
        //
        switch(psLineCoding->ui8Parity)
        {
            case USB_CDC_PARITY_NONE:
            {
                ui32Config |= UART_CONFIG_PAR_NONE;
                break;
            }
    
            case USB_CDC_PARITY_ODD:
            {
                ui32Config |= UART_CONFIG_PAR_ODD;
                break;
            }
    
            case USB_CDC_PARITY_EVEN:
            {
                ui32Config |= UART_CONFIG_PAR_EVEN;
                break;
            }
    
            case USB_CDC_PARITY_MARK:
            {
                ui32Config |= UART_CONFIG_PAR_ONE;
                break;
            }
    
            case USB_CDC_PARITY_SPACE:
            {
                ui32Config |= UART_CONFIG_PAR_ZERO;
                break;
            }
    
            default:
            {
                ui32Config |= UART_CONFIG_PAR_NONE;
                bRetcode = false;
                break;
            }
        }
    
        //
        // Stop bits.  Our hardware only supports 1 or 2 stop bits whereas CDC
        // allows the host to select 1.5 stop bits.  If passed 1.5 (or any other
        // invalid or unsupported value of ui8Stop, we set up for 1 stop bit but
        // return an error in case the caller needs to Stall or otherwise report
        // this back to the host.
        //
        switch(psLineCoding->ui8Stop)
        {
            //
            // One stop bit requested.
            //
            case USB_CDC_STOP_BITS_1:
            {
                ui32Config |= UART_CONFIG_STOP_ONE;
                break;
            }
    
            //
            // Two stop bits requested.
            //
            case USB_CDC_STOP_BITS_2:
            {
                ui32Config |= UART_CONFIG_STOP_TWO;
                break;
            }
    
            //
            // Other cases are either invalid values of ui8Stop or values that we
            // cannot support so set 1 stop bit but return an error.
            //
            default:
            {
                ui32Config = UART_CONFIG_STOP_ONE;
                bRetcode |= false;
                break;
            }
        }
    
        //
        // Set the UART mode appropriately.
        //
        UARTConfigSetExpClk(UART0_BASE, g_ui32SysClock, psLineCoding->ui32Rate,
                            ui32Config);
    
        //
        // Let the caller know if we had a problem or not.
        //
        return(bRetcode);
    }
    
    //****************************************************************************
    //
    // Get the communication parameters in use on the UART.
    //
    //****************************************************************************
    static void
    GetLineCoding(tLineCoding *psLineCoding)
    {
        uint32_t ui32Config;
        uint32_t ui32Rate;
    
        //
        // Get the current line coding set in the UART.
        //
        UARTConfigGetExpClk(UART0_BASE, g_ui32SysClock, &ui32Rate,
                            &ui32Config);
        psLineCoding->ui32Rate = ui32Rate;
    
        //
        // Translate the configuration word length field into the format expected
        // by the host.
        //
        switch(ui32Config & UART_CONFIG_WLEN_MASK)
        {
            case UART_CONFIG_WLEN_8:
            {
                psLineCoding->ui8Databits = 8;
                break;
            }
    
            case UART_CONFIG_WLEN_7:
            {
                psLineCoding->ui8Databits = 7;
                break;
            }
    
            case UART_CONFIG_WLEN_6:
            {
                psLineCoding->ui8Databits = 6;
                break;
            }
    
            case UART_CONFIG_WLEN_5:
            {
                psLineCoding->ui8Databits = 5;
                break;
            }
        }
    
        //
        // Translate the configuration parity field into the format expected
        // by the host.
        //
        switch(ui32Config & UART_CONFIG_PAR_MASK)
        {
            case UART_CONFIG_PAR_NONE:
            {
                psLineCoding->ui8Parity = USB_CDC_PARITY_NONE;
                break;
            }
    
            case UART_CONFIG_PAR_ODD:
            {
                psLineCoding->ui8Parity = USB_CDC_PARITY_ODD;
                break;
            }
    
            case UART_CONFIG_PAR_EVEN:
            {
                psLineCoding->ui8Parity = USB_CDC_PARITY_EVEN;
                break;
            }
    
            case UART_CONFIG_PAR_ONE:
            {
                psLineCoding->ui8Parity = USB_CDC_PARITY_MARK;
                break;
            }
    
            case UART_CONFIG_PAR_ZERO:
            {
                psLineCoding->ui8Parity = USB_CDC_PARITY_SPACE;
                break;
            }
        }
    
        //
        // Translate the configuration stop bits field into the format expected
        // by the host.
        //
        switch(ui32Config & UART_CONFIG_STOP_MASK)
        {
            case UART_CONFIG_STOP_ONE:
            {
                psLineCoding->ui8Stop = USB_CDC_STOP_BITS_1;
                break;
            }
    
            case UART_CONFIG_STOP_TWO:
            {
                psLineCoding->ui8Stop = USB_CDC_STOP_BITS_2;
                break;
            }
        }
    }
    
    //****************************************************************************
    //
    // This function sets or clears a break condition on the redirected UART RX
    // line.  A break is started when the function is called with \e bSend set to
    // \b true and persists until the function is called again with \e bSend set
    // to \b false.
    //
    //****************************************************************************
    static void
    SendBreak(bool bSend)
    {
        //
        // Are we being asked to start or stop the break condition?
        //
        if(!bSend)
        {
            //
            // Remove the break condition on the line.
            //
            UARTBreakCtl(UART0_BASE, false);
            g_bSendingBreak = false;
        }
        else
        {
            //
            // Start sending a break condition on the line.
            //
            UARTBreakCtl(UART0_BASE, true);
            g_bSendingBreak = true;
        }
    }
    
    //****************************************************************************
    //
    // Handles CDC driver notifications related to control and setup of the
    // device.
    //
    // \param pvCBData is the client-supplied callback pointer for this channel.
    // \param ui32Event identifies the event we are being notified about.
    // \param ui32MsgValue is an event-specific value.
    // \param pvMsgData is an event-specific pointer.
    //
    // This function is called by the CDC driver to perform control-related
    // operations on behalf of the USB host.  These functions include setting
    // and querying the serial communication parameters, setting handshake line
    // states and sending break conditions.
    //
    // \return The return value is event-specific.
    //
    //****************************************************************************
    uint32_t
    ControlHandler(void *pvCBData, uint32_t ui32Event,
                   uint32_t ui32MsgValue, void *pvMsgData)
    {
        //
        // Which event are we being asked to process?
        //
        switch(ui32Event)
        {
            //
            // We are connected to a host and communication is now possible.
            //
            case USB_EVENT_CONNECTED:
                g_bUSBConfigured = true;
    
                //
                // Flush our buffers.
                //
                USBBufferFlush(&g_sTxBuffer);
                USBBufferFlush(&g_sRxBuffer);
    
                break;
    
            //
            // The host has disconnected.
            //
            case USB_EVENT_DISCONNECTED:
                g_bUSBConfigured = false;
    
                break;
    
            //
            // Return the current serial communication parameters.
            //
            case USBD_CDC_EVENT_GET_LINE_CODING:
                GetLineCoding(pvMsgData);
                break;
    
            //
            // Set the current serial communication parameters.
            //
            case USBD_CDC_EVENT_SET_LINE_CODING:
                SetLineCoding(pvMsgData);
                break;
    
            //
            // Set the current serial communication parameters.
            //
            case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE:
                SetControlLineState((uint16_t)ui32MsgValue);
                break;
    
            //
            // Send a break condition on the serial line.
            //
            case USBD_CDC_EVENT_SEND_BREAK:
                SendBreak(true);
                break;
    
            //
            // Clear the break condition on the serial line.
            //
            case USBD_CDC_EVENT_CLEAR_BREAK:
                SendBreak(false);
                break;
    
            //
            // Ignore SUSPEND and RESUME for now.
            //
            case USB_EVENT_SUSPEND:
            case USB_EVENT_RESUME:
                break;
    
            //
            // We don't expect to receive any other events.  Ignore any that show
            // up in a release build or hang in a debug build.
            //
            default:
    #ifdef DEBUG
                while(1);
    #else
                break;
    #endif
    
        }
    
        return(0);
    }
    
    //*****************************************************************************
    //
    // Handles CDC driver notifications related to the transmit channel (data to
    // the USB host).
    //
    // \param ui32CBData is the client-supplied callback pointer for this channel.
    // \param ui32Event identifies the event we are being notified about.
    // \param ui32MsgValue is an event-specific value.
    // \param pvMsgData is an event-specific pointer.
    //
    // This function is called by the CDC driver to notify us of any events
    // related to operation of the transmit data channel (the IN channel carrying
    // data to the USB host).
    //
    // \return The return value is event-specific.
    //
    //*****************************************************************************
    uint32_t
    TxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,
              void *pvMsgData)
    {
        //
        // Which event have we been sent?
        //
        switch(ui32Event)
        {
            case USB_EVENT_TX_COMPLETE:
                //
                // Since we are using the USBBuffer, we don't need to do anything
                // here.
                //
                break;
    
            //
            // We don't expect to receive any other events.  Ignore any that show
            // up in a release build or hang in a debug build.
            //
            default:
    #ifdef DEBUG
                while(1);
    #else
                break;
    #endif
    
        }
        return(0);
    }
    
    //*****************************************************************************
    //
    // Handles CDC driver notifications related to the receive channel (data from
    // the USB host).
    //
    // \param ui32CBData is the client-supplied callback data value for this
    //        channel.
    // \param ui32Event identifies the event we are being notified about.
    // \param ui32MsgValue is an event-specific value.
    // \param pvMsgData is an event-specific pointer.
    //
    // This function is called by the CDC driver to notify us of any events
    // related to operation of the receive data channel (the OUT channel carrying
    // data from the USB host).
    //
    // \return The return value is event-specific.
    //
    //*****************************************************************************
    uint32_t
    RxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,
              void *pvMsgData)
    {
        uint32_t ui32Count;
    
        //
        // Which event are we being sent?
        //
        switch(ui32Event)
        {
            //
            // A new packet has been received.
            //
            case USB_EVENT_RX_AVAILABLE:
            {
                //
                // Feed some characters into the UART TX FIFO and enable the
                // interrupt so we are told when there is more space.
                //
                USBUARTPrimeTransmit(UART0_BASE);
                ROM_UARTIntEnable(UART0_BASE, UART_INT_TX);
                break;
            }
    
            //
            // We are being asked how much unprocessed data we have still to
            // process. We return 0 if the UART is currently idle or 1 if it is
            // in the process of transmitting something. The actual number of
            // bytes in the UART FIFO is not important here, merely whether or
            // not everything previously sent to us has been transmitted.
            //
            case USB_EVENT_DATA_REMAINING:
            {
                //
                // Get the number of bytes in the buffer and add 1 if some data
                // still has to clear the transmitter.
                //
                ui32Count = ROM_UARTBusy(UART0_BASE) ? 1 : 0;
                return(ui32Count);
            }
    
            //
            // We are being asked to provide a buffer into which the next packet
            // can be read. We do not support this mode of receiving data so let
            // the driver know by returning 0. The CDC driver should not be sending
            // this message but this is included just for illustration and
            // completeness.
            //
            case USB_EVENT_REQUEST_BUFFER:
            {
                return(0);
            }
    
            //
            // We don't expect to receive any other events.  Ignore any that show
            // up in a release build or hang in a debug build.
            //
            default:
    #ifdef DEBUG
                while(1);
    #else
                break;
    #endif
        }
    
        return(0);
    }
    //****************************************************************************
    //
    // This function will print out to the console UART and not the echo UART.
    //
    //****************************************************************************
    void
    CommandPrint(const char *pcStr)
    {
        uint32_t ui32Index;
        const char cCR = 0xd;
    
    
        ui32Index = 0;
    
        while(pcStr[ui32Index] != 0)
        {
            //
            // Wait for space for two bytes in case there is a need to send out
            // the line feed plus the carriage return.
            //
            while(USBBufferSpaceAvailable(&g_sTxBuffer) < 2)
            {
            }
    
            //
            // Print the next character.
            //
            USBBufferWrite(&g_sTxBuffer, (const uint8_t *)&pcStr[ui32Index],
                           1);
    
            //
            // If this is a line feed then send a carriage return as well.
            //
            if(pcStr[ui32Index] == 0xa)
            {
                USBBufferWrite(&g_sTxBuffer, (const uint8_t *)&cCR, 1);
    
            }
    
            ui32Index++;
        }
    }
    //****************************************************************************
    //
    // Init ADC
    //
    //****************************************************************************
    static void
    InitADC()
    {
        uint32_t sequencer_ADC0 = 2;
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0);
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_1);
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2);
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
    
        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL| ADC_CLOCK_RATE_HALF, 15);
    
        IntDisable(INT_ADC0SS2);
        ADCIntDisable(ADC0_BASE, sequencer_ADC0);
        ADCSequenceDisable(ADC0_BASE, sequencer_ADC0);
    
        ADCSequenceConfigure(ADC0_BASE, sequencer_ADC0, ADC_TRIGGER_ALWAYS, 0);
    
        ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH3);
        ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH2);
        ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH1);
        ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH0| ADC_CTL_IE | ADC_CTL_END);
    
        ADCSequenceDMAEnable(ADC0_BASE, sequencer_ADC0);
        ADCIntClear(ADC0_BASE, 2);
    
        ADCIntEnable(ADC0_BASE, sequencer_ADC0);
        IntEnable(INT_ADC0SS2);
        ADCSequenceEnable(ADC0_BASE, 2);
    
    }
    //****************************************************************************
    //
    // Init DMA
    //
    //****************************************************************************
    #define BufferSize 16
    static uint32_t ui32ADC0BufferDMAPri[BufferSize];
    //****************************************************************************
    #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
    
    
    static void
    InitDMA()
    {
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA))
            {
            }
    
        uDMAEnable();
    
    
        uDMAControlBaseSet(pui8ControlTable);
    
        uDMAChannelAssign(UDMA_CH16_ADC0_2 );
    
    
        uDMAChannelAttributeDisable(UDMA_CHANNEL_ADC2,
                                       UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY |
                                       UDMA_ATTR_REQMASK | UDMA_ATTR_USEBURST);
    
    
        uDMAChannelControlSet(UDMA_CHANNEL_ADC2, UDMA_SIZE_32 |
                              UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | UDMA_ARB_2 );
    
        uDMAChannelTransferSet(UDMA_CHANNEL_ADC2,  UDMA_MODE_BASIC,
                               (void *)(ADC0_BASE + ADC_O_SSFIFO2), &ui32ADC0BufferDMAPri,
                               BufferSize);
    
        uDMAChannelAttributeEnable(UDMA_CHANNEL_ADC2, UDMA_ATTR_USEBURST);
    
        uDMAChannelEnable(UDMA_CHANNEL_ADC2);
    }
    //****************************************************************************
    //
    // Init Timer0A
    //
    //****************************************************************************
    static void
    InitTimer0A(void)
    {
        //
        // Enable peripheral for Timer0.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0))
            {
            }
    
        //
        // Timer A configured as a 16 Bit counter which counts down.
        //
        TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PERIODIC);
    
        //
        // Set prescaler 256 and the load value.
        //
        TimerPrescaleSet(TIMER0_BASE, TIMER_A, 255);
        TimerLoadSet(TIMER0_BASE, TIMER_A, 4687);
    
        //
        //Enable interrupt for timer A timeout.
        //
        IntEnable(INT_TIMER0A);
        TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    
        //
        //Enable Timer A
        //
        TimerEnable(TIMER0_BASE, TIMER_A);
    }
    
    //****************************************************************************
    //
    // ISR
    //
    //****************************************************************************
    static volatile uint32_t ui32Timer0ACounter = 0;
    static volatile uint32_t ui32ADC0Seq2Buffer[4];
    static volatile uint32_t ui32FlagForChannel = 0;
    //****************************************************************************
    void
    ADC0Seq2Handler()
    {
        int i, k;
        char string[5];
    
        ADCIntClear(ADC0_BASE, 2);
    
        if(ui32Timer0ACounter >= 1000){
            //ADCSequenceDataGet(ADC0_BASE,2, ui32ADC0Seq2Buffer);
            //
            // Send the data over the virtual com port as ASCII.
            //
            for(k = 0; k < BufferSize ; k++){
                for(i = 0; i < 4; i++)
                {
                    string[3-i] = (ui32ADC0BufferDMAPri[k] % 10) + '0';
                    ui32ADC0BufferDMAPri[k] /= 10;
                }
                string[4] = '\0';
    
                CommandPrint(string);
                CommandPrint("\r");
            }
            ui32Timer0ACounter = 0;
            ui32FlagForChannel = 1;
    
        }
    }
    
    void
    Timer0AIntHandler()
    {
        TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
        ui32Timer0ACounter++;
    
    }
    
    //****************************************************************************
    //
    // This is the main application entry function.
    //
    //****************************************************************************
    int
    main(void)
    {
    
        uint32_t ui32PLLRate;
    
        //
        // Run from the PLL at 120 MHz.
        // Note: SYSCTL_CFG_VCO_240 is a new setting provided in TivaWare 2.2.x and
        // later to better reflect the actual VCO speed due to SYSCTL#22.
        //
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                                 SYSCTL_OSC_MAIN |
                                                 SYSCTL_USE_PLL |
                                                 SYSCTL_CFG_VCO_240), 120000000);
    
        //
        // Not configured initially.
        //
        g_bUSBConfigured = false;
    
        //
        // Enable the peripherals used in this example.
        //
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
    
        //
        // Configure the device pins.
        //
        PinoutSet(false, true);
    
        //
        // Set the default UART configuration.
        //
        UARTConfigSetExpClk(UART0_BASE, g_ui32SysClock, DEFAULT_BIT_RATE,
                            DEFAULT_UART_CONFIG);
        UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
    
        //
        // Configure and enable UART interrupts.
        //
        UARTIntClear(UART0_BASE, UARTIntStatus(UART0_BASE, false));
        UARTIntEnable(UART0_BASE, (UART_INT_OE | UART_INT_BE | UART_INT_PE |
                      UART_INT_FE | UART_INT_RT | UART_INT_TX | UART_INT_RX));
    
        //
        // Enable the system tick.
        //
        SysTickPeriodSet(g_ui32SysClock / SYSTICKS_PER_SECOND);
        SysTickIntEnable();
        SysTickEnable();
    
        //
        // Initialize the transmit and receive buffers.
        //
        USBBufferInit(&g_sTxBuffer);
        USBBufferInit(&g_sRxBuffer);
    
        //
        // Tell the USB library the CPU clock and the PLL frequency.  This is a
        // new requirement for TM4C129 devices.
        //
        SysCtlVCOGet(SYSCTL_XTAL_25MHZ, &ui32PLLRate);
        USBDCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &g_ui32SysClock);
        USBDCDFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate);
        
        //
        // Forcing device mode so that the VBUS and ID pins are not used or
        // monitored by the USB controller. For USB OTG, this function should
        // not be called.  If the USB Host will supply power, and the LaunchPad
        // power jumper is set to "OTG", this function should not be called.
        //
        USBStackModeSet(0, eUSBModeForceDevice, 0);
        
        //
        // Pass the device information to the USB library and place the device
        // on the bus.
        //
        USBDCDCInit(0, &g_sCDCDevice);
    
    
        //
        // Enable interrupts now that the application is ready to start.
        //
        IntEnable(INT_UART0);
    
        InitTimer0A();
        InitADC();
        InitDMA();
        //
        // Main application loop.
        //
        while(1)
        {
            if(ui32FlagForChannel == 1){
                ui32FlagForChannel = 0;
                uDMAChannelTransferSet(UDMA_CHANNEL_ADC2,  UDMA_MODE_BASIC,
                                                 (void *)(ADC0_BASE + ADC_O_SSFIFO2), &ui32ADC0BufferDMAPri,
                                                 BufferSize);
                uDMAChannelEnable(UDMA_CHANNEL_ADC2);
    
            }
        }
    }
    

    Thanks for help.

    Kind regards,

    Constantin

  • Constantin Weis said:
    - If I use the ADC Clock frequency: 16 MHz with ADC_CLOCK_RATE_HALF, the channels are read in the correct order. But If I increase the frequency or the rate, the order of the channel changes in the second run.

    Hi Constantin,

      Is this done by using the CPU to read?   If this is the case, then we can briefly leave out the uDMA from the picture since using CPU will read out different order. 

      I quickly went through your code. Until now, I never realized your ADC/uDMA code is only part of the your USB CDC application. I'm not sure if you are sending out the ADC data via the USB and find that the data is in wrong order. Since the USB comes into the picture, I don't know if the order was changed due to the USB or not.  Is it possible for you to keep it simple by creating a testcase focusing on the ADC and uDMA only? 

  • Hi Charles,

    yes, this is done by using the CPU to read. I created a testcase without the whole USB part but the results are still the same.

    Kind regards,

    Constantin 

  • Hi Constantin,

      If the CPU will also read out incorrect order then we can even leave out the uDMA. This is the first time I have seen report that the ADC can store the sampled channels in wrong order in the ADC FIFO. I have some questions.

       Below is the earlier code you sent on July-23th. Your configure step 0 to be CTL_CH3, step 1 to CTL_CH2 and so on. Is this intentionally done in reverse order?

    ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH3);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH2);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 2, ADC_CTL_CH1);
    ADCSequenceStepConfigure(ADC0_BASE, 2, 3, ADC_CTL_CH0| ADC_CTL_IE | ADC_CTL_END);

    Your VCO is 240Mhz as in below SYSCLK configuration. This clock config is fine. No issue here.

    g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
    SYSCTL_OSC_MAIN |
    SYSCTL_USE_PLL |
    SYSCTL_CFG_VCO_240), 120000000);

      You also wrote below code for the ADC clock configuration. Is this what makes it work or what makes it not work? To me I think the below ADC clock config is correct. It configures the ADC clock to be 16Mhz which should be ok.  I just don't know if you have tried experiments that might have caused the ADC clock to run too fast or not. 

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL| ADC_CLOCK_RATE_HALF, 15); 

    With that said, I'm interested to find out if configure the ADC clock using the PIOSC as the clock source. 

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1); // PIOSC=16Mhz for 1Msps

    Can you call SysCtlVCOGet to find out what the VCO frequency? The ADC clock is derived from the VCO when you call ADCClockConfigSet. 

  • Hi Charles,

    the order of the channel is not  intentionally in reverse order, but it doesn´t make a difference if I change the order. The VCO frequency is as expected.

    It doesn´t make a difference whether I use the PIOSC as the clock source.

    Charles Tsai said:
    I just don't know if you have tried experiments that might have caused the ADC clock to run too fast or not

    The highest frequency which I used was 30 MHz and in the data sheet it says it can be increased up to 32 MHz. Do you have such a development board in order to try out whether the order of the channel changes?

    Kind regards,

    Constantin

  • Hi Constantin,

      Your frequency is fine. 

      I have the launchPad that I can test but I need to have a simple test environment as I'm working from home. Can you please send me a CCS project that limits the number of ADC channels to 3 only and only use the CPU to read the ADC FIFO? I can manage to provide these three channels with different voltages (e.g. 0V, 1.6V, 3.3V). If you have too many channels then I can't create the inputs with my limited resources at home. Again, please keep it as simple as possible with three channels and no DMA if that is a setup you can replicate the ordering problem.

  • Hi Constantin,

      I created a small example. The purpose of the project is only to show if the data read out from the FIFO is in correct order or not. I use three channels. I only use CPU to read the ADC FIFO. I do not use uDMA because you said even with CPU, it is reading wrong order. Therefore, it is not a uDMA issue and I want to leave out the uDMA for debugging. I tried with either the PIOSC or PLL as the clock source and there is no difference in terms of the order the data is stored in the ADC FIFO.

      PE3 -> AIN0 (tied to 0V)

      PE2 -> AIN1 (tied to ~1.6V)

      PE1 -> AIN2 (tied to 3.3V)

    I see them read out in correct order. Can you please compare with your program and see any differences?

    Here is my code.

    //*****************************************************************************
    //
    // single_ended.c - Example demonstrating how to configure the ADC for
    //                  single ended operation.
    //
    // Copyright (c) 2010-2017 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    // 
    //   Redistribution and use in source and binary forms, with or without
    //   modification, are permitted provided that the following conditions
    //   are met:
    //
    //   Redistributions of source code must retain the above copyright
    //   notice, this list of conditions and the following disclaimer.
    //
    //   Redistributions in binary form must reproduce the above copyright
    //   notice, this list of conditions and the following disclaimer in the
    //   documentation and/or other materials provided with the
    //   distribution.
    //
    //   Neither the name of Texas Instruments Incorporated nor the names of
    //   its contributors may be used to endorse or promote products derived
    //   from this software without specific prior written permission.
    // 
    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    // 
    // This is part of revision 2.1.4.178 of the Tiva Firmware Development Package.
    //
    //*****************************************************************************
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "driverlib/adc.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    
    //*****************************************************************************
    //
    //! \addtogroup adc_examples_list
    //! <h1>Single Ended ADC (single_ended)</h1>
    //!
    //! This example shows how to setup ADC0 as a single ended input and take a
    //! single sample on AIN0/PE3.
    //!
    //! This example uses the following peripherals and I/O signals.  You must
    //! review these and change as needed for your own board:
    //! - ADC0 peripheral
    //! - GPIO Port E peripheral (for AIN0 pin)
    //! - AIN0 - PE3
    //!
    //! The following UART signals are configured only for displaying console
    //! messages for this example.  These are not required for operation of the
    //! ADC.
    //! - UART0 peripheral
    //! - GPIO Port A peripheral (for UART0 pins)
    //! - UART0RX - PA0
    //! - UART0TX - PA1
    //!
    //! This example uses the following interrupt handlers.  To use this example
    //! in your own application you must add these interrupt handlers to your
    //! vector table.
    //! - None.
    //
    //*****************************************************************************
    #define USER_LED1  GPIO_PIN_2
    //*****************************************************************************
    //
    // This function sets up UART0 to be used for a console to display information
    // as the example is running.
    //
    //*****************************************************************************
    void
    InitConsole(void)
    {
        //
        // Enable GPIO port A which is used for UART0 pins.
        // TODO: change this to whichever GPIO port you are using.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        //
        // Configure the pin muxing for UART0 functions on port A0 and A1.
        // This step is not necessary if your part does not support pin muxing.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
    
        //
        // Enable UART0 so that we can configure the clock.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        //
        // Use the internal 16MHz oscillator as the UART clock source.
        //
        UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    
        //
        // Select the alternate (UART) function for these pins.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Initialize the UART for console I/O.
        //
        UARTStdioConfig(0, 115200, 16000000);
    }
    
    //*****************************************************************************
    //
    // Configure ADC0 for a single-ended input and a single sample.  Once the
    // sample is ready, an interrupt flag will be set.  Using a polling method,
    // the data will be read then displayed on the console via UART0.
    //
    //*****************************************************************************
    int
    main(void)
    {
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
        uint32_t ui32SysClock;
    #endif
    
        //
        // This array is used for storing the data read from the ADC FIFO.
        //
        uint32_t pui32ADC0Value[3];
    
        //
        // Set the clocking to run at 20 MHz (200 MHz / 10) using the PLL.  When
        // using the ADC, you must either use the PLL or supply a 16 MHz clock
        // source.
        // TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
        // crystal on your board.
        //
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
        ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                                           SYSCTL_OSC_MAIN |
                                           SYSCTL_USE_PLL |
                                           SYSCTL_CFG_VCO_240), 120000000);
    
    
    #else
        SysCtlClockSet(SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);
    #endif
    
        //
        // Set up the serial console to use for displaying messages.  This is
        // just for this example program and is not needed for ADC operation.
        //
        InitConsole();
    
        //
        // Display the setup on the console.
        //
        UARTprintf("ADC ->\n");
        UARTprintf("  Type: Single Ended\n");
    
        //
        // The ADC0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    
        /*
         * ADC Clock runs at 30Mhz
         */
        //ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 8);
        ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL, 1);
    
    
        /* PE3 - AIN0
         * PE2 - AIN1
         * PE1 - AIN2
         */
    
        GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1);
    
        ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    
        ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0 );
    
        ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1 );
    
        ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_IE |
                                 ADC_CTL_END);
    
        ADCSequenceEnable(ADC0_BASE, 0);
    
        ADCIntClear(ADC0_BASE, 0);
    
        //
        // Sample AIN0/AIN1/AIN2 forever.  Display the value on the console.
        //
    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
        while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION))
        {
        }
    
        //
        // Configure the GPIO port for the LED operation.
        //
        GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, USER_LED1);
            //
    
        while(1)
        {
    
            GPIOPinWrite(GPIO_PORTN_BASE, USER_LED1, USER_LED1);
    
            //
            // Trigger the ADC conversion.
            //
    
            ADCProcessorTrigger(ADC0_BASE, 0);
    
            //
            // Wait for conversion to be completed.
            //
            while(!ADCIntStatus(ADC0_BASE, 0, false))
            {
            }
            GPIOPinWrite(GPIO_PORTN_BASE, USER_LED1, 0);
    
            //
            // Clear the ADC interrupt flag.
            //
            ADCIntClear(ADC0_BASE, 0);
    
            //
            // Read ADC Value.
            //
            ADCSequenceDataGet(ADC0_BASE, 0, pui32ADC0Value);
    
            //
            // Display the AIN0 (PE3) digital value on the console.
            //
            UARTprintf("AIN0 = %4d\n", pui32ADC0Value[0]);
            UARTprintf("AIN1 = %4d\n", pui32ADC0Value[1]);
            UARTprintf("AIN2 = %4d\n", pui32ADC0Value[2]);
    
    
            //
            // This function provides a means of generating a constant length
            // delay.  The function delay (in cycles) = 3 * parameter.  Delay
            // 250ms arbitrarily.
            //
    #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
        defined(TARGET_IS_TM4C129_RA1) ||                                         \
        defined(TARGET_IS_TM4C129_RA2)
            SysCtlDelay(ui32SysClock / 500000);
    #else
            SysCtlDelay(SysCtlClockGet() / 12);
    #endif
        }
    }
    
    

  • Hi Charles,

    you are right. If I use ADC_TRIGGER_PROCESSOR (which was the difference) the order doesn´t change. But if I change the trigger of the ADC to ADC_TRIGGER_ALWAYS or ADC_TRIGGER_TIMER, the order changes. The function  ADCSequenceDataGet() gives back the number of how many samples were copied to the buffer. As you can see it sometimes copies more than four samples to the buffer, so that the order changes if the next trigger occurs. I think that´s the problem. 

    Kind regards,

    Constantin

  • HI Constantin,

      I cannot see the images you uploaded. I think we are making some progress.

      The ADC_TRIGGER_ALWAYS means it is continuously sampling. It will be out of sync with your program flow if you are not careful. Let's say you have 5 channels to convert in the sequencer. After all five channels are converted, an interrupt is fired or a DMA request is generated to the uDMA. However, the ADC will immediately repeat the channel sampling again. By the time the DMA or the CPU read the ADC FIFO, there may be more samples converted. You might have reserved 5 buffers for the CPU to read but the FIFO actually contains more than 5 conversion data.

     Let's use an example.

      1. You are converting channel AIN0,1,2,3,4.

      2 After all five channels are converted an interrupt is generated. 

      3. By the time the CPU is ready to read the FIFO there are actually 7 channels converted and they are AIN0,1,2,3,4,0,1

      4. You issue the ADCSequenceDataGet but only the first five data are transferred to the buffer. The 6th and 7th samples are never read and lost.

      5. Since the ADC continues to sample in the background and the next channel (AIN2) conversion data will be stored in the first FIFO location. 

      6. By the time the ADC generates the next interrupt the FIFO will contain AIN2,3,4 or maybe AIN2,3,4,0,1.

      7. When the CPU transfers the data to the buffer, the buffer appears to be out of order (AIN2,3,4,0,1 instead of AIN0,1,2,3,4)

    I don't know how you use ADC_TRIGGER_TIMER. If you use the timer as a trigger then you need to make sure the timer does not trigger before you complete your FIFO read. You must have some type of synchronization in place so you do not trigger the ADC until you finish the FIFO reading. This is the key.

      I will suggest you use ADC_TRIGGER_PROCESSOR so you can control the synchronization on when to start the conversion before you finish the FIFO read. Use the ADC_TRIGGER_TIMER is OK but be very careful on the synchronization. 

  • Hi Carles,

    I can just agree. Thanks for your help.

    Kind regards,

    Constantin