使用ARM Cortex SysTick计算经过的时间;当SysTick计数器翻身时会发生什么?

时间:2017-01-21 04:18:01

标签: c arm embedded integer-overflow

我目前正在审核有关嵌入式系统的在线edX课程,并了解如何使用SysTick计时器计算已用时间。

Picture of logic I'm referring to

Code of logic I'm referring to

然而,有一点令我感到困惑。我理解减去"现在"的想法。从"最后"获得经过的时间。但是,当现在"现在"当SysTick计时器达到0并重新加载时翻转,但是#34; last"是在SysTick计时器翻转之前的值(所以"现在"比"最后",通常它应该更小)?该值存储在unsigned long中,它是否会破坏程序?或者这种情况从未发生过,如果是这样,为什么呢?我希望有任何帮助清理它!

我查看了我能找到的唯一一个与我的问题类似的其他链接:How to deal with a wrapping counter in embedded C但我没有从中找到明确的答案。

3 个答案:

答案 0 :(得分:1)

拿一个3位计数器,它都可以扩展到24或32(我认为systick定时器是24。

因此,向上或向下计数并不重要,您只需要正确调整操作数即可。所以说倒计时7,6,5,4 7 - 4是3计数。但是二进制1 - 6 = 001 - 110

中的1,0,7,6怎么样?
     1
   001
+  001
=======

并解决它

   011
   001
+  001
=======
   011     

并剪切为3位,这是正确答案。

为4位或5位计数器编写程序应该很简单,尝试随机大小的计数。或者使用固定大小的计数,允许使用素数翻转

#include <stdio.h>
#define BITMASK 0xF
int main ( void )
{
    unsigned int now;
    unsigned int beg;
    unsigned int end;
    unsigned int ra;
    unsigned int rb;
    now=0;
    for(ra=0;ra<100000;ra++)
    {
        beg=now;
        for(rb=0;rb<13;rb++) now--;
        end=now;
        if(((beg-now)&BITMASK)!=13)
        {
            printf("Error 0x%X 0x%X\n",beg,end);
        }
    }
    printf("Done.\n");
    return(1);
}

如果它是一个计数器,根据方向从全零到全部或全部滚动到全零,这只能工作。我希望很明显,如果你设置一个假设的3位定时器从5开始,它从0回到5设定点然后三步可能是1,0,5,4和1-4不是3

    111
    001
+   011
=========
    101

考虑到这一点的另一种方式是两个补充的另一个特征,靠近环绕点

101  -3
110  -2
111  -1
000  +0
001  +1
010  +2

就像我们在小学时使用的数字线一样。从位模式110到010在数字线上是4步+2-2 = 4或4个单位。 基本上使用二进制补码的美,我们知道二进制补码中的加法和减法不是无符号或有符号特定的相同位模式导致相同的位模式。这就是我们如何解释它们。所以我们做了一点作弊,拿了签名数学并将结果解释为无符号。

答案 1 :(得分:1)

old_timer有一个很棒的答案,在对我自己进行了一些研究之后,我发现Stack Overflow的另一个很好的答案,我想我也会链接:How to subtract two unsigned ints with wrap around or overflow

答案 2 :(得分:1)

有一个简单的解决方案来计算与任何标准整数类型大小不同的两个整数之间的差异。您只需移动整数以使其MSB处于标准类型的MSB位置,执行算术,然后可选将结果移回原位,使其与原始值的单位相同。

在这种情况下,systick是一个24位计数器,所以:

uint32_t start_time = getSystick() ;

// do something

uint32_t end_time = getSystick() ;

uint32_t elapsed_time_in_systicks = ((start_time << 8) - (end_time << 8)) >> 8 ;

注意操作数的顺序 - systick计数 down 。这假设您设置的最大重新加载值为0xffffff,并且该指南针不会包含多次。

如果时间间隔使得计数器可以包装多次,那么您可能不需要这么高的分辨率,并且可能使用较低的重载值和使计数器递增的中断处理程序,并使用该较低分辨率的计数器用于时间测量。

或者,您可以在重载中断上使用uint8_t计数器递增。然后该计数器可以与24位的systick组合以创建一个真正的32位计数器。但是为了确保一致性,必须要小心 - 这样你就不会在它刚刚重新加载重载计数器之前组合一个systick(反之亦然)。这可以通过以下方式完成:

uint32_t getSystick32()
{
    uint8_t msb ;
    uint32_t systick ;
    do
    {
       msb = getSystickReloadCounter() ;
       systick = getSystick() ;

    } while( msb != systick_reload_counter ) ;

    return msb << 24 | systick ;
}