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.
This commit is contained in:
edisondeng
2026-07-31 15:52:50 +08:00
parent 3c896ec972
commit 767aa14086

View File

@@ -35,6 +35,8 @@ u32 Timer0_GetTimestamp(void)
{ {
u16 ovf1, ovf2; u16 ovf1, ovf2;
u8 h1, h2, l; u8 h1, h2, l;
bit tf, ea_bak;
do { do {
ovf1 = timer0_ovf_count; ovf1 = timer0_ovf_count;
h1 = TH0; h1 = TH0;
@@ -42,6 +44,21 @@ u32 Timer0_GetTimestamp(void)
h2 = TH0; h2 = TH0;
ovf2 = timer0_ovf_count; ovf2 = timer0_ovf_count;
} while (h1 != h2 || ovf1 != ovf2); } 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; return ((u32)ovf1 << 16) | ((u16)h1 << 8) | l;
} }