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






