From 767aa140862eb02f31e1706ea1567459b022fa64 Mon Sep 17 00:00:00 2001 From: edisondeng Date: Fri, 31 Jul 2026 15:52:50 +0800 Subject: [PATCH] fix(timer): compensate Timer0_GetTimestamp for pending TF0 overflow TH0/TL0 torn-read protection didn't cover the case where the hardware counter already wrapped but Timer0_Isr hasn't run yet to bump timer0_ovf_count, making the timestamp appear to jump backward by one overflow period (~32.768ms). Read TF0 under a brief EA guard and treat a pending-but-unserviced overflow as ovf+1 without clearing it. --- Drivers/timer.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Drivers/timer.c b/Drivers/timer.c index 2efa5e2..b92a08a 100644 --- a/Drivers/timer.c +++ b/Drivers/timer.c @@ -35,6 +35,8 @@ u32 Timer0_GetTimestamp(void) { u16 ovf1, ovf2; u8 h1, h2, l; + bit tf, ea_bak; + do { ovf1 = timer0_ovf_count; h1 = TH0; @@ -42,6 +44,21 @@ u32 Timer0_GetTimestamp(void) h2 = TH0; ovf2 = timer0_ovf_count; } while (h1 != h2 || ovf1 != ovf2); + + // 上面的撕裂读保护只处理了 TH0/TL0 两字节配对读取时的撕裂,但还有另一种更隐蔽的竞态: + // 硬件计数器已经物理溢出归零 (TH0/TL0 已经是溢出后的小数值),但 Timer0_Isr 因为中断延迟 + // 还没来得及把 timer0_ovf_count 加一,导致算出来的时间戳比真实值凭空小了一整个溢出周期 + // (65536 tick ≈ 32.768ms),看起来像是"时间倒流"。这里读一次硬件溢出标志位 TF0 做补偿: + // 若 TF0 已经置位但还未被服务,本次读数临时按 ovf+1 计算,不清 TF0,留给真正的 + // Timer0_Isr 之后正常执行、更新持久计数,不影响后续调用。 + ea_bak = EA; + EA = 0; + tf = TF0; + EA = ea_bak; + if (tf) { + ovf1++; + } + return ((u32)ovf1 << 16) | ((u16)h1 << 8) | l; }