我正在编写一个微 Controller 中断,该中断需要为其硬件计时器之一添加偏移量。但是,由于定时器预分频器的工作方式,幼稚的方法可能会引入中断(off-by-one)错误,具体取决于中断执行相对于预分频器时钟的时序。
我为此在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个周期内进行,因此应在相同的预分频器“相位”下进行。 (例如,由于不同微 Controller 类型之间的差异,可以调整nop的数量)。但是,该解决方案无法考虑在设置ICR1
和读取TCNT1
之间发生的预分频器“相位”变化。关于c - 向预分频的硬件计时器添加偏移量时,如何避免这种一一对应的错误?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42308445/