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.

CC2652R7: Problem with configuration of ADC sampling frequency

Part Number: CC2652R7

I am seeking assistance with an issue related to the CC2652R7 device. I have configured the ADC sampling frequency to be 32768Hz, but upon retrieving the samples I've noticed that that frequency is approximately 1% higher than the configured one. Any insights on what might be causing this ?

  • Hello Danae Tsaousi,

    I hope you are doing well. Could you please provide your SDK version, CCS version, and if applicable what example you based your code off of? Is your board a custom board, or a development board? What is your sampling duration? Could you also provide a screenshot displaying this 1 % discrepancy. 

    Thanks,
    Alex F

  • Hello Alex,

    Thank you for the quick reply.

    • CCS version: [12.1.0]
    • SDK version: SimpleLink CC13xx CC26xx SDK [6.29.0.29]

    It's about a custom board and sampling duration is equal to ADCBufCC26X2_SAMPLING_DURATION_2P7_US.My code is based on the example for adcBufContinuous provided by ti.

    The input i gave was a sine 256Hz.After I applied fft on the retrieved data.I plotted the result to observe the highest peek which corresponds to the sampling frequency and is around 254Hz. 

  • Hello Danae Tsaousi,

    Did you make any changes to the adcBufContinuous example (like the config)? For the sampling duration it seems fine considering what we are measuring. So for my understanding you set your frequency to 32768 Hz, measuring 254 wavelengths (from the sine wave). One reason we might see an inconsistency here is that the sine wave and/or the CC2652R7 system clock may not be perfectly exact and may cause the error. Did you perhaps have the adc buffer with all the received samples in debug mode? Can you elaborate on your algorithm for your calculations? 

    Thanks,
    Alex 

  • Hello Alex,

    Thank you for the reply. Your understanding is right about the procedure.I have some samples but first I would like to update you for a simpler version of code.

    I run also [adcbufcontinuous_LP_CC2652R7_tirtos_ticlang] example project on the CC2652R7 evaluation kit.

    The changes I've made are described below:

    • ADCSAMPLESIZE and adcBufParams.samplingFrequency set to 1024.
    • When adcBufCallback is being called I post an event for the main thread and main thread is pending for an event from callback as well.

    After running this on debug mode and printing the current timestamp in seconds every time adcBufCallback was posting an event, I was expecting that after each event post timestamp will get increased by one, as both size of adc buffer and sampling frequency are equal to 1024. However, there were cases that the timestamp was the same for two consecutive adcBufCallbacks.(e.g on timestamps : 128, 254 sec). So I'm still encountering the same problem even with the evaluation kit.

    Regarding the testing procedure on the custom board, I used a precision waveform generator to produce a sinewave at 256Hz and stored all the data to an external flash memory. After retrieving the samples and applied fft for the sampling frequency 32768Hz, the highest peek was located around 254Hz.

    Thanks in advance,

    Danae.

  • Hello Danae, 

    As a test in the SKD example of adcbufcontinuous, I created a global variable, and incremented its count in the adcbufcallback, normally we would expect this global variable to match with the buffer count (the items displayed in putty terminal as an output). At a lower frequency (100 Hz) I was able to closely match the global variable with the buffer count. However, as the frequency increases RTOS latency starts becoming a problem, and the loop is simply not able to service all the callbacks, in response it's likely that mq_receive is receiving too much data from mq_send resulting in the discrepancy.

    Since in your case you are using a "fast" signal, we would recommend bypassing the loop for any data processing. Try to use only the callback and load the data in some global array, and then after that array is full the callback can use mq_send back to the application loop to process the data. We are trying to make sure that the data gathering does not conflict with the data processing here. 

    Could you also check your memory allocation to make sure nothing is full? You may also need to look at the samples to cover 2 full cycles, so for a 256 Hz signal you may need 512 an array*

    Thanks,
    Alex F

  • Hello Alex,

    Actually I'm not using the mq_receive / mq_send functions.You may see the code that I run below.In the terminal, current timestamp is printed to be able to observe that some of them are same for different adcCallBacks. For your better understanding, I am not giving any sine wave as input for this test case.

    /*
     * shm_board.h
     *
     */
    
    #ifndef SHM_BOARD_H_
    #define SHM_BOARD_H_
    
    #include "ti_drivers_config.h"
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/hal/Seconds.h>
    #include <ti/sysbios/knl/Event.h>
    
    /* EVENTS */
    #define MAIN_THREAD_EVENT_FLAG   1
    
    extern Event_Handle         mainThreadEvent;
    extern Event_Params   mainThreadEventParams;
    
    /* TIMING */
    #define Board_us2ticks(x)   ((x) / 10)
    #define Board_ms2ticks(x)   ((x) * 100)
    
    #define Board_sleepus(x)    Task_sleep(Board_us2ticks(x))
    #define Board_sleepms(x)    Task_sleep(Board_ms2ticks(x))
    #define Board_setTime(s)    Seconds_set(s)
    #define Board_getTime       Seconds_get
    
    
    #endif /* SHM_BOARD_H_ */

    /*
     *  ======== adcBufContinuousSampling.c ========
     */
     
    #include <stdint.h>
    #include <stdio.h>
    #include <mqueue.h>
    #include <ti/sysbios/BIOS.h>
    
    /* Driver Header files */
    #include <ti/drivers/ADCBuf.h>
    
    /* Display Header files */
    #include <ti/display/Display.h>
    
    /* Driver configuration */
    #include "ti_drivers_config.h"
    #include "shm_board.h"
    #include <ti/sysbios/knl/Event.h> //DT
    
    #define ADCSAMPLESIZE              (1024)
    #define MAX_QUEUED_ADC_CONVERSIONS (4)
    #define QUEUED_ADC_MESSAGE_SIZE    (sizeof(uint32_t) * ADCSAMPLESIZE)
    
    UInt data_handling_event;
    
    uint16_t sampleBufferOne[ADCSAMPLESIZE];
    uint16_t sampleBufferTwo[ADCSAMPLESIZE];
    uint32_t microVoltBuffer[ADCSAMPLESIZE];
    uint32_t outputBuffer[ADCSAMPLESIZE];
    uint32_t buffersCompletedCounter = 0;
    
    /* Display Driver Handle */
    Display_Handle displayHandle;
    
    /* Used to pass ADC data between callback and main task */
    static mqd_t queueReceive;
    static mqd_t queueSend;
    
    uint32_t curr_time=0;
    
    /*
     * This function is called whenever an ADC buffer is full.
     * The content of the buffer is then converted into human-readable format and
     * sent to the main thread.
     */
    void adcBufCallback(ADCBuf_Handle handle,
                        ADCBuf_Conversion *conversion,
                        void *completedADCBuffer,
                        uint32_t completedChannel,
                        int_fast16_t status)
    {
        /* Adjust raw ADC values and convert them to microvolts */
        ADCBuf_adjustRawValues(handle, completedADCBuffer, ADCSAMPLESIZE, completedChannel);
        ADCBuf_convertAdjustedToMicroVolts(handle, completedChannel, completedADCBuffer, microVoltBuffer, ADCSAMPLESIZE);
    
        /* Wake up main thread  */
        Event_post(mainThreadEvent,MAIN_THREAD_EVENT_FLAG );
    
    }
    
    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        Display_Params displayParams;
        ADCBuf_Handle adcBuf;
        ADCBuf_Params adcBufParams;
        ADCBuf_Conversion continuousConversion;
        struct mq_attr attr;
        uint_fast16_t i = 0;
    
        /* Create RTOS Queue */
        attr.mq_flags   = 0;
        attr.mq_maxmsg  = MAX_QUEUED_ADC_CONVERSIONS;
        attr.mq_msgsize = QUEUED_ADC_MESSAGE_SIZE;
        attr.mq_curmsgs = 0;
    
        /* Call driver init functions */
        ADCBuf_init();
        Display_init();
    
        /* Configure & open Display driver */
        Display_Params_init(&displayParams);
        displayParams.lineClearMode = DISPLAY_CLEAR_BOTH;
        displayHandle               = Display_open(Display_Type_UART, &displayParams);
        if (displayHandle == NULL)
        {
            Display_printf(displayHandle, 0, 0, "Error creating displayHandle\n");
            while (1) {}
        }
    
        Display_printf(displayHandle, 0, 0, "Starting the ADCBufContinuous example");
    
        /* Set up an ADCBuf peripheral in ADCBuf_RECURRENCE_MODE_CONTINUOUS */
        ADCBuf_Params_init(&adcBufParams);
        adcBufParams.callbackFxn       = adcBufCallback;
        adcBufParams.recurrenceMode    = ADCBuf_RECURRENCE_MODE_CONTINUOUS;
        adcBufParams.returnMode        = ADCBuf_RETURN_MODE_CALLBACK;
        adcBufParams.samplingFrequency = 1024;
        adcBuf                         = ADCBuf_open(CONFIG_ADCBUF_0, &adcBufParams);
    
        /* Configure the conversion struct */
        continuousConversion.arg                   = NULL;
        continuousConversion.adcChannel            = CONFIG_ADCBUF_0_CHANNEL_0;
        continuousConversion.sampleBuffer          = sampleBufferOne;
        continuousConversion.sampleBufferTwo       = sampleBufferTwo;
        continuousConversion.samplesRequestedCount = ADCSAMPLESIZE;
    
        if (adcBuf == NULL)
        {
            /* ADCBuf failed to open. */
            while (1) {}
        }
    
        /* Start converting. */
        if (ADCBuf_convert(adcBuf, &continuousConversion, 1) != ADCBuf_STATUS_SUCCESS)
        {
            /* Did not start conversion process correctly. */
            while (1) {}
        }
    
        /*
         * Wait for ADC  from the callback
         * function. When event is posted, get current timestamp and print it to UART
         */
        while (1)
        {
    
            /* Waits (always) event from the ADC.Every time one of the two buffer is filled successfully, event is posted from ADC. */
            data_handling_event = Event_pend(mainThreadEvent, Event_Id_NONE, MAIN_THREAD_EVENT_FLAG,BIOS_WAIT_FOREVER);
    
            curr_time = Board_getTime();
            Display_printf(displayHandle, 0, 0, "Tmsp: %u",curr_time );
    
        }
    }

  • Hello Danae, 

    I am taking a look at your code now. Could you provide me a screenshot/picture of the repeated timestamp/adc_callback. I did get the code running but since it does not include the sine wave, I get the timestamps only on my output (although I did notice that for a 1 second timestamp I read 1145 values, do you read this as well?). 

    Thanks,
    Alex F

  • Hello Alex,

    You are right, it appears that oversampling is occurring. I've realized that when I'm using Power_setDependency(PowerCC26XX_XOSC_HF); parameter in my code , there is a lot of improvement in the accuracy. Could you please provide me information about the potential impact in case I will set this parameter? From my perspective, I understood that RC oscillator is used by default and when adjusting this parameter crystal oscillator is used instead. This could explain also the fact that accuracy improved.

    Thanks in advance,

    Danae.

  • Hello Danae,

    For the potential impact of the XOSC_HF is as follows: You can't go into standby (or shutdown) with the ADC clock since it uses RCOSC_HF or XOSC_HF as the clock source, both of which are off in standby mode (so you would consume more power as you can't shutdown/standby). Additionally, as you confirmed yourself, the XOSC_HF is more accurate than the RCOSC_HF, so forcing the MCU to use XOSC_HF will give you the best results. 

    For more information please reference:

    CC13x2x7 and CC26x2x7 Technical Reference Manual (ti.com) 

    Specifically 

    -Section 19.5.2.2.4, "ADC Clock Source"

    -Section 7.6 "Power Modes" (which shows us what can be active in what power mode). 

    Thanks,
    Alex F