C для приложения AVR - повторение ISR

Я пытаюсь заставить простую подпрограмму прерывания работать на ATMega328P. Есть светодиод, подключенный к PD6, и встроенная кнопка на PB7. Светодиод должен нормально мигать до тех пор, пока кнопка не будет нажата, а затем постоянно светиться в течение 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, чтобы он не запускался снова, но все же срабатывает. Заранее благодарю за любую помощь!

1 ответ

Решение

Давайте работать на основе того, что вы с радостью игнорируете любые нажатия кнопок, пока светодиод горит в течение 1,5 секунд. Вы можете написать свой обработчик прерываний следующим образом:

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

и поместите это в верхней части вашего кода:

volatile int button_pressed = 0;

(См. Эту страницу для получения информации о том, что 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),

Другие вопросы по тегам