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