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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user