使用时间溢出中断时出现故障
glitches when using time overflow interrupt
我猜我在 CODE 中遇到溢出中断(用于将 8 位定时器的分辨率从 16µs/step 提高到 1µs/step)的问题。程序在我的主循环中的 if 语句中似乎触发了溢出中断,从而搞砸了!
if(pInt == 1) //PCNINT-if-statment
{
pulse16 = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse16 >1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse16 <1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
}
}
pInt = 0; //resets pInt to exit PCNINT-if-statment
}
pInt是一个"flag-variable",表示PCINT被触发。
tot_overflow 变量在每次触发溢出中断时递增。
我使用 ATtiny85 作为 RC 开关,当来自接收器的微秒高于 1555 时,它应该在 PB2 引脚上变为低电平,而当微秒低于 1490 时,它应该变为高电平。
发生的情况如下:当检查 µs 是否高于 1555 或低于 1490 并使用溢出中断时,它有时会在 1490-1555 的 "dead-span" 中将 PB2 引脚 HIGH/LOW 转为不应该的值,并且有时在"dead-span"之外!这是关于故障的 VIDEO。请注意,绿色 LED 是 redLED,黄色 LED 是 greenLED 在我的代码中。
我对此很陌生,我不确定为什么会这样,我不明白为什么代码不起作用。我查看了 CTC 函数,但我看不出它有什么帮助,因为我仍然必须使用定时器溢出中断来获得我想要的 1µs/step.
分辨率
编辑
我已经尝试了几个变体(@yann-vernier 的建议),但仍然无法正常工作,这是最接近工作代码的:
while(1) //leave and/or put your own code here
{
static uint8_t tot_overflow; //variable to count the number of Timer/Counter1 overflows
if (TIFR & (1 << TOV1) )
{
TIFR |= (1 << TOV1); // clear timer-overflow-flag
tot_overflow ++;
}
if(GIFR & (1 << PCIF) ) //PCINT-flag idicates PCINT
{
uint16_t pulse; //variable to make a 16 bit integer from tot_overflow and TCNT1
// PORTB |= (1 << debugPin); //pin is HIGH on when interrupt is intialized
pulse = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
这部分我没有开始工作:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
我不确定我是否明白上面发生的事情,我唯一知道的是我可能做错了!当我评论上面的部分时,它和 mu 旧代码一样工作!
else
{
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse > 1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse < 1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
// PORTB |= (1 << greenLED); //for debug to indicate dead-span
// PORTB |= (1 << redLED); //for debug to indicate dead-span
}
}
}
GIFR |= (1 << PCIF); //clear PCINT-flag
}
else
{
}
}
}
ISR(TIMER1_OVF_vect) //when Counter/Timer1 overflows
{
}
ISR(PCINT0_vect) //when pin-level changes on PB3
{
}
是快到了还是我还是出乎意料?
是的,溢出中断随时可能发生(尽管它确实经常发生)。引脚更改中断也是如此。在这种情况下,它们中的每一个都只接触一个 8 位易失性变量(分别为 tot_overflow
和 pInt
),这些变量又由您的主循环轮询。这种结构并不能真正帮助你,除非主循环有其他工作要做,这可能需要比一个完整的定时器周期更长的时间(在这种情况下为 256*8=2048 个周期)。
因此,您可以在主循环中检查 GIFR 位 PCIF(而不是 pInt)和 TIFR 位 TOV1,而不是启用中断,这将使您更容易理解行为。重置它们是一种特殊情况,即向该位且仅向该位写入 1。这将为您节省引脚更改中断的时间,因为主循环检查可能会更频繁地发生,并且不需要跳转到中断服务例程的延迟。
但是,它不会解决您的问题。您正在尝试测量脉冲宽度,这在稍大的 AVR 上可以使用定时器输入捕获功能轻松完成。 ATtiny85 中的计时器没有此功能。在这样做的 AVR 上,输入捕获和定时器溢出中断的安排方式使得输入捕获中断可以安全地读取软件驱动的溢出计数器。
当您遇到定时器溢出时,您会递增 tot_overflow,当您检测到引脚更改时,您会读取 TCNT1 以组合这些值。这两个计数器,虽然一个提供给另一个,但不是同时读取的。您的阈值为 0x5d2 和 0x613。如果翻转发生在读取 tot_overflow 之后但在读取 TCNT1 之前,您很可能一次得到类似 0x501 的时间,而该时间应该是 0x601。同样,如果您停止 tot_overflow 更新。如果你在 tot_overflow 之前读取 TCNT1,你可能会在你应该读取 0x5fe 时得到 0x6fe。简而言之,没有安全顺序——但有关系。我们需要知道的是在读取计数器值时溢出计数值是否是最新的。
static uint8_t ovf;
if (TIFR & 1<<TOV1) {
TIFR = 1<<TOV1; // Clear overflow flag
ovf++;
}
if (GIFR & 1<<PCIF) {
GIFR = 1<<PCIF; // clear pin change flag
uint16_t timestamp = ovf<<8 | TCNT1;
if (TIFR&1<<TOV1 && (timestamp&0xff)<0x80)
timestamp += 0x100; // Overflow had not been counted
// Do what we like with the timestamp here
}
关键是我们在加载定时器值后检查溢出。如果溢出发生在我们读取值之前,则读取的值一定很低;否则我们想要旧计数。这个特定示例实际上依赖于未在中断中处理的溢出,但您可以通过将块包含在中断屏蔽中来使用相同的方法。我们需要TOV1和ovf的两次读取匹配,在TCNT1之后读取TOV1。我们不希望有一个中断过程,从而清除 TOV1,这样我们就无法推断出 ovf 和 TCNT1 的顺序。请注意,使用引脚更改中断来执行此逻辑会自动授予我们该逻辑,因为中断处理程序 运行 禁用了中断。
你的脉冲宽度不会比响应引脚变化的延迟方差更高的精度,在你显示的代码中,定时器重置(它也应该重置预分频器,位 PSR1 在GTCCR)。这通常意味着您确实想在中断处理程序本身中读取计时器。我们还可以观察到,您可以选择一个计时器速度,使您的阈值适合 8 位值;例如,定时器 1 运行ning 在 8MHz/64 时,您的阈值将在 186 和 194,偏移量为 16-24µs。由于计时器不必从 0 开始,人们甚至可以做一些技巧,例如在溢出时精确设置一个阈值。
@YannVernier 感谢您将我推向正确的方向,或者给我正确的方法! ;-) 我想我终于搞定了,多亏了一位朋友的额外帮助!
这是最后的 CODE
我没有首先知道我必须删除 TIMSK enable ande sei() 加上 ISR 例程,还有不小心放在后面的 else 语句:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
我猜我在 CODE 中遇到溢出中断(用于将 8 位定时器的分辨率从 16µs/step 提高到 1µs/step)的问题。程序在我的主循环中的 if 语句中似乎触发了溢出中断,从而搞砸了!
if(pInt == 1) //PCNINT-if-statment
{
pulse16 = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse16 >1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse16 <1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
}
}
pInt = 0; //resets pInt to exit PCNINT-if-statment
}
pInt是一个"flag-variable",表示PCINT被触发。 tot_overflow 变量在每次触发溢出中断时递增。
我使用 ATtiny85 作为 RC 开关,当来自接收器的微秒高于 1555 时,它应该在 PB2 引脚上变为低电平,而当微秒低于 1490 时,它应该变为高电平。
发生的情况如下:当检查 µs 是否高于 1555 或低于 1490 并使用溢出中断时,它有时会在 1490-1555 的 "dead-span" 中将 PB2 引脚 HIGH/LOW 转为不应该的值,并且有时在"dead-span"之外!这是关于故障的 VIDEO。请注意,绿色 LED 是 redLED,黄色 LED 是 greenLED 在我的代码中。
我对此很陌生,我不确定为什么会这样,我不明白为什么代码不起作用。我查看了 CTC 函数,但我看不出它有什么帮助,因为我仍然必须使用定时器溢出中断来获得我想要的 1µs/step.
分辨率编辑
我已经尝试了几个变体(@yann-vernier 的建议),但仍然无法正常工作,这是最接近工作代码的:
while(1) //leave and/or put your own code here
{
static uint8_t tot_overflow; //variable to count the number of Timer/Counter1 overflows
if (TIFR & (1 << TOV1) )
{
TIFR |= (1 << TOV1); // clear timer-overflow-flag
tot_overflow ++;
}
if(GIFR & (1 << PCIF) ) //PCINT-flag idicates PCINT
{
uint16_t pulse; //variable to make a 16 bit integer from tot_overflow and TCNT1
// PORTB |= (1 << debugPin); //pin is HIGH on when interrupt is intialized
pulse = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
这部分我没有开始工作:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
我不确定我是否明白上面发生的事情,我唯一知道的是我可能做错了!当我评论上面的部分时,它和 mu 旧代码一样工作!
else
{
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse > 1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse < 1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
// PORTB |= (1 << greenLED); //for debug to indicate dead-span
// PORTB |= (1 << redLED); //for debug to indicate dead-span
}
}
}
GIFR |= (1 << PCIF); //clear PCINT-flag
}
else
{
}
}
}
ISR(TIMER1_OVF_vect) //when Counter/Timer1 overflows
{
}
ISR(PCINT0_vect) //when pin-level changes on PB3
{
}
是快到了还是我还是出乎意料?
是的,溢出中断随时可能发生(尽管它确实经常发生)。引脚更改中断也是如此。在这种情况下,它们中的每一个都只接触一个 8 位易失性变量(分别为 tot_overflow
和 pInt
),这些变量又由您的主循环轮询。这种结构并不能真正帮助你,除非主循环有其他工作要做,这可能需要比一个完整的定时器周期更长的时间(在这种情况下为 256*8=2048 个周期)。
因此,您可以在主循环中检查 GIFR 位 PCIF(而不是 pInt)和 TIFR 位 TOV1,而不是启用中断,这将使您更容易理解行为。重置它们是一种特殊情况,即向该位且仅向该位写入 1。这将为您节省引脚更改中断的时间,因为主循环检查可能会更频繁地发生,并且不需要跳转到中断服务例程的延迟。
但是,它不会解决您的问题。您正在尝试测量脉冲宽度,这在稍大的 AVR 上可以使用定时器输入捕获功能轻松完成。 ATtiny85 中的计时器没有此功能。在这样做的 AVR 上,输入捕获和定时器溢出中断的安排方式使得输入捕获中断可以安全地读取软件驱动的溢出计数器。
当您遇到定时器溢出时,您会递增 tot_overflow,当您检测到引脚更改时,您会读取 TCNT1 以组合这些值。这两个计数器,虽然一个提供给另一个,但不是同时读取的。您的阈值为 0x5d2 和 0x613。如果翻转发生在读取 tot_overflow 之后但在读取 TCNT1 之前,您很可能一次得到类似 0x501 的时间,而该时间应该是 0x601。同样,如果您停止 tot_overflow 更新。如果你在 tot_overflow 之前读取 TCNT1,你可能会在你应该读取 0x5fe 时得到 0x6fe。简而言之,没有安全顺序——但有关系。我们需要知道的是在读取计数器值时溢出计数值是否是最新的。
static uint8_t ovf;
if (TIFR & 1<<TOV1) {
TIFR = 1<<TOV1; // Clear overflow flag
ovf++;
}
if (GIFR & 1<<PCIF) {
GIFR = 1<<PCIF; // clear pin change flag
uint16_t timestamp = ovf<<8 | TCNT1;
if (TIFR&1<<TOV1 && (timestamp&0xff)<0x80)
timestamp += 0x100; // Overflow had not been counted
// Do what we like with the timestamp here
}
关键是我们在加载定时器值后检查溢出。如果溢出发生在我们读取值之前,则读取的值一定很低;否则我们想要旧计数。这个特定示例实际上依赖于未在中断中处理的溢出,但您可以通过将块包含在中断屏蔽中来使用相同的方法。我们需要TOV1和ovf的两次读取匹配,在TCNT1之后读取TOV1。我们不希望有一个中断过程,从而清除 TOV1,这样我们就无法推断出 ovf 和 TCNT1 的顺序。请注意,使用引脚更改中断来执行此逻辑会自动授予我们该逻辑,因为中断处理程序 运行 禁用了中断。
你的脉冲宽度不会比响应引脚变化的延迟方差更高的精度,在你显示的代码中,定时器重置(它也应该重置预分频器,位 PSR1 在GTCCR)。这通常意味着您确实想在中断处理程序本身中读取计时器。我们还可以观察到,您可以选择一个计时器速度,使您的阈值适合 8 位值;例如,定时器 1 运行ning 在 8MHz/64 时,您的阈值将在 186 和 194,偏移量为 16-24µs。由于计时器不必从 0 开始,人们甚至可以做一些技巧,例如在溢出时精确设置一个阈值。
@YannVernier 感谢您将我推向正确的方向,或者给我正确的方法! ;-) 我想我终于搞定了,多亏了一位朋友的额外帮助!
这是最后的 CODE
我没有首先知道我必须删除 TIMSK enable ande sei() 加上 ISR 例程,还有不小心放在后面的 else 语句:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}