STM32 32 位 ARM 架构上的系统节拍翻转

System Tick rollover on STM32 32bit ARM architecture

无法理解当 STM32 MCU 上的 32 位系统节拍使用 ST 提供的 HAL 平台翻转时会发生什么。

如果 MCU 运行ning 直到 HAL_GetTick() returns 其最大值为 2^32 -1 =0xFFFFFFFF 即 4,294,967,295 / 1000 / 60 / 60 / 24 = 大约 49 天(计算 1 毫秒滴答到可测量的最大持续时间时)。 如果你有一个计时器 运行 跨越翻转点会怎样?

在翻转事件上创建 100 毫秒延迟的示例代码:

uint32_t start = HAL_GetTick()  // start = 0xFFFF FFFF (in this example)

--> Interrupt increments systick which rolls it over to 0 at this point

while ((HAL_GetTick() - start) < 100);

所以当第一次计算循环中的表达式时 HAL_GetTick() = 0x0000 0000 并且开始 = 0xFFFF FFFF。因此 0x0000 00000 - 0xFFFF FFFF =? (这个数字不存在,因为它是负数,我们正在做无符号算术)

然而,当我 运行 在我的 STM32 上使用 GCC ARM 编译的以下代码时:

   uint32_t a = 0xFFFFFFFFUL;
   uint32_t b = 0x00000000UL;
   uint32_t c = b - a;
   printf("a =%lu b=%lu c=%lu\r\n", a, b, c);

输出为: a =4294967295 b=0 c=1

事实上,从代码在溢出时正常运行的角度来看,c=1 是好的,但我不明白这里在低级别上实际发生了什么。 0 - 4294967295 = 1 如何实现?遇到这种情况,单片机内部的算术逻辑单元在做什么,纸上怎么算?

不会有什么不好的事情发生。它的工作方式与环绕之前相同。

int main(void)
{
    uint32_t start = UINT32_MAX - 20;
    uint32_t current = start;

    for(uint32_t x = 0; x < 100; x++)
    {
        printf("start = 0x%08"PRIx32" current = 0x%08"PRIx32 " current - start = %"PRIu32"\n", start, current, current-start);
        current++;
    }
}

你可以在这里看到: https://godbolt.org/z/jx4T4fhsW

0x00000000 - 0xffffffff 将为 1,因为需要将 1 添加到 0xffffffff 以获得 0x00000000。其他号码也一样。

顺便说一句,如果您使用十六进制数而不是在编程中使用非常有限的十进制数,则更容易理解。

这是modular arithmetic. Or modulo wrapping is what happens when an unsigned integer overflows的特点。

当使用固定数量的 digits/bits 时,算术运算可能会溢出固定数量的数字。但是溢出的部分不能用固定的digits/bits个数表示,基本被mask掉了。溢出部分可以认为是取模,固定数digits/bits内的部分是余数或取模。给定模数,模数值在导致溢出的操作后保持 correct/congruent。

最好的理解方式就是在纸上用笔做一些操作。选择一个基地。十六进制很棒,但它适用于十进制、二进制和所有基数。选择固定数量的digits/bits。对于 uint32_t 你有 8 个十六进制数字或 32 位。选择两个相加时会溢出固定位数的值。在纸上做数学运算,并将任何溢出包括到一个额外的数字中。现在通过用手覆盖溢出来执行模运算。您的 CPU 凭借固定位数(即 uint32_t)自动执行此模运算。用不同的数字重复此操作并用 subtraction/underflow 重复。最终你会开始相信它是有效的。

设置此操作时必须小心。使用无符号类型并从当前刻度值中减去起始刻度值,就像在示例代码中所做的那样。 (例如,不要添加开始滴答的延迟并与当前滴答作比较。)Raymond Chen 的文章,Using modular arithmetic to avoid timing overflow problems 有更多信息。

How does 0 - 4294967295 = 1 ?? How would I calculate this on paper to show what the arithmetic logic unit inside the MCU is doing when this situation is encountered?

先用十六进制写成这样:

     0000_0000
 -   FFFF_FFFF
 _____________
 

然后意识到在第一个值(被减数)上可以有一个取模值0x1_0000_0000。 (因为根据模运算,“0x0_0000_0000 和 0x1_0000_0000 是全等模 0x1_0000_0000”)。那么应该很明显,差异是 1.

   1_0000_0000
 - 0_FFFF_FFFF
 _____________
   0_0000_0001