【问题标题】:C for AVR Application - ISR RepetitionC 用于 AVR 应用程序 - ISR 重复
【发布时间】:2019-04-07 01:56:08
【问题描述】:

我正在尝试让一个简单的中断例程在 ATMega328P 上工作。有一个连接到 PD6 的 LED 和一个在 PB7 上的内置按钮。 LED 应正常闪烁,直到按下按钮,然后稳定 1.5 秒,然后再重新闪烁。代码如下:

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int main(void)
{
    // Enable pull-ups and set pin directions
    MCUCR |= (1<<PUD);
    PORTD &= ~(1<<PORTD6);
    DDRD |= (1<<DDD6);
    PORTB |= (1<<PORTB7);
    DDRB &= ~(1<<DDB7);

    // Enable pin change interrupt
    PCICR = 0x01;
    PCMSK0 = 0x80;
    sei();

    while (1) 
    {
        // Blink LED at standard rate
        _delay_ms(500);
        PORTD ^= (1<<PORTD6);
        _delay_ms(500);
        PORTD ^= (1<<PORTD6);
    }
}

ISR(PCINT0_vect,ISR_BLOCK)
{
    PORTD &= ~(1<<PORTD6);
    _delay_ms(500);
    PORTD |= (1<<PORTD6);
    _delay_ms(1500);
    PORTD &= ~(1<<PORTD6);
}

中断触发正确,但是 ISR 例程循环了两次。我想这是某种按钮弹跳问题,但我不熟悉如何处理它。我尝试在开始时引入 500 毫秒延迟,并且我还尝试清除 ISR 中的引脚更改中断标志,以便它不会再次触发,但它仍然会触发。提前感谢您的帮助!

【问题讨论】:

  • 请不要在中断处理程序中设置延迟,它应该尽可能快,并且可能会阻止其他中断被服务。更好的是记录按下按钮的时间计数。如果在上一个按钮之后太快按下按钮,请记录时间但忽略它。使用中断处理程序设置的信息从较低级别控制 LED。
  • 最简单的方法是让 ISR 只设置一个 button_pressed 标志并退出。然后在主循环中打开灯后输入if (button_pressed) {delay(1000); button_pressed=false;}。但总的来说,嵌入式代码应该完全避免延迟循环,并用时钟检查和/或定时器中断来代替它们。
  • 我确实听说避免使用 delay() 更可取,我会寻找一些示例来用计时器控件替换 delay() 函数。

标签: c avr atmega debouncing isr


【解决方案1】:

让我们在 LED 亮起 1.5 秒时您乐于忽略任何按钮按下的基础上工作。你可以这样写你的中断处理程序:

ISR(PCINT0_vect,ISR_BLOCK)
{
    button_pressed = 1;
}

并将其放在代码顶部的旁边:

volatile int button_pressed = 0;

(请参阅this page 了解volatile 的全部内容以及此处为何需要它。)

那么你的主循环可以是这样的:

while (1) 
{
    // Blink LED on and off

    PORTD |= (1<<PORTD6);   // Turn LED on.
    if (button_pressed) {
        _delay_ms(1500);    // Long delay if button was pressed.
        button_pressed = 0;
    } else {
        _delay_ms(500);     // Regular delay otherwise.
    }

    PORTD &= ~(1<<PORTD6);  // Turn LED off.
    _delay_ms(500);
}

高级读者注意事项:

  1. volatile int button_pressed = 0; 实际上可能只是volatile int button_pressed;,因为文件范围内的静态ints 被初始化为0,但显式初始化要清晰得多。

  2. C 程序通常使用 for (;;) 作为“永远循环”的习惯用法,而不是 while (1)

【讨论】:

  • 你似乎把异或操作误认为是或; OP 在主循环中使用 XOR,这确实会打开和关闭 LED;您的代码应该在 while() 主体的第一行使用 OR 而不是 XOR 操作。
  • @FrancescoLavra 你说得对。哎呀。谢谢,我会解决的。
  • button_pressed应该是volatile
  • @tofro 谢谢。是的,this page 提供了一些细节。我会更新我的答案。
  • 感谢这个例子,有趣的是,AtmelStudio 7 实际上在 main() 中默认包含一个 while(1){} 循环。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多