Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Микропроцессорные системы. Средства разработки программного обеспечения для микроконтроллеров семейства AVR. Учебное пособие
.pdf
71
Вектор прерывания – это закрепленный за устройством номер
или адрес памяти, который идентифицирует соответствующий обработ-
чик прерываний.
Таблица векторов прерываний микроконтроллеров семейства AVR
содержит для каждого из прерываний команду перехода на соответ-
ствующую подпрограмму-обработчик прерывания.
Размер элемента таблицы векторов прерываний микроконтроллера
семейства AVR – 2 или 4 байта, в зависимости от конкретной модели
микроконтроллера.
Для микроконтроллеров AVR с объемом памяти программ не более 8 Кбайт размер элемента таблицы векторов прерываний составляет
2 байта, поскольку для перехода на любой из адресов памяти программ
достаточно команды относительного перехода RJMP, имеющей размер
2 байта.
Для микроконтроллеров AVR с объемом памяти программ более
8 Кбайт размер элемента таблицы векторов прерываний составляет
4 байта, поскольку для перехода на любой из адресов памяти программ
необходимо использовать команду абсолютного перехода JMP, имею-
щую размер 4 байта.
Для примера приведем таблицу векторов прерываний для микроконтроллера AT Mega169 (табл. 3).
Таблица 3
Список векторов прерываний ATMega169
Номер
вектора
Адрес
(в байтах)
Команда в таблице
векторов прерываний
Описание
0
0x0000
jmp RESET
Рестарт процессора по сигналу
Reset или WatchDog-таймеру
1
0x0004
jmp EXT_INT0
Прерывание от внешнего сиг-
нала
2
0x0008
jmp PCINT0
Прерывание по изменению со-
стояния линии
3
0x000С
jmp PCINT1
Прерывание по изменению со-
стояния линии
4
0x0010
jmp TIM2_COMP
Прерывание по совпадению
таймера 2
5
0x0014
jmp TIM2_OVF
Прерывание по переполнению
таймера 2
6
0x0018
jmp TIM1_CAPT
Прерывание по захвату тайме-
ра 1

72
7
0x001С
jmp TIM1_COMPA
Прерывание по совпадению с
регистром А таймера 1
8
0x0020
jmp TIM1_COMPB
Прерывание по совпадению с
регистром B таймера 1
9
0x0024
jmp TIM1_OVF
Прерывание по переполнению
таймера 1
10
0x0028
jmp TIM0_COMP
Прерывание по совпадению
таймера 0
11
0x002С
jmp TIM0_OVF
Прерывание по переполнению
таймера 0
12
0x0030
jmp SPI_STC
Прерывание по завершению
обмена SPI
13
0x0034
jmp USART_RXCn
Прерывание по приему байта
UART
14
0x0038
jmp USART_DRE
Прерывание по флагу «регистр
передачи данных пуст» UART
15
0x003С
jmp USART_TXCn
Прерывание по окончанию пе-
редачи байта UART
16
0x0040
jmp USI_STRT
Прерывание по началу обмена,
интерфейс USI
17
0x0044
jmp USI_OVFL
Прерывание по переполнению,
интерфейс USI
18
0x0048
jmp ANA_COMP
Прерывание по событию от
аналогового компаратора
19
0x004С
jmp ADC
Прерывание от АЦП, аналого-
цифровое преобразование за-
вершено
20
0x0050
jmp EE_RDY
Прерывание по готовности
энергонезависимой памяти
EEPROM
21
0x0054
jmp SPM_RDY
Прерывание по готовности к
записи в память программ
22
0x0058
jmp LCD_SOF
Прерывание по началу фрейма
ЖКИ-дисплея
Поскольку объем памяти программ микроконтроллера ATMega169
составляет 16 Кбайт, то каждый элемент таблицы векторов прерываний
представляет собой 4 байта.
Если устройство генерирует прерывание, то осуществляется вызов
подпрограммы по адресу, расположенному в таблице векторов прерываний, закрепленному за этим устройством. Одновременно с вызовом
подпрограммы (процедуры) обработки прерывания сбрасывается флаг I

