Cortex-M4单片机中用C计算定时器Ticks避免溢出的正确方法
解决Cortex-M4下SysTick定时器Ticks计算的溢出与浮点运算问题
问题描述
我需要计算运行在freq(单位Hz)下的SysTick定时器产生delay(单位ms)所需的ticks,理论公式为 ticks = freq * delay / 1000。但直接按此顺序计算容易发生32位无符号整数溢出;如果调整为freq * (delay / 1000)又会引入不必要且易出错的浮点运算。代码运行于Cortex-M4 ARM单片机,所有变量均为uint32_t或volatile uint32_t,请问如何用C语言通过纯整数运算正确实现,同时避免溢出?
方法1:利用64位中间变量(推荐,最直接高效)
Cortex-M4内核原生支持64位整数运算(ARMv7-M架构包含64位乘法指令),因此可以先将32位变量提升为64位完成乘法,再做除法,最后转回32位。这种方法完全规避浮点运算,且溢出风险为零(只要最终结果在uint32_t范围内,而SysTick的24位计数器天然限制了ticks最大值为0xFFFFFF,肯定满足)。
代码实现:
#include <stdint.h> uint32_t calculate_systick_ticks(uint32_t freq, uint32_t delay_ms) { uint64_t temp = (uint64_t)freq * delay_ms; return (uint32_t)(temp / 1000); }
说明
- 强制将
freq转为uint64_t后,乘法运算会以64位精度执行,彻底避免32位乘法的溢出问题。 - Cortex-M4的硬件指令支持64位乘除的核心操作,性能损失可以忽略。
方法2:通过最大公约数(GCD)约分(无64位依赖时备选)
如果出于特殊限制不能使用64位整数(尽管Cortex-M4支持),可以先对freq和1000求最大公约数,通过约分缩小乘法操作数的规模,降低溢出概率。
代码实现:
#include <stdint.h> // 欧几里得算法计算最大公约数 static uint32_t gcd(uint32_t a, uint32_t b) { while (b != 0) { uint32_t temp = b; b = a % b; a = temp; } return a; } uint32_t calculate_systick_ticks(uint32_t freq, uint32_t delay_ms) { uint32_t common_divisor = gcd(freq, 1000); uint32_t scaled_freq = freq / common_divisor; uint32_t scaled_divisor = 1000 / common_divisor; // 约分后乘法溢出概率大幅降低 return (scaled_freq * delay_ms) / scaled_divisor; }
说明
- 例如,当
freq为168MHz(168000000)时,与1000的最大公约数是200,约分后scaled_freq为840000,scaled_divisor为5,此时840000 * delay_ms的溢出风险远低于原始的168000000 * delay_ms。
额外优化:SysTick范围校验
由于SysTick的重载寄存器是24位(最大值为0xFFFFFF),可以在函数中增加校验,确保返回值不超出硬件限制:
uint32_t calculate_systick_ticks(uint32_t freq, uint32_t delay_ms) { uint64_t temp = (uint64_t)freq * delay_ms; uint32_t ticks = (uint32_t)(temp / 1000); return (ticks > 0xFFFFFF) ? 0xFFFFFF : ticks; }
内容的提问来源于stack exchange,提问作者SRobertJames
相关产品推荐
相关产品推荐

