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:ADC 采样频率配置问题

Guru**** 2747345 points

Other Parts Discussed in Thread: CC2652R7

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1280097/cc2652r7-problem-with-configuration-of-adc-sampling-frequency

器件型号:CC2652R7

我正在寻求有关 CC2652R7器件相关问题的帮助。 我已经将 ADC 采样频率配置为32768Hz、但在检索样本时、我注意到该频率大约比配置的频率高1%。 对可能造成这种情况的原因有任何见解吗?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好,Danae Tsaousi,

    我希望你们做得好。 您能否提供您的 SDK 版本、CCS 版本、以及如果适用、您基于哪个代码构建的示例? 您的电路板是定制电路板还是开发电路板? 您的采样持续时间是多长? 您还能否提供一个显示此1%差异的屏幕截图。  

    谢谢。
    A·F

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Alex:

    感谢您的快速回复。

    • CCS 版本:[12.1.0]
    • SDK 版本:SimpleLink CC13xx CC26xx SDK [6.29.0.29]

    这涉及到自定义电路板、并且采样持续时间等于 ADCBufCC26X2_SAMPLING_DURATION_2P7_US.My 代码基于 ti 提供的 adcBuf连续 示例。

    在我对检索到的数据应用 FFT 之后、我给出的输入是一个正弦256Hz。 我绘制了观察到最高峰值的结果、该峰值对应于采样频率、大约为254Hz。  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好,Danae Tsaousi,

    您是否对 adcBufContinuent 示例(如 config)进行了任何更改? 考虑到我们正在测量的值、对于采样持续时间而言、它似乎没有问题。 根据我的理解、您将频率设置为 32768Hz、测量254个波长(来自正弦波)。 我们可能会看到这里不一致的一个原因是正弦波和/或 CC2652R7系统时钟可能并不完全精确、并可能导致错误。 您是否具有 ADC 缓冲器且所有接收到的样本都处于调试模式? 您能够详细说明一下您的计算算法吗?  

    谢谢。
    亚历克斯  

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Alex:

    谢谢你的答复。 您对程序的理解是正确的。 我有一些示例、但首先、我想为您更新一个更简单版本的代码。

    I run 也[ adcbufcontinuous_lp_CC2652r7_tirtos_ticlang ] CC2652R7评估套件上的示例项目。

    下面介绍了我所做的更改:

    • ADCSAMPLESIZE 寄存器 adcBufParams.samplingFrequency 设置为 1024
    • adcBuf 回调 被调用、我为主线程发布事件、并且主线程也挂起来自回调的事件。

    在调试模式下运行该文件并每次以秒为单位打印当前时间戳 adcBuf 回调 当时我预计在每次事件发布后、时间戳都会增加1、因为 ADC 缓冲区和采样频率的大小都等于1024。 但是、在某些情况下、连续两次的时间戳是相同的、 adcBufCallback。 (例如时间戳:128,254秒)。 因此、即使使用评估套件、仍然会遇到相同的问题。

    关于定制电路板上的测试程序、我使用了一个精密波形发生器来以256Hz 的频率产生正弦波、并将所有数据存储到外部闪存中。 检索样本并为采样频率32768Hz 施加 FFT 后、最高的峰值位于254Hz 左右。

    提前感谢、

    Danae。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Danae:  

    作为在 adcbufcontinuous 的 SKD 示例中的测试、我创建了一个全局变量、并在 adcbufcallback 中增加其计数、通常我们会期望该全局变量与缓冲区计数匹配(Putty 终端中显示为输出的项目)。 在较低的频率(100Hz)下、我能够将全局变量与缓冲区计数紧密匹配。 然而、随着频率增加、RTOS 延迟开始成为一个问题、循环根本无法为所有回调提供服务、作为响应、mq_receive 可能从 mq_send 接收过多数据、从而导致不一致。

    由于在您的情况下使用的是"快速"信号、因此建议在进行任何数据处理时绕过环路。 尝试仅使用回调并在某个全局阵列中加载数据、然后在该阵列填满后、回调可使用 mq_send 返回到应用程序循环来处理数据。 我们将努力确保数据收集不与此处的数据处理相冲突。  

    您还能检查内存分配情况、确保没有任何内容被填满吗? 您可能还需要查看样本以覆盖2个完整周期,因此对于256 Hz 信号,您可能需要512个数组*

    谢谢。
    A·F

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Alex:

    实际上、我不使用 mq_receive / mq_send 函数。 您可能会看到我在下面运行的代码。 在终端中、会打印当前时间戳、以便能够观察到其中某些时间戳对于不同的 adcCallBack 是相同的。 为了便于您理解、我不提供任何正弦波作为此测试用例的输入。

    /*
     * 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 );
    
        }
    }

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Danae:  

    我现在来看一下您的代码。 您能否给我提供一个重复时间戳/adc_callback 的屏幕截图/图片。 我的确在运行代码、但由于它不包含正弦波、我只在我的输出上获得时间戳(尽管我确实注意到、对于1秒的时间戳、我读取了1145个值、您是否也读取了该值?)。  

    谢谢。
    A·F

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Alex:

    您是对的、似乎正在发生过采样。 我已经意识到、当我使用 Power_setDependency (PowerCC26XX_XOSC_HF);参数在我的代码中、精度有很大的提高。 您能否向我提供有关在我设置此参数时可能产生的影响的信息? 从我的角度来看、我了解 RC 振荡器是默认使用的、调整此参数时会改用晶体振荡器。 这也可以解释精度提高的原因。

    提前感谢、

    Danae。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Danae:

    由于 XOSC_HF 的潜在影响、如下所示:您不能使用 ADC 时钟进入待机(或关断)模式、因为它使用 RCOSC_HF 或 XOSC_HF 作为时钟源、两者都在待机模式下关闭(因此、您会消耗更多功率、因为您不能关断/待机)。 此外、正如您自己已经确认的那样、XOSC_HF 比 RCOSC_HF 更准确、所以强制 MCU 使用 XOSC_HF 将为您提供最佳结果。  

    有关更多信息、请参阅:

    CC13x2x7和 CC26x2x7技术参考手册(TI.com) 

    专门  

    -第19.5.2.2.4节"ADC 时钟源"

    -第7.6节"功耗模式"(它显示了我们在什么功耗模式下可以激活的功能)。  

    谢谢。
    A·F