73
регистра SREG, т. е. запрещаются все прерывания. Поэтому, пока про-
цедура обработки прерывания не завершится, все прерывания будут
запрещены. Команда выхода из процедуры обработки прерывания –
RETI. Помимо возврата из подпрограммы, данная команда устанавли-
вает флаг I регистра SREG, т. е. разрешает все прерывания от внешних
устройств, которые разрешены регистрами управления соответствую-
щих устройств.
Пример общей структуры обработчика прерываний в микроконтроллере ATMega169 на языке ассемблера показан ниже.
; Элемент таблицы векторов прерываний по адресу 0x0004
; содержит одну команду – переход на обработчик
; прерывания от внешнего сигнала
0x0004: JMP EXT_INT0
; Адрес EXT_INT0находится
; за пределами таблицы векторов прерываний
EXT_INT0:
; Обработчик прерывания от внешнего сигнала
; Прерывания запрещены
; ……………………………
; Возвращаемся из процедуры обработки прерывания,
; и одновременно разрешаем прерывания
RETI
Рациональное проектирование обработчиков прерываний позволя-
ет рационально использовать ресурсы микроконтроллера, поэтому обработка прерываний является важнейшей частью программирования мик-
роконтроллеров.
9.4.1. Обработка прерываний на языках C и С++
Для микроконтроллеров семейства AVR в библиотеке языков С и
С++ имеются специальные макросы для описания процедур обработки
прерываний, описанные в файле avr/interrupt.h.
Имеются два полезных макроса для разрешения и запрета всех
маскируемых прерываний:
макрос sei() – устанавливает флаг глобального разрешения пре-
рываний I регистра SREG (разрешение маскируемых прерываний);
макрос cli() – сбрасывает флаг глобального разрешения преры-
ваний I регистра SREG (запрет маскируемых прерываний).

74
Для установки пользовательского обработчика прерывания имеются несколько макросов.
Макрос ISR(INT_VEC_NAME) объявляет обработчик прерыва-
ния с вектором INT_VEC_NAME, где INT_VEC_NAME – это номер
вектора. Символические названия векторов прерывания для различных
микроконтроллеров определены в заголовочных файлах для каждого из
типов микроконтроллеров.
Пример. Описать обработчик прерывания для таймера 0.
#include <avr/interrupt.h>
#include <avr/io.h>
ISR ( TIMER0_COMP_vect) {
// Процедура обработки прерывания от таймера 0
}
Процедуры обработки прерывания, описанные с помощью макроса ISR(), завершаются корректно командой RETI вместо RET.
Существует несколько вариантов обработки прерываний, которые
используются в зависимости от того, какие задачи решает обработчик
прерывания.
9.4.2. Блокировка прерываний
При использовании данного способа, пока выполняется обработка
одного из прерываний, все остальные прерывания запрещены.
Именно такой способ обычно используется в микроконтроллерах
семейства AVR. Поэтому процедуры обработки прерываний следует писать с минимальным, по возможности, временем выполнения. Следует,
по возможности, избегать в этих процедурах циклов и других длитель-
ных операций.
Все приведенные ранее примеры обработчиков прерываний ис-
пользовали именно такой способ – полная блокировка всех прерываний
на время выполнения обработчика прерывания.
9.4.3. Приоритетная обработка прерываний
Устройства, генерирующие прерывания, можно разбить на более
приоритетные и менее приоритетные. Прерывания от более приоритетных устройств должны быть обработаны раньше, чем прерывания от
менее приоритетных.
Рассмотрим, что собой представляет приоритетная обработка прерываний в общем случае (рис. 13).

75
Рис. 13. Принцип приоритетной обработки прерываний
Пусть имеются несколько устройств, генерирующих прерывания.
В нашем примере их три – с приоритетами 1, 2 и 3.
Будем считать формально, что основная программа имеет самый
низкий приоритет – например, в нашем случае, нулевой.
Каждый раз, при возникновении прерывания, сравниваются приоритеты устройства-источника прерывания и приоритет исполняемой в
данный момент программы или подпрограммы (текущий приоритет).
Если приоритет устройства, вызывающего прерывание больше те-
кущего приоритета, то исполняемая в данный момент программа или
подпрограмма прерывается и вызывается обработчик прерывания от
прерывающего устройства.
Если приоритет устройства, вызывающего прерывание, меньше
или равен текущему приоритету, то вызов обработчика прерывания от
этого устройства произойдет только тогда, когда завершатся все подпрограммы с большим или равным приоритетом, выполняемые на момент
возникновения прерывания.
В некоторых микропроцессорных системах в контроллере преры-
ваний имеется возможность задать приоритет прерываний от различных
устройств на аппаратном уровне.
В микроконтроллерах семейства AVR нет различия прерываний по
приоритету. Иными словами, если во время обработки одного прерывания возникает другое прерывание, то оно не обработается, пока не завершится текущий обработчик прерывания. Это далеко не всегда удобно, поскольку приоритет обработки прерываний от внешних устройств

76
часто неодинаков. Особенно актуально эта проблема встает при реализации систем управления и обработки данных реального времени, в которых для некоторых устройств время отклика программы на событие
жестко регламентировано.
Имеется несколько решений проблемы организации приоритетов
обработки прерываний в микроконтроллерах семейства AVR, в зависи-
мости от конкретной задачи. Рассмотрим некоторые из них.
Пример 1. Предположим, что в микропроцессорной системе на
базе AVR имеется одно устройство, для которого критично время отклика. Для остальных устройств будем считать время отклика на обработку
прерываний некритичным. Это простой, хотя и довольно часто встречающийся на практике, случай. В этом случае можно оставить обработку
прерывания только от этого, приоритетного, устройства. Остальные
устройства должны обрабатываться в основной программе путем циклического опроса их состояния. Достоинства такого способа – простота
программной реализации. Недостатки очевидны – обработка прерыва-
ния не более чем от одного устройства.
Пример 2. Предположим, что в микропроцессорной системе на
базе AVR имеется несколько устройств, для которых критично время отклика. Приоритет каждого из устройств известен. В этом случае необхо-
димо реализовать программный контроллер приоритета прерываний.
Воспользуемся тем, что все устройства в микроконтроллерах AVR
имеют локальные флаги разрешения прерываний в соответствующих
регистрах. Это позволит нам организовать программное управление
приоритетами прерываний.
Пусть в микропроцессорной системе имеются три устройства, ге-
нерирующие прерывания, с приоритетами 1, 2 и 3. С помощью управления локальными флагами разрешения прерываний мы реализуем функции разрешения и запрещения прерываний для каждого из устройств
отдельно. Содержимое тела каждой из этих функций в данный момент
не имеет значения и зависит от конкретного устройства.
// Разрешение прерывания от устройств с приоритетом 1
void irq1_enable();
// Запрещение прерывания от устройств с приоритетом 1
void irq1_disable();
// Разрешение прерывания от устройств с приоритетом 2
void irq2_enable();
// Запрещение прерывания от устройств с приоритетом 2
void irq2_disable();
// Разрешение прерывания от устройств с приоритетом 3

77
void irq3_enable();
// Запрещение прерывания от устройств с приоритетом 3
void irq3_disable();
Обработчики прерываний каждого из устройств выглядят так:
ISR (DEV1_vect) {
// Прерывания запрещены автоматически
irq1_disable();
irq2_enable();
irq3_enable();
sei(); // Разрешаем прерывания
// Далее – обработчик прерывания с приоритетом 1
…………………………………………………………….
}
ISR (DEV2_vect) {
// Прерывания запрещены автоматически
irq1_disable();
irq2_disable();
irq3_enable();
sei(); // Разрешаем прерывания
// Далее – обработчик прерывания с приоритетом 2
…………………………………………………………….
}
ISR (DEV3_vect) {
// Прерывания запрещены автоматически
// Это самое приоритетное устройство, поэтому
// разрешать прерывания не нужно
// Обработчик прерывания с приоритетом 3
…………………………………………………………….
}
Другими словами, при входе в обработчик прерывания все преры-
вания от устройств с меньшим или равным приоритетом запрещаются, а
для устройств с приоритетом, большим чем текущий, – разрешаются.
После чего прерывания глобально разрешаются. Напомним, что в микроконтроллерах семейства AVR, при вызове процедуры обработки прерывания, глобальный флаг разрешения прерываний сбрасывается, сле-
довательно, прерывания на момент вызова обработчика запрещены.

78
Если имеется несколько устройств с одинаковым приоритетом,
то такой способ программной реализации приоритетов обработки прерываний также работает.
При необходимости можно программно реализовать и более
сложные способы управления прерываниями. Однако следует помнить,
что, как правило, чем сложнее программная реализация, тем больше она
требует ресурсов микроконтроллера и тем больше вероятность возникновения алгоритмических ошибок. Поэтому следует реализовывать
наиболее простой из возможных способ обслуживания прерываний.
9.4.4. Обработка повторных прерываний
Иногда возникают ситуации, когда время обработки прерывания
больше, чем время между двумя последовательными событиями, вызы-
вающими это прерывание.
Или, иными словами, проблему можно сформулировать так:
как обработать прерывание от устройства, если оно возникло во время
выполнения обработчика прерывания от этого же устройства?
Например, мы получаем прерывания от таймера с периодом вре-
мени T. Пусть среднее время выполнения обработчика прерывания от
этого таймера составляет менее, чем T, но в некоторых случаях обработ-
чик имеет время выполнения больше, чем период T.
Существует несколько вариантов решения этой проблемы. Рассмотрим некоторые из них.
Вариант 1. Игнорирование повторных прерываний
Это самый простой способ. Достаточно запретить повторный вход
в функцию, как было показано в пп. 8.6. Тогда все повторные вызовы
прерывания просто не будут обработаны. Пример процедуры обработчика прерывания, которая игнорирует обработку всех прерываний от
этого же устройства, показан ниже.
В обработчике задана статическая переменная-флаг already_running,
указывающая на то, выполняется ли в данный момент обработчик пре-
рывания от таймера или нет.
При каждом входе в процедуру обработки флаг already_running
проверяется и, если он равен true, происходит немедленный выход из
процедуры обработки прерывания, и запуска тела обработчика не про-
исходит.
#include <avr/interrupt.h>
#include <avr/io.h>
// Процедура обработки прерывания от таймера 0

79
ISR ( TIMER0_COMP_vect) {
// Прерывания запрещены автоматически
// already_running==false – обработчик не выполняется
// already_running== true – обработчик выполняется
volatile static bool already_running=false;
// Если обработчик уже выполняется – то выходим
if(already_running){
return;
}
// Обработчик не выполняется. Запускаем его
already_running=true;
sei(); // Разрешаем прерывания
// Отсюда начинается тело обработчика прерывания
……………………………………..
// Обработчик завершен
cli(); // Запрет прерываний
already_running=false; // Разрешаем запуск обработчика
}
Если флаг already_running равен false, то выставляется признак
того, что обработчик запущен и запускается тело обработчика прерывания. При этом макросом sei() разрешаются прерывания, что позволяет
обрабатывать прерывания от других устройств.
По завершению выполнения тела обработчика прерывания, флаг
already_running устанавливается в значение false и происходит выход
из обработчика прерывания.
Такой способ обработки применим не всегда, а только для случаев,
когда нам важно обработать хотя бы одно событие от устройства и возможно игнорирование некоторых событий. Если необходимо вызвать
обработчик точно столько раз, сколько произошло событий, генерирую-
щих прерывание, необходимы другие методы.
Вариант 2. Подсчет событий
Этот вариант применим, если необходимо вызвать обработчик
точно столько раз, сколько произошло событий, и представляет собой по
сути счетчик событий.
#include <avr/interrupt.h>
#include <avr/io.h>

80
// Процедура обработки прерывания от таймера 0
ISR ( TIMER0_COMP_vect) {
// Прерывания запрещены автоматически
// Счетчик событий
volatile static unsigned char event_counter=0;
// Увеличиваем значение счетчика событий
event_counter++;
// Если обработчик уже выполняется, то выходим
if( event_counter>1)
return;
// Обработчик не выполняется. Запускаем его
while( event_counter != 0 ){
sei(); // Разрешаем прерывания
// Тело обработчика прерывания
……………………………………..
// Обработчик завершен
// Уменьшаем значение счетчика событий
event_counter--;
// Запрещаем прерывания
cli();
}
}
Этот вариант более сложен, чем предыдущий. Рассмотрим его работу.
В процедуре обработки прерывания введена переменная-счетчик
событий event_counter. При каждом вызове обработчика прерывания
переменная-счетчик событий event_counter увеличивается на единицу.
После каждой итерации тела обработчика прерывания переменнаясчетчик событий event_counter уменьшается на единицу.
Запуск цикла обработки событий от таймера while() выполняется
только один раз – при первом вызове обработчика прерывания. Если во
время выполнения обработчика прерывания вновь возникнет прерывание от того же таймера, то произойдет увеличение счетчика событий на
единицу, но повторного запуска цикла while() не будет.
Цикл while() пройдет столько итераций, сколько раз возникло пре-
рывание от таймера вне зависимости от того, успеет выполниться обра-
ботчик до возникновения следующего прерывания или нет.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
