代码之家  ›  专栏  ›  技术社区  ›  SRobertJames

在微控制器上避免C溢出的同时,从时钟“freq”确定“ticks”的正确方法是什么?

  •  0
  • SRobertJames  · 技术社区  · 2 年前

    在C中乘和除int以避免溢出的正确方法是什么?我想确定有多少 ticks 的计时器 freq (以赫兹为单位) delay (以毫秒为单位)。这应该是 ticks = freq * delay / 1000

    但是,这句话在我看来很危险 (freq * delay) / 1000 我们冒着溢出的风险。如果我们写 freq * (delay / 1000) ,我们将进入浮动-这是不必要的和容易出错的,特别是在微控制器上。

    正确的方法是什么?


    代码正在Cortex M4 ARM微控制器上运行。Tick来自SysTick计时器。所有变量都是 uint32_t volatile uint32_t

    2 回复  |  直到 2 年前
        1
  •  3
  •   Clifford    2 年前

    您的建议:

    freq * (delay / 1000)
    

    浮点运算。这需要:

    freq * (delay / 1000.0)
    

    但你说得对,没有必要。更好的做法是:

    (freq / 1000) * delay
    

    只要 freq 是1000的倍数,这将不会导致精度损失,并至少避免“过早”溢出(即由于操作顺序选择不当而在较低值时发生的溢出)。

    当然,溢出仍然是可能的,但此表达式给出了 delay 而不采用更大的类型。例如,如果表达式的类型为 uint32_t , 延迟 最多可以是2 32 /1000或接近72分钟。

    至关重要的是,重新排序使 延迟 在任何系统中都是确定性的。它不再依赖于 freq -它将永远是72分钟。

    如果72分钟还不够长,那么在采用更大的数据类型之前,您甚至可以考虑较低分辨率的延迟(例如整秒)(这将降低效率,并可能增加原子性问题)。长延迟很少需要毫秒精度,而且在任何情况下,您的时钟在这段时间内都可能不是毫秒精度的——即使是TCXO也通常精确到2ppm,500秒后可能会产生1ms的漂移。

        2
  •  1
  •   0___________    2 年前

    简单地说,想想你能有多长的最大延迟。

    如果使用32位无符号数(负数表示延迟不是很实用,因为我不知道如何使用负延迟,这会让我们回到过去)。最大uint32_t为4294967295,这对于一个多小时的延迟来说已经足够了。

    如果您将计时器设置为每1/1000秒递增一次,则uint32_t将足以延迟 49.7102696181

    如果您需要更多,请在计算中使用较大的无符号整数: ((uint64_t)freq * delay) / 1000

        3
  •  0
  •   chux    2 年前

    以确定以freq(以Hz为单位)运行的计时器需要多少节拍才能产生延迟(以ms为单位)。这应该是 ticks = freq * delay / 1000

    • 使用足够宽的数学,也许 uint64_t 以防止乘法溢出。

    • 考虑 无符号的 在这种情况下,数学可以简化舍入。

    • 物理量通常应该得到一个全面的答案。在除法前将分母的1/2相加进行四舍五入。

    • 明确标识物理单元。

    • 避免裸魔术数字。

    uint32_t ticks(uint32_t freq /* Hz */, uint32_t delay /* ms */) {
      #define ms_PER_s 1000
      return (uint32_t) (( (uint64_t)freq*delay + ms_PER_s/2) / ms_PER_s);
    }