Прерывание на контроллере AVR в Atmel AVR Studio

Первым делом о том что такое прерывание.
Прерывание ( interrupt ) - это своеобразная функция, которая будет выполнена при поступлении сигнала на какой нибудь вход контроллера.
При работе в AVR Studio прерывания создаются при помощи макросов ISR() , SIGNAL() и INTERRUPT(). Они помечают некоторую функцию как обработчик прерывания. Их различие в том, что INTERRUPT() и ISR() определяют функцию обработчик для случая, когда разрешено общее прерывание (обработчик может быть прерван), а SIGNAL() для случая когда общее прерывание запрещено.

На этом покончим с теорией и перейдём к практике (хотя теории ещё будет ниже).
Соберём в ISIS такую схему:

Как вы уже наверное догадались мы напишем прерывание (которое генерируется кнопкой) которое будет зажигать и тушить диод.
Итак, откройте студию и создайте стандартный проект.
Для использования прерываний включим заголовочный файл:

#include <avr/interrupt.h>

Условимся, что прерывание (физически) не будет включать выключать питание на ноге контроллера (как это делается я уже рассматривал), а всего лишь будет изменять флаг. При определённых значениях которого и будет включаться и выключаться диод.
Зададим этот флаг глобально:

int num = 1;

Теперь объявим прерывание:

ISR(SIG_INTERRUPT1){

    if (num == 1)
        num = 0;
    else
        num = 1;
}

Как видите в скобках макроса указан так называемый вектор прерывания. Этот вектор указывает компилятору для какого входа будет сгенерировано прерывание. Для INT1 - это SIG_INTERRUPT1. Для АЦП (ADC) например это - SIG_ADC. (весь перечень отлично описан в книге "Шпак Ю.А. Программирование на языке Си для AVR и PIC микроконтроллеров".)
Теперь перейдём к функции main нашей "программы".
Нам необходимо разрешить прерывания в целом и для INT1 в частности:

    sei();  // в целом
    GIMSK |= (1<<INT1);  // в частности

Когда это сделано нужно настроить поведение прерывания. Оно может быть сгенерировано по разному.
Скачиваем datasheet (ссылка есть при создании проекта) и находим в разделе прерывания (interrupt) такую таблицу:

Думаю как перевести это вы и так поймёте.
Установим состояние генерации прерывания при каждом "логическом изменении на INT1".

MCUCR = (0<<ISC11) | (1<<ISC10); //настройка

Теперь установим весь порт С как выход:

DDRC = 0xff;  // порт С - выход

Ну а это уже должно быть понятно:

    while (1){
        if (num == 1)  
            PORTC |= 1; // включаем первый выход С
        else
            PORTC &= ~1;  // выключаем первый выход С

        _delay_ms(100); // ждём 100мс
    }

Ждать не обязательно. Тем более что это уменьшает быстродействие. Но мне так хочется.
Программа целиком:

#define F_CPU 8000000UL // 8MHz

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

int num = 1;

ISR(SIG_INTERRUPT1){
    if (num == 1)
        num = 0;
    else
        num = 1;
}

int main (void){
    sei();
    GIMSK |= (1<<INT1);
    MCUCR = (0<<ISC11) | (1<<ISC10); //настроились
    DDRC = 0xff;  // порт С - выход
    while (1){
        if (num == 1)
            PORTC |= 1;
        else
            PORTC &= ~1;
        _delay_ms(100);
    }

    return 0;
}

Компилируем hex и собираем схему в Proteus. Наслаждаемся работой прерывания при изменении положения кнопки.

Просмотров:   27373

Комментарии

чт, 11/22/2012 - 14:11

Подскажите начинающему, что означают:
GIMSK |= (1<<INT1);
MCUCR = (0<<ISC11) | (1<<ISC10); //настроились
и что с чем происходит?

чт, 11/22/2012 - 14:11

<< - это бинарный сдвиг.

GIMSK |= (1<<INT1);

означает что мы вносим в регистр GIMSK еденицу в разряд INT1

Второй пример по сути аналогичен.

чт, 11/22/2012 - 14:11

Кажется вопрос был больше в том что это за регистры, а не в том что именно происходит

чт, 11/27/2014 - 21:18
Alex Shum

Спасибо!! Достаточно быстро разобрался, прочитав тему.

сб, 02/27/2016 - 09:57
Саша

Правильно ли я понимаю что из прерывание мы возвращаем число num в основную функцию???

пн, 02/29/2016 - 11:49

num - глобальная переменная, она доступна во всех частях программы. В прерывании она редактируется, а в бесконечном цикле используется для ветвления.

вс, 04/10/2016 - 20:54
Dmitry

Здравствуйте, а почему нигде не указали что pd3 работает как вход? Если мы активируем прерывание на нем, то он в принципе теряет способность быть выходом? И еще, е от pd3 через 3ома на землю необходима ли будет, если подключить внутреннее сопротивление pd3?

пн, 05/15/2017 - 15:06
Евгений

Здравствуйте!
Очень запоздалый комментарий, но все же.
Реальная схема будет работать не так как ожидается. Причина - дребезг кнопки. В момент нажатия, контроллер будет регистрировать множество прерываний, а не одно, как предполагается.

вс, 05/28/2017 - 15:28
Петр_Т

Да "дребезг" контактов в подавляющем числе случаев имеет место, но здесь задержка 100 мс (//мне так хочется) как раз и "подавит" дребезг (обычно он "длится" от долей до нескольких миллисекунд.

Добавить комментарий

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.
CAPTCHA
Введи эти символы. Ато роботы одолели!