如何可靠且安全地处理硬件计时器溢出引发的逻辑误判问题
我最近在处理硬件计时器溢出的边缘情况时卡了壳:当计时器数值溢出回绕后,原本的时间差判断会产生误判,触发不该执行的逻辑。
举个实际场景,我用的是16MHz的芯片,32位硬件计时器大约每72分钟就会溢出一次。下面是我目前的代码逻辑——eventTime是由某个硬件中断服务程序(ISR)在事件触发时记录的时间戳:
volatile unsigned long eventTime; // 32位无符号整数,单位微秒 void someISR(){ eventTime = micros(); } void loop(){ auto now = micros(); // 32位无符号整数,单位微秒 // 判断事件是否在过去1秒内发生 if (now - eventTime < 1000000){ // 执行仅在事件发生后1秒内才需要运行的代码 } }
大部分时候这个判断是正常的,但有一种情况会出问题:如果事件已经超过72分钟没发生,此时now刚好溢出回绕,并且刚超过eventTime + 72分钟的时刻,now - eventTime的计算结果会小于1000000,导致每72分钟左右就会出现一次误判,概率大概是0.02%。
我想知道该如何区分这种真实的短时间差和溢出导致的虚假时间差?
我自己先梳理了几个可能的解决方案,但都有各自的短板:
方案1:将32位时间戳升级为64位
实际使用中这能解决问题,但会引入更慢的64位运算,而且从逻辑上来说并没有真正修复这个误判的bug——只是把溢出的周期拉长到了几乎可以忽略的程度。方案2:维护一个溢出计数器
同样,实际使用中能缓解问题,但会增加代码复杂度,而且本质上还是没解决bug:因为溢出计数器本身最终也会溢出。方案3:在ISR内处理逻辑判断
这会带来其他问题:ISR内会延迟其他中断的响应,尤其是那些负责维护micros()的中断,导致在长ISR执行期间micros()返回的数值不准确。
补充说明:
为了避免误解,我这个问题不是关于有符号整数溢出的行为定义,而是要区分两种时间差:一种是真实的小时间差x,另一种是n倍的变量类型最大值加上x的时间差(其中x是我们关心的小值,n是正整数),场景特指计时器和中断的环境。
备注:内容来源于stack exchange,提问作者zero-day

