Other Parts Discussed in Thread: CC2652R7
我正在寻求有关 CC2652R7器件相关问题的帮助。 我已经将 ADC 采样频率配置为32768Hz、但在检索样本时、我注意到该频率大约比配置的频率高1%。 对可能造成这种情况的原因有任何见解吗?
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.
您好 Alex:
感谢您的快速回复。
这涉及到自定义电路板、并且采样持续时间等于 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评估套件上的示例项目。
下面介绍了我所做的更改:
在调试模式下运行该文件并每次以秒为单位打印当前时间戳 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 );
}
}您好 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