Прерывание на контроллере 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. Наслаждаемся работой прерывания при изменении положения кнопки.

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