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.

LP-AM243: ADC Continuous Conversion Buffering?

Part Number: LP-AM243


After looking through a lot of the examples such as udma/adc_read and adc/single_shot, I found that pretty much all of the examples listed use the ADC in single shot mode, and have a function that waits for the interrupt to occur before continuing the code. If I switch the adc to continuous mode, naturally with or without the interrupt wait message the code gets stopped after it is told to read. 

Do you think it would be possible to have one of the 4 device cores running a continuous ADC to save data to a buffer of sorts, so that a different core can then access that memory at any given time and read the whole buffer? In the linker you can specify memory origin and size so perhaps if I make 2 cores share that memory?

- Thank you for your help.

  • Hi Dylan,

    Yes, it can be done easily. Change the following line in App_adcConfig() of adc_singleshot.c

    adcConfig.mode             = ADC_OPERATION_MODE_SINGLE_SHOT; --> ADC_OPERATION_MODE_CONTINUOUS

    Best regards,

    Ming

  • Doing that causes the output to display:
    '

    and the code gets stuck from there. The next lines of code it isnt getting to is:
    DebugP_log("Step ID Voltage Level\r\n");
    DebugP_log("------- -------------\r\n");

    and the loop displaying it. Not sure why it gets hung up, possibly from the ISR.

  • Hi Dylan,

    What "Debug Log" you are using? "CCS Log" even the "UART Log" may be too slow for continuous  sampling mode. can you save the samples in memory and print out them later?

    Best regards,

    Ming 

  • Did a test to read X elements from the ISR before stopping the ADC so the DebugP_log (CCS Log / UART) messages are printed. It reached the ISR an amount of times, but the FIFO remained at 0

    Ok so I modified it as such:

        /* Initialize, Configure and Start the ADC module */
        App_adcInit(baseAddr);
        App_adcConfig(baseAddr);
        App_adcStart(baseAddr);
    
        /* Wait for the interrupt to occur */
        SemaphoreP_pend(&gAdcSyncSemObject, SystemP_WAIT_FOREVER);
    
        /* Stop and power down the ADC*/
        App_adcStop(baseAddr);
        App_adcDeInit(baseAddr);
        HwiP_destruct(&gAdcHwiObject);
        SemaphoreP_destruct(&gAdcSyncSemObject);
    
        DebugP_log("Done Reading %i\r\n", reads);


    where reads is an int counted in the ISR like so:

    void App_adcISR(void *handle)
    {
        uint32_t status;
        uint32_t baseAddr = CONFIG_ADC0_BASE_ADDR;
        reads++;
        if(reads > 20) {
            /* Get interrupt status and clear */
            status = ADCGetIntrStatus(baseAddr);
            ADCClearIntrStatus(baseAddr, status);
            /* Process ISR */
            SemaphoreP_post(&gAdcSyncSemObject);
        }
    
        /* Set EOI to generate next interrupt if any */
        ADCWriteEOI(baseAddr);
    }

  • Hi Dylan,

    Try the attached file. It works for me.

    Best regards,

    Ming

    /*
     *  Copyright (C) 2021 Texas Instruments Incorporated
     *
     *  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.
     */
    
    #include <kernel/dpl/DebugP.h>
    #include <kernel/dpl/SemaphoreP.h>
    #include <kernel/dpl/HwiP.h>
    #include <kernel/dpl/ClockP.h>
    #include <drivers/adc.h>
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    
    /*
     * This example shows ADC conversion for all available input channels.
     *
     * ADC has eight input channels and all of them are used in this example.
     *
     * All channels are converted and their results are stored in FIFO 0,
     * upon the end of all conversions, the FIFO data is printed to the console.
     *
     * This example configures ADC for single shot conversion, that means
     * a single channel is converted only once.
     *
     * For this example the ADC does averaging of 16 samples to get the
     * result for each conversion.
     *
     * This example also shows how to use interrupts with ADC module
     * to get the required functionality.
     */
    
    /* Reference voltage for ADC - should be given in mV */
    #define APP_ADC_REF_VOLTAGE         (1800U)
    
    /* Number of channels being converted */
    #define APP_ADC_NUM_CH              (8U)
    
    /* Global variables and objects */
    static HwiP_Object gAdcHwiObject;
    static SemaphoreP_Object gAdcSyncSemObject;
    
    /* Function prototypes */
    static void App_adcISR(void *handle);
    static void App_adcInit(uint32_t baseAddr);
    static void App_adcConfig(uint32_t baseAddr);
    static void App_adcStart(uint32_t baseAddr);
    static void App_adcStop(uint32_t baseAddr);
    static void App_adcDeInit(uint32_t baseAddr);
    
    #define MAXADCSAMPLELOGSIZE 100
    int32_t adcSampleLog[MAXADCSAMPLELOGSIZE];
    int32_t adcSampleLogIndex = 0;
    void adc_singleshot_main(void *args)
    {
        uint32_t baseAddr = CONFIG_ADC0_BASE_ADDR;
        ///HwiP_Params hwiPrms;
        uint32_t loopcnt, fifoData, fifoWordCnt, stepID, voltageLvl;
        int32_t status;
    
        /* Open drivers to open the UART driver for console */
        Drivers_open();
        Board_driversOpen();
    
        DebugP_log("ADC Single Shot Test Started ...\r\n");
    
        status = SemaphoreP_constructBinary(&gAdcSyncSemObject, 0);
        DebugP_assert(SystemP_SUCCESS == status);
    
        /* Register & enable interrupt */
        ///HwiP_Params_init(&hwiPrms);
        ///hwiPrms.intNum = CONFIG_ADC0_INTR;
        ///hwiPrms.callback = &App_adcISR;
        ///hwiPrms.priority = 1U;
        ///status = HwiP_construct(&gAdcHwiObject, &hwiPrms);
        ///DebugP_assert(SystemP_SUCCESS == status);
    
        /* Initialize, Configure and Start the ADC module */
        App_adcInit(baseAddr);
        App_adcConfig(baseAddr);
        App_adcStart(baseAddr);
    
        /* Wait for the interrupt to occur */
        ///SemaphoreP_pend(&gAdcSyncSemObject, SystemP_WAIT_FOREVER);
    
        while (1)
        {
            /* Get FIFO data */
            fifoWordCnt = ADCGetFIFOWordCount(baseAddr, ADC_FIFO_NUM_0);
            if (fifoWordCnt>0)
            {
                if ((adcSampleLogIndex+fifoWordCnt)>MAXADCSAMPLELOGSIZE)
                    break;
                ///DebugP_log("Number of Samples in FIFO : %d\r\n", fifoWordCnt);
                ///DebugP_log("Step ID     Voltage Level\r\n");
                ///DebugP_log("-------     -------------\r\n");
                for (loopcnt = 0U; loopcnt < fifoWordCnt; loopcnt++)
                {
                    fifoData = ADCGetFIFOData(baseAddr, ADC_FIFO_NUM_0);
                    stepID   = ((fifoData & ADC_FIFODATA_ADCCHNLID_MASK) >>
                                ADC_FIFODATA_ADCCHNLID_SHIFT);
                    fifoData = ((fifoData & ADC_FIFODATA_ADCDATA_MASK) >>
                                ADC_FIFODATA_ADCDATA_SHIFT);
                    voltageLvl  = fifoData * (uint32_t) APP_ADC_REF_VOLTAGE;
                    voltageLvl /= (uint32_t) ADC_GET_RANGE(CONFIG_ADC0_NUM_BITS);
                    adcSampleLog[adcSampleLogIndex] = voltageLvl;
                    adcSampleLogIndex++;
                    ///DebugP_log("%d           %d mV\r\n", (uint32_t)(stepID + 1U), (uint32_t)voltageLvl);
                }
            }
        }
        /* Stop and power down the ADC*/
        App_adcStop(baseAddr);
        App_adcDeInit(baseAddr);
    
        HwiP_destruct(&gAdcHwiObject);
        SemaphoreP_destruct(&gAdcSyncSemObject);
    
        DebugP_log("ADC Single Shot Test Completed!!\r\n");
        DebugP_log("%d ADC Samples Have Been Collected ...\r\n", adcSampleLogIndex);
        DebugP_log("All tests have passed!!\r\n");
    
        Board_driversClose();
        Drivers_close();
    }
    
    void App_adcISR(void *handle)
    {
        uint32_t status;
        uint32_t baseAddr = CONFIG_ADC0_BASE_ADDR;
    
        /* Get interrupt status and clear */
        status = ADCGetIntrStatus(baseAddr);
        ADCClearIntrStatus(baseAddr, status);
    
        /* Process ISR */
        SemaphoreP_post(&gAdcSyncSemObject);
    
        /* Set EOI to generate next interrupt if any */
        ADCWriteEOI(baseAddr);
    }
    
    void App_adcConfig(uint32_t baseAddr)
    {
        int32_t         configStatus;
        uint32_t        chCnt, adcStep;
        adcStepConfig_t adcConfig;
    
        /* Enable interrupts */
        ADCEnableIntr(baseAddr, (ADC_INTR_SRC_END_OF_SEQUENCE |
                                 ADC_INTR_SRC_FIFO0_THRESHOLD |
                                 ADC_INTR_SRC_FIFO0_OVERRUN |
                                 ADC_INTR_SRC_FIFO0_UNDERFLOW |
                                 ADC_INTR_SRC_FIFO1_THRESHOLD |
                                 ADC_INTR_SRC_FIFO1_OVERRUN |
                                 ADC_INTR_SRC_FIFO1_UNDERFLOW |
                                 ADC_INTR_SRC_OUT_OF_RANGE));
    
        /*
         * Configure all ADC Steps
         */
        /* Initialize ADC configuration params */
        adcConfig.mode             = ADC_OPERATION_MODE_CONTINUOUS; ///ADC_OPERATION_MODE_SINGLE_SHOT;
        adcConfig.openDelay        = 0x1U;
        adcConfig.sampleDelay      = 0U;
        adcConfig.rangeCheckEnable = 0U;
        adcConfig.averaging        = ADC_AVERAGING_16_SAMPLES;
        adcConfig.fifoNum          = ADC_FIFO_NUM_0;
    
        /* Configure all required steps - Step 1 to N mapped to Channel 1 to N */
        for(chCnt = 0U; chCnt < APP_ADC_NUM_CH; chCnt++)
        {
            adcConfig.channel = ADC_CHANNEL_1 + chCnt;
            adcStep = ADC_STEP_1 + chCnt;   /* Step -> Channel one to one mapped */
            configStatus = ADCSetStepParams(baseAddr, adcStep, &adcConfig);
            DebugP_assert(SystemP_SUCCESS == configStatus);
        }
    
        ADCStepIdTagEnable(baseAddr, TRUE);
        configStatus = ADCSetCPUFIFOThresholdLevel(baseAddr, ADC_FIFO_NUM_0, 40U);
        DebugP_assert(SystemP_SUCCESS == configStatus);
    
        /* Step enable */
        for(chCnt = 0U; chCnt < APP_ADC_NUM_CH; chCnt++)
        {
            adcStep = ADC_STEP_1 + chCnt;   /* Step -> Channel one to one mapped */
            ADCStepEnable(baseAddr, adcStep, TRUE);
        }
    }
    
    static void App_adcInit(uint32_t baseAddr)
    {
        /* Clear All interrupt status */
        ADCClearIntrStatus(baseAddr, ADC_INTR_STATUS_ALL);
    
        /* Power up AFE */
        ADCPowerUp(baseAddr, TRUE);
    
        /* Wait for 4us at least */
        ClockP_usleep(5U);
    
        /* Do the internal calibration */
        ADCInit(baseAddr, FALSE, 0U, 0U);
    }
    
    static void App_adcStart(uint32_t baseAddr)
    {
        adcSequencerStatus_t status;
    
        /* Check if FSM is idle */
        ADCGetSequencerStatus(baseAddr, &status);
        while ((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE != status.stepId)
        {
            ADCGetSequencerStatus(baseAddr, &status);
        }
        /* Start ADC conversion */
        ADCStart(baseAddr, TRUE);
    }
    
    static void App_adcStop(uint32_t baseAddr)
    {
        uint32_t                chCnt, adcStep;
        adcSequencerStatus_t    status;
    
        /* Disable all/enabled steps */
        for(chCnt = 0U; chCnt < APP_ADC_NUM_CH; chCnt++)
        {
            adcStep = ADC_STEP_1 + chCnt;   /* Step -> Channel one to one mapped */
            ADCStepEnable(baseAddr, adcStep, FALSE);
        }
    
        /* Wait for FSM to go IDLE */
        ADCGetSequencerStatus(baseAddr, &status);
        while((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE  != status.stepId)
        {
            ADCGetSequencerStatus(baseAddr, &status);
        }
    
        /* Stop ADC */
        ADCStart(baseAddr, FALSE);
    
        /* Wait for FSM to go IDLE */
        ADCGetSequencerStatus(baseAddr, &status);
        while ((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
                ADC_ADCSTAT_STEP_ID_IDLE  != status.stepId)
        {
            ADCGetSequencerStatus(baseAddr, &status);
        }
    }
    
    static void App_adcDeInit(uint32_t baseAddr)
    {
        ADCPowerUp(baseAddr, FALSE);
    }
    

  • Thanks, that does work for me too, so how would you recommend I access this data on a different core, because from this example, in order to truly continuously sample the ADC the while loop needs to be maintained indefinitely, so a different core would need to read the data asynchronously. Should I make the SRAM overlap and try to specify an exact point in the SRAM memory or is there a protocol for passing buffers from one core to the next?

  • Hi Dylan,

    This polling example just to prove the ADC continuous mode is working. The continuous mode should work for DMA or interrupt mode. By using the DMA or the interrupt mode, you do not need a another core for polling.

    If you decided to use the interrupt mode, then your ISR has to handle the the various interrupt events. Only sends the semaphore when it is ADC_INTR_SRC_END_OF_SEQUENCE.

    Best regards,

    Ming

  • So basically I can use the DMA example with continuous mode, and since it has direct memory access I should not need to trigger ISR's or pauses in my code? I will try to implement this.

  • In udma adcread test, if I apply continuous transfer mode, it works for one run of the code but if I run it again it gives me "ASSERT: 0.53114s: ../udma_adc_read.c:App_create:275: UDMA_SOK == retVal failed !!!" I believe this could have to do with the buffer overflowing from continuous mode? Is there some additional settings to make the UDMA have a buffer that is. Here is where I am at so far which generates that error:

    It most consistently has this retVal error for the dequeue function "retVal = Udma_ringDequeueRaw(
    Udma_chGetCqRingHandle(rxChHandle), &pDesc);"

    There is probably a simple setting which makes the ring operate correctly but I am unfamiliar with the UDMA ring library.

    /*
     *  Copyright (C) 2021 Texas Instruments Incorporated
     *
     *  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 example performs PDMA RX data capture from ADC.
     *
     * ADC is configured in single shot mode and captures APP_ADC_NUM_CH channel
     * of ADC data. The FIFO is configured to generate a DMA trigger after all
     * channel data is captured.
     *
     * The application opens and configures a Packet DMA (PKTDMA) channel.
     * It configures the PDMA parameter for transfer from ADC. The PDMA element count
     * is set to the number of ADC samples - APP_ADC_NUM_CH.
     *
     * This uses Host Packet Descriptor (HPD) to receive data from ADC PDMA channel
     * into the destination buffer.
     *
     * The ADC is configured to tag the channel/step ID as part of ADC data using \ref ADCStepIdTagEnable API.
     * The application uses this to compare that the DMA read data is in proper
     * sequence and prints pass/fail accordingly.
     */
    
    #include <kernel/dpl/CacheP.h>
    #include <kernel/dpl/DebugP.h>
    #include <kernel/dpl/SemaphoreP.h>
    #include <drivers/adc.h>
    #include "ti_drivers_config.h"
    #include "ti_drivers_open_close.h"
    #include "ti_board_open_close.h"
    
    /*
     * Application test parameters
     */
    #define APP_ADC_FIFO                    (ADC_FIFO_NUM_0)
    #define APP_ADC_MODULE                  (CSL_ADC0_BASE)
    #define APP_ADC_RX_PDMA_CH              (UDMA_PDMA_CH_MAIN1_ADC0_CH0_RX)
    #define APP_ADC_NUM_CH                  (8U)
    
    /* Reference voltage for ADC - should be given in mV */
    #define APP_ADC_REF_VOLTAGE             (1800U)
    
    /*
     * Number of bytes transmitted by PDMA per RX event sent by ADC.
     * In ADC, this should be equal to the DMA trigger.
     * Testing single shot mode - so should be same as number of channels being
     * captured
     */
    #define RX_BYTES_PER_EVENT              (APP_ADC_NUM_CH)
    
    /** \brief Number of times to perform the ADC operation */
    #define UDMA_TEST_LOOP_CNT              (1U)
    
    /*
     * Ring parameters
     */
    /** \brief Number of ring entries - we can prime this much ADC operations */
    #define UDMA_TEST_RING_ENTRIES          (1U)
    /** \brief Size (in bytes) of each ring entry (Size of pointer - 64-bit) */
    #define UDMA_TEST_RING_ENTRY_SIZE       (sizeof(uint64_t))
    /** \brief Total ring memory */
    #define UDMA_TEST_RING_MEM_SIZE         (UDMA_TEST_RING_ENTRIES * UDMA_TEST_RING_ENTRY_SIZE)
    /** \brief UDMA host mode buffer descriptor memory size. */
    #define UDMA_TEST_DESC_SIZE             (sizeof(CSL_UdmapCppi5HMPD))
    
    static void App_adcTest(Udma_ChHandle rxChHandle);
    static void App_udmaAdcRx(Udma_ChHandle rxChHandle, uint32_t *destBuf);
    
    static void App_udmaEventDmaCb(Udma_EventHandle eventHandle,
                                   uint32_t eventType,
                                   void *appData);
    static void App_create(Udma_DrvHandle drvHandle, Udma_ChHandle rxChHandle);
    static void App_delete(Udma_DrvHandle drvHandle, Udma_ChHandle rxChHandle);
    
    static void App_udmaRxHpdInit(Udma_ChHandle rxChHandle,
                                  uint8_t *pHpdMem,
                                  const uint32_t *destBuf,
                                  uint32_t length);
    
    static void App_adcInit(void);
    static void App_adcConfig(void);
    static void App_adcStart(void);
    static void App_adcStop(void);
    static void App_adcDeInit(void);
    
    /*
     * UDMA driver objects
     */
    Udma_ChObject       gUdmaRxChObj;
    Udma_EventObject    gUdmaCqEventObj;
    
    /*
     * UDMA Memories
     */
    static uint8_t gRxFqRingMem[UDMA_ALIGN_SIZE(UDMA_TEST_RING_MEM_SIZE)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    static uint8_t gUdmaRxHpdMem[UDMA_ALIGN_SIZE(UDMA_TEST_DESC_SIZE)] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    
    /*
     * Application Buffers
     */
    uint32_t gAdcDestBuf[APP_ADC_NUM_CH] __attribute__((aligned(UDMA_CACHELINE_ALIGNMENT)));
    
    /* Semaphore to indicate transfer completion */
    static SemaphoreP_Object gUdmaTestDoneSem;
    
    /* Variables for Continuous Sampling */
    #define MAXADCSAMPLELOGSIZE 64
    int32_t adcSampleLog[MAXADCSAMPLELOGSIZE];
    int32_t adcSampleLogIndex = 0;
    
    void *udma_adc_read_main(void *args)
    {
        Udma_DrvHandle  drvHandle = &gUdmaDrvObj[CONFIG_UDMA0];
        Udma_ChHandle   rxChHandle = &gUdmaRxChObj;
    
        /* Open drivers to open the UART driver for console */
        Drivers_open();
        Board_driversOpen();
        App_adcInit();
        DebugP_log("[UDMA] ADC read application started ...\r\n");
    
        App_create(drvHandle, rxChHandle);
    
        App_adcTest(rxChHandle);
    
        App_delete(drvHandle, rxChHandle);
    
        DebugP_log("All tests have passed!!\r\n");
        Board_driversClose();
        App_adcDeInit();
        Drivers_close();
    
        return NULL;
    }
    
    static void App_adcTest(Udma_ChHandle rxChHandle)
    {
        uint32_t    loopCnt = 0U;
        uint32_t   *destBuf = &gAdcDestBuf[0U];
        uint32_t    i;
    
        /* Init buffers */
        for(i = 0U; i < APP_ADC_NUM_CH; i++)
        {
            destBuf[i] = 0U;
        }
        /* Writeback buffer */
        CacheP_wb(&gAdcDestBuf[0U], sizeof(gAdcDestBuf), CacheP_TYPE_ALLD);
    
        while(loopCnt < UDMA_TEST_LOOP_CNT)
        {
            /* Perform UDMA ADC RX */
            App_udmaAdcRx(rxChHandle, destBuf);
            loopCnt++;
            DebugP_log("Loop Count: %d completed!!\n\r\n", loopCnt);
        }
    
        for(int i = 0; i < adcSampleLogIndex; i++)
        {
            DebugP_log("%d           %d mV\r\n", i, (uint32_t)adcSampleLog[i]);
        }
    
        return;
    }
    
    static void App_udmaAdcRx(Udma_ChHandle rxChHandle, uint32_t *destBuf)
    {
        int32_t         retVal;
        uint32_t        loopCnt, fifoData, adcData, stepId, chCnt, voltageLvl;
        uint64_t        pDesc = 0;
        uint8_t        *pHpdMem = &gUdmaRxHpdMem[0U];
    
        App_adcConfig();
    
        /* Update host packet descriptor */
        App_udmaRxHpdInit(rxChHandle, pHpdMem, destBuf, APP_ADC_NUM_CH * 4U);
    
        /* Submit HPD to channel */
        retVal = Udma_ringQueueRaw(
                     Udma_chGetFqRingHandle(rxChHandle),
                     (uint64_t) Udma_defaultVirtToPhyFxn(pHpdMem, 0U, NULL));
        DebugP_assert(UDMA_SOK == retVal);
    
        App_adcStart();
    
        /* Wait for return descriptor in completion ring - this marks the
         * transfer completion */
        //SemaphoreP_pend(&gUdmaTestDoneSem, SystemP_WAIT_FOREVER);
    
        /* Response received in completion queue */
        retVal = Udma_ringDequeueRaw(
                     Udma_chGetCqRingHandle(rxChHandle), &pDesc);
        DebugP_assert(UDMA_SOK == retVal);
    
        /* Invalidate cache */
        CacheP_inv(pHpdMem, UDMA_TEST_DESC_SIZE, CacheP_TYPE_ALLD);
        CacheP_inv(&gAdcDestBuf[0U], sizeof(gAdcDestBuf), CacheP_TYPE_ALLD);
    
            for (loopCnt = 0U; loopCnt < APP_ADC_NUM_CH; loopCnt++)
            {
                chCnt = loopCnt % APP_ADC_NUM_CH;
                fifoData = destBuf[loopCnt];
                stepId   = ((fifoData & ADC_FIFODATA_ADCCHNLID_MASK) >>
                        ADC_FIFODATA_ADCCHNLID_SHIFT);
                adcData = ((fifoData & ADC_FIFODATA_ADCDATA_MASK) >>
                        ADC_FIFODATA_ADCDATA_SHIFT);
                if(stepId != chCnt)     /* Both channel and step are 1:1 mapped */
                {
                    DebugP_logError("Step ID Error: %d\r\n", stepId);
                    DebugP_assert(FALSE);
                }
                voltageLvl  = adcData * (uint32_t) APP_ADC_REF_VOLTAGE;
                voltageLvl /= (uint32_t) ADC_GET_RANGE(CONFIG_ADC0_NUM_BITS);
                adcSampleLog[adcSampleLogIndex] = voltageLvl;
                adcSampleLogIndex++;
                //DebugP_log("CH %d ", chCnt);
                //DebugP_log("ADC Voltage: %d mV\r\n", voltageLvl);
            }
    
        App_adcStop();
    
        return;
    }
    
    static void App_udmaEventDmaCb(Udma_EventHandle eventHandle,
                                   uint32_t eventType,
                                   void *appData)
    {
        if(UDMA_EVENT_TYPE_DMA_COMPLETION == eventType)
        {
            SemaphoreP_post(&gUdmaTestDoneSem);
        }
    
        return;
    }
    
    static void App_create(Udma_DrvHandle drvHandle, Udma_ChHandle rxChHandle)
    {
        int32_t             retVal;
        uint32_t            chType;
        Udma_ChPrms         chPrms;
        Udma_ChRxPrms       rxPrms;
        Udma_EventHandle    eventHandle;
        Udma_EventPrms      eventPrms;
        Udma_ChPdmaPrms     pdmaPrms;
    
        retVal = SemaphoreP_constructBinary(&gUdmaTestDoneSem, 0);
        DebugP_assert(SystemP_SUCCESS == retVal);
    
        /* Init channel parameters */
        chType = UDMA_CH_TYPE_PDMA_RX;
        UdmaChPrms_init(&chPrms, chType);
        chPrms.peerChNum            = APP_ADC_RX_PDMA_CH;
        chPrms.fqRingPrms.ringMem   = &gRxFqRingMem[0U];
        chPrms.fqRingPrms.ringMemSize   = UDMA_TEST_RING_MEM_SIZE;
        chPrms.fqRingPrms.elemCnt   = UDMA_TEST_RING_ENTRIES;
    
        /* Open channel for block copy */
        retVal = Udma_chOpen(drvHandle, rxChHandle, chType, &chPrms);
        DebugP_assert(UDMA_SOK == retVal);
    
        /* Config RX channel */
        UdmaChRxPrms_init(&rxPrms, UDMA_CH_TYPE_PDMA_RX);
        retVal = Udma_chConfigRx(rxChHandle, &rxPrms);
        DebugP_assert(UDMA_SOK == retVal);
    
        /* Register ring completion callback */
        eventHandle = &gUdmaCqEventObj;
        UdmaEventPrms_init(&eventPrms);
        eventPrms.eventType         = UDMA_EVENT_TYPE_DMA_COMPLETION;
        eventPrms.eventMode         = UDMA_EVENT_MODE_SHARED;
        eventPrms.chHandle          = rxChHandle;
        eventPrms.masterEventHandle = Udma_eventGetGlobalHandle(drvHandle);
        eventPrms.eventCb           = &App_udmaEventDmaCb;
        retVal = Udma_eventRegister(drvHandle, eventHandle, &eventPrms);
        DebugP_assert(UDMA_SOK == retVal);
    
        /* Config PDMA channel */
        UdmaChPdmaPrms_init(&pdmaPrms);
        pdmaPrms.elemSize   = UDMA_PDMA_ES_32BITS;
        pdmaPrms.elemCnt    = RX_BYTES_PER_EVENT;
        pdmaPrms.fifoCnt    = (APP_ADC_NUM_CH / RX_BYTES_PER_EVENT);
        retVal = Udma_chConfigPdma(rxChHandle, &pdmaPrms);
        DebugP_assert(UDMA_SOK == retVal);
    
        retVal = Udma_chEnable(rxChHandle);
        DebugP_assert(UDMA_SOK == retVal);
    
        return;
    }
    
    static void App_delete(Udma_DrvHandle drvHandle, Udma_ChHandle rxChHandle)
    {
        int32_t             retVal;
        Udma_EventHandle    eventHandle;
    
        retVal = Udma_chDisable(rxChHandle, UDMA_DEFAULT_CH_DISABLE_TIMEOUT);
        DebugP_assert(UDMA_SOK == retVal);
    
        /* Unregister all events */
        eventHandle = &gUdmaCqEventObj;
        retVal = Udma_eventUnRegister(eventHandle);
        DebugP_assert(UDMA_SOK == retVal);
    
        retVal = Udma_chClose(rxChHandle);
        DebugP_assert(UDMA_SOK == retVal);
    
        SemaphoreP_destruct(&gUdmaTestDoneSem);
    
        return;
    }
    
    static void App_udmaRxHpdInit(Udma_ChHandle rxChHandle,
                                  uint8_t *pHpdMem,
                                  const uint32_t *destBuf,
                                  uint32_t length)
    {
        CSL_UdmapCppi5HMPD *pHpd = (CSL_UdmapCppi5HMPD *) pHpdMem;
        uint32_t descType = (uint32_t)CSL_UDMAP_CPPI5_PD_DESCINFO_DTYPE_VAL_HOST;
        uint32_t cqRingNum = Udma_chGetCqRingNum(rxChHandle);
    
        /* Setup descriptor */
        CSL_udmapCppi5SetDescType(pHpd, descType);
        CSL_udmapCppi5SetEpiDataPresent(pHpd, FALSE);
        CSL_udmapCppi5SetPsDataLoc(pHpd, 0U);
        CSL_udmapCppi5SetPsDataLen(pHpd, 0U);
        CSL_udmapCppi5SetPktLen(pHpd, descType, length);
        CSL_udmapCppi5SetPsFlags(pHpd, 0U);
        CSL_udmapCppi5SetIds(pHpd, descType, 0x321, UDMA_DEFAULT_FLOW_ID);
        CSL_udmapCppi5SetSrcTag(pHpd, 0x0000);     /* Not used */
        CSL_udmapCppi5SetDstTag(pHpd, 0x0000);     /* Not used */
        CSL_udmapCppi5SetReturnPolicy(
            pHpd,
            descType,
            CSL_UDMAP_CPPI5_PD_PKTINFO2_RETPOLICY_VAL_ENTIRE_PKT,
            CSL_UDMAP_CPPI5_PD_PKTINFO2_EARLYRET_VAL_NO,
            CSL_UDMAP_CPPI5_PD_PKTINFO2_RETPUSHPOLICY_VAL_TO_TAIL,
            cqRingNum);
        CSL_udmapCppi5LinkDesc(pHpd, 0U);
        CSL_udmapCppi5SetBufferAddr(pHpd, (uint64_t) Udma_defaultVirtToPhyFxn(destBuf, 0U, NULL));
        CSL_udmapCppi5SetBufferLen(pHpd, length);
        CSL_udmapCppi5SetOrgBufferAddr(pHpd, (uint64_t) Udma_defaultVirtToPhyFxn(destBuf, 0U, NULL));
        CSL_udmapCppi5SetOrgBufferLen(pHpd, length);
    
        /* Writeback cache */
        CacheP_wb(pHpdMem, UDMA_TEST_DESC_SIZE, CacheP_TYPE_ALLD);
    
        return;
    }
    
    static void App_adcInit(void)
    {
        /* Clear All interrupt status */
        ADCClearIntrStatus(APP_ADC_MODULE, ADC_INTR_STATUS_ALL);
    
        /* Power up AFE */
        ADCPowerUp(APP_ADC_MODULE, TRUE);
        /* Wait for 4us at least */
        ClockP_usleep(10);
    
        /* Do the internal calibration */
        ADCInit(APP_ADC_MODULE, FALSE, 0U, 0U);
    
        return;
    }
    
    static void App_adcConfig(void)
    {
        uint32_t        chCnt;
        adcStepConfig_t adcConfig;
    
        /* Initialize ADC configuration params */
        //adcConfig.mode             = ADC_OPERATION_MODE_SINGLE_SHOT;
        adcConfig.mode             = ADC_OPERATION_MODE_CONTINUOUS;
        adcConfig.openDelay        = 0x1U;
        adcConfig.sampleDelay      = 0U;
        adcConfig.rangeCheckEnable = 0U;
        adcConfig.averaging        = ADC_AVERAGING_16_SAMPLES;
        adcConfig.fifoNum          = APP_ADC_FIFO;
        for(chCnt = 0U; chCnt < APP_ADC_NUM_CH; chCnt++)
        {
            /* Step configuration */
            adcConfig.channel = ADC_CHANNEL_1 + chCnt;
            ADCSetStepParams(APP_ADC_MODULE, ADC_STEP_1 + chCnt, &adcConfig);
            /* step enable */
            ADCStepEnable(APP_ADC_MODULE, ADC_STEP_1 + chCnt, TRUE);
        }
    
        ADCStepIdTagEnable(APP_ADC_MODULE, TRUE);
        ADCSetDMAFIFOThresholdLevel(APP_ADC_MODULE, APP_ADC_FIFO, APP_ADC_NUM_CH);
    
        return;
    }
    
    static void App_adcStart(void)
    {
        adcSequencerStatus_t status;
    
        /* Enable DMA */
        ADCFIFODMAAccessEnable(APP_ADC_MODULE, APP_ADC_FIFO, TRUE);
    
        /* Check if FSM is idle */
        ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        while((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE != status.stepId)
        {
            ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        }
    
        /* Start ADC conversion */
        ADCStart(APP_ADC_MODULE, TRUE);
    
        return;
    }
    
    static void App_adcStop(void)
    {
        uint32_t                chCnt;
        adcSequencerStatus_t    status;
    
        /* Disable DMA */
        ADCFIFODMAAccessEnable(APP_ADC_MODULE, APP_ADC_FIFO, FALSE);
    
        /* Disable all/enabled steps */
        for(chCnt = 0U; chCnt < APP_ADC_NUM_CH; chCnt++)
        {
            ADCStepEnable(APP_ADC_MODULE, ADC_STEP_1 + chCnt, FALSE);
        }
    
        /* Wait for FSM to go IDLE */
        ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        while((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE != status.stepId)
        {
            ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        }
    
        /* Stop ADC */
        ADCStart(APP_ADC_MODULE, FALSE);
    
        /* Wait for FSM to go IDLE */
        ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        while((ADC_ADCSTAT_FSM_BUSY_IDLE != status.fsmBusy) &&
               ADC_ADCSTAT_STEP_ID_IDLE != status.stepId)
        {
            ADCGetSequencerStatus(APP_ADC_MODULE, &status);
        }
    
        return;
    }
    
    static void App_adcDeInit(void)
    {
        /* Power down ADC */
        ADCPowerUp(APP_ADC_MODULE, FALSE);
    
        return;
    }
    

  • Hi Dylan,

    The ADC DMA example is using the DMA to read data from 8 channels of ADC, so it set to single shot mode. It will trigger the ADC and DMA multiple times, but each time only does single shot ADC sampling for 8 channels. The DMA was not set for continuous ADC sampling. In order to use DMA for continuous ADC sampling, I think the DMA with Ping-Pong buffer is needed. I do not see such example available in MCU+ SDK. Let me check with the software development team for solution and get back to you early next week.

    Best regards,

    Ming

  • I can see how the ping pong buffer would be useful so you can read from one and fill another, but in my case I would like to be able to read from one almost asynchronously and ensure a certain amount of data is there. My full situation is I have a PPS signal from a GPS that ticks in seconds. At the start of a tick, I want to collect the previous second of data from the ADC. So ideally it would always store >1second of data which I can grab at some point. I could probably work this out with a ping pong buffer, especially if I can make more than 1 ping pong buffer container? (ie) 3 buffers it swaps between and fills and they are all equal to roughly 1 second of data so at any given point at least one of the buffers is completely full, and I can send that when the PPS signal ticks.

    Thanks for asking the team for me!

    -Dylan

  • Hi Dylan,

    Yes, you may need more than two DMA RX buffers for this case. Will get back to you as soon as I get replies back

  • Actually just 2 buffers is fine, as long as it can call an ISR once the ping pong buffer is filled.

  • Hi Dylan,

    Make sense. I am still waiting for the response from software development team.

  • Hi Dylan,

    SInce you have started a new thraed for the same topic: (+) LP-AM243: Ping-Pong DMA Buffer for ADC Continuous Reading - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums. I will close this one.

    Best regards,

    Ming