向预分频硬件定时器添加偏移量时,如何避免这种差一错误?

How can I avoid this off-by-one error when adding an offset to a prescaled hardware timer?

我正在编写一个微控制器中断,需要为其硬件定时器之一添加一个偏移量。然而,由于定时器预分频器的工作方式,天真的方法可能会引入差一错误,具体取决于中断执行相对于预分频器时钟的时序。

为此,我在 ATmega328P(= arduino)上使用定时器 1。我将它设置为带有 /8 预分频器的正常模式,并使用定时器捕获中断来触发它;中断的目标是将定时器设置为在触发输入捕获的事件发生后恰好溢出 period 个周期(以防触发发生在另一个中断或其他中断被禁用的情况下)。

(我滥用 PWM 输出以可变 AC 相位偏移触发两个电源光电三极管,而不需要在其上燃烧所有 CPU 时间;中断由上的过零检测器触发电源阶段。)

ISR 的代码应该是这样的:

uint_16 period = 16667;

ISR(TIMER1_CAPT_vect){
    TCNT1 = TCNT1 - ICR1 - period + (elapsed counter ticks during execution);
}

这里的关键间隔是从 TCNT1 读取到再次写入之间的间隔。

据我所知,没有办法直接读取预分频器的状态,所以我认为不可能根据 ISR 时序应用不同的偏移量。

我可以在 ISR (GTCCR |= _BV(TSM); GTCCR |= _BV(PSRSYNC); GTCCR &= ~_BV(TSM);) 之前重置预分频器以进行同步,但这仍然会根据 ISR 时序向定时器引入随机偏移。

我正在考虑的另一种方法是使用定时器来生成与预分频器同步的中断。我已经在定时器 1 上使用了两个输出比较寄存器,但定时器 0 共享预分频器,因此可以使用它。但是,定时器中断的执行最终可能会被另一个中断或 'cli' 块延迟,因此不能保证这会起作用。

如何编写中断以避免此错误?

如果你把 ISR 写成

ISR(TIMER1_CAPT_vect){
    int counter = TCNT1 - ICR1 - period + 3;
    asm("nop");
    asm("nop");
    TCNT1 = counter;
}

TCNT1 的写入应该在读取寄存器后正好 24 个周期发生,因此在相同的预分频器 'phase' 处。 (如果需要,可以调整 nop 的数量,例如,由于不同微控制器类型之间的差异)。但是,该解决方案无法考虑在设置 ICR1 和读取 TCNT1 之间发生的预分频器 'phase' 的变化。