【发布时间】:2021-10-30 17:36:33
【问题描述】:
我在生产模式下使用OSD32MP1(基于 STM32MP157c),在 Core A7 上使用 OpenSTLinux,在 M4 上使用 FreeRTOS。其中一项任务是以非常高的速度、非常精确地为 M4 获取的 ADC 数据添加时间戳(认为它的数量级为纳秒到微秒)。请注意,只有测量之间的时间差很重要。
片上 RTC 可用(分配给 A7,但 M4 可以访问寄存器)。然而,亚秒级精度约为 0.003 秒(PREDIV_S 为 255 - 详情请参阅 Reference Manual),因此还不够好。
This、this 和 this stackoverflow 帖子导致使用 DWT_CYCCNT 即 CPU 周期计数器来测量时间差。相关部分代码如下:
在 M4 侧:
typedef struct tTimeStamp
{
uint32_t nCPUFreq;
uint32_t nCPUCycles;
...
}tTimeStamp;
...
tTimeStamp oTimeStamp;
...
oTimeStamp.nCPUCycles = DWT->CYCCNT;
oTimeStamp.nCPUFreq = HAL_RCC_GetSystemCoreClockFreq();
最后 2 个语句在读取 ADC 值之前在 FreeRTOS 任务中运行。时间戳与其他数据一起交给 A7。
在 A7 侧(假设在时间 T0 有 tTimeStamp,然后在时间 T1 有 tTimeStamp):
// Second to NanoSecond Conversion
#define SECTONS 1000000000
...
float ComputeTimeDiffNS(tTimeStamp oTS0, tTimeStamp oTS1)
{
// to avoid reporting time diff at t0
// and in case CPU frequency changes
if (oTS0.nCPUFreq != oTS1.nCPUFreq)
return -1;
// in case of counter overflow
if (oTS0.nCPUCycles > oTS1.nCPUCycles)
{
float fCyclesDiff = float(UINT32_MAX- oTS0.nCPUCycles + oTS1.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq) / 2;
}
// base case
else
{
float fCyclesDiff = float(oTS1.nCPUCycles - oTS0.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq);
}
}
- 这是使用 DWT->CYCCNT 和 HAL_RCC_GetSystemCoreClockFreq() 测量非常精确的时差的正确方法吗?有没有更好、更精确的方法?
- 上述方法给了我两倍的时间。在读取 DWT->CYCCNT 时,我还切换了一个引脚并使用逻辑分析仪测量切换之间的间隔。说这个时间tActual是2ms。然而,上述公式,即 CPU_Cycles / CPU_Frequency 返回 tMeasured = 4ms。
这似乎表明公式应该是 CPU_Cycles / (2*CPU_Frequency)。所以要么频率需要加倍,要么周期需要减半。
在读数中,nCPUFreq 为 208878528(每个 Reference Manual 允许的最大值为 209000000),因此这必须是正确的,不能乘以 2。
CPU_Cycles 可以被 2 分频,但它不会表明 CPU 每个时钟周期要经过 2 个周期吗?这可能吗(CPU 在上升沿和下降沿循环??)
【问题讨论】:
-
1.这取决于您想要多少精度,以及什么是系统时钟源(HSI?HSE?),以及源时钟精度本身。注意 DWT 计数器,您需要手动启动它,因为它是在调试期间由探针完成的,而不是没有(默认情况下)。 2. 也许系统时钟比你想象的要慢(/2)?有可能吗?
-
为什么在代码中使用浮点数来浪费循环?原始周期工作得很好......当你做这样的数学时,你也会失去精度。
-
为什么不使用 M4 上的高速计时器之一?
标签: arm real-time stm32 freertos real-time-clock