使用STM32设备获取以微秒为单位的当前时间时出现问题



我使用的是stm32f103c8,我需要一个函数,当从中断处理程序中调用时,该函数将返回以微秒为单位的正确时间。我在网上发现了以下建议这样做的代码:

uint32_t microsISR()
{
uint32_t ret;
uint32_t st = SysTick->VAL;
uint32_t pending = SCB->ICSR & SCB_ICSR_PENDSTSET_Msk;
uint32_t ms = UptimeMillis;
if (pending == 0)
ms++;
return ms * 1000 - st / ((SysTick->LOAD + 1) / 1000);
}

我对其工作原理的理解是使用系统时钟计数器,该计数器从8000(LOAD+1(开始重复倒计时,当它达到零时,会产生一个中断,使变量UptimeMills递增。这给出了以毫秒为单位的时间。为了得到微秒,我们得到系统时钟计数器的当前值,并将其除以8000/1000,得到以微秒为单位的偏移量。由于计数器正在倒计时,我们将其从当前时间中减去,单位为毫秒*1000。(事实上,为了正确起见,我认为在这个计算中应该在#毫秒中添加一个(。

这一切都很好,除非(在中断处理程序中(调用此函数时,系统时钟计数器已经结束,但系统时钟中断尚未调用,那么UptimeMillis计数将减少一。这是以下线路的目的:

if (pending == 0)
ms++;

然而,从这个角度来看是没有意义的。如果没有挂起的中断,它将递增#ms。事实上,如果我使用这个代码,在计数器翻转的点上,我会在返回的时间中出现大量故障。所以我把行改成:

if (pending != 0)
ms++;

这产生了更好的结果,但我仍然会偶尔出现故障(大约每2000次中断中就有1次(,这种故障总是在计数器滚动时发生。

在中断期间,我记录毫秒、微秒和计数器值的当前值。我发现有两种情况我会出错:

Milli Micros  DT  Counter Pending
1 1661  1660550 826  3602   0
2 1662  1661374 824  5010   0
3 1663  1662196 822  6436   0
4 1663  1662022 -174 7826   0
5 1664  1663847 1825 1228   0
6 1665  1664674 827  2614   0
7 1666  1665501 827  3993   0

中断是以大约820us的常规速率混合进来的。在这种情况下,中断3和中断4之间似乎发生的是计数器已经结束,但挂起标志没有设置。所以我需要在这个值上加1000,因为我没有这样做,所以我得到了一个负的经过时间。

第二种情况如下:

Milli Micros  DT  Counter Pending
1 1814  1813535 818  3721   0
2 1815  1814357 822  5151   0
3 1816  1815181 824  6554   0
4 1817  1817000 1819 2      1
5 1817  1816817 -183 1466   0
6 1818  1817637 820  2906   0

这是一个非常类似的情况,除了在这种情况下计数器还没有包装,但我已经得到了挂起的中断标志,这导致我错误地添加了1000。

显然,在两个相互竞争的中断之间存在某种竞争条件。我已经尝试将时钟中断优先级设置为高于和低于外部中断的优先级,但问题仍然存在。

有人对如何处理这个问题有任何建议吗?或者有人建议用不同的方法来获得中断处理程序中微秒的时间。

SysTick->VAL之前和之后读取UptimeMillis以确保没有发生滚动。

uint32_t microsISR()
{
uint32_t ms = UptimeMillis;
uint32_t st = SysTick->VAL;
// Did UptimeMillis rollover while reading SysTick->VAL?
if (ms != UptimeMillis)
{
// Rollover occurred so read both again.
// Must read both because we don't know whether the
// rollover occurred before or after reading SysTick->VAL.
// No need to check for another rollover because there is
// no chance of another rollover occurring so quickly.
ms = UptimeMillis;
st = SysTick->VAL;
}
return ms * 1000 - st / ((SysTick->LOAD + 1) / 1000);
}

或者在do-wile循环中也有同样的想法。

uint32_t microsISR()
{
uint32_t ms;
uint32_t st;
// Read UptimeMillis and SysTick->VAL until
// UptimeMillis doesn't rollover.
do
{
ms = UptimeMillis;
st = SysTick->VAL;
} while (ms != UptimeMillis);
return ms * 1000 - st / ((SysTick->LOAD + 1) / 1000);
}

最新更新