我使用的是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);
}