C для приложения AVR - повторение ISR - PullRequest
0 голосов
/ 02 ноября 2018

Я пытаюсь заставить простую подпрограмму прерывания работать на 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 Ответ

0 голосов
/ 03 ноября 2018

Давайте работать на основе того, что вы с радостью игнорируете любые нажатия кнопок, пока светодиод горит в течение 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;, так как статические int s в области видимости файла инициализируются в 0, но намного яснее инициализировать явно.

  2. C-программы часто используют for (;;) в качестве идиомы для «loop навсегда» вместо while (1).

...