AVR应用程序的C-ISR重复

时间:2018-11-02 19:54:15

标签: c avr atmega debouncing isr

我正在尝试获取一个简单的中断例程以在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例程循环两次。我认为这是某种按钮弹跳的问题,但我不熟悉如何处理。我尝试在开始时引入500ms延迟,并且还尝试清除ISR中的引脚更改中断标志,以便它不会再次触发,但仍然会触发。预先感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我们的工作基于您很高兴在LED亮起1.5秒时忽略任何按钮按下。您可以这样编写中断处理程序:

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

,然后将其放在代码的顶部:

volatile int button_pressed = 0;

(有关volatile的全部内容以及为什么需要使用它的信息,请参见this page。)

然后您的主循环如下所示:

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;,因为文件范围内的静态int初始化为0,但是显式初始化要清晰得多。

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