⚙️ Полное руководство по прерываниям в микроконтроллерах ATmega
Прерывания — мощный механизм микроконтроллеров AVR, позволяющий асинхронно реагировать на события без постоянного опроса.
🔧 Что такое прерывание?
Это аппаратный или программный сигнал, который временно приостанавливает основную программу и передаёт управление в специализированный обработчик — ISR (Interrupt Service Routine).
📋 Таблица векторов прерываний
ТАБЛИЦА ВЕКТОРОВ AVR
Приоритет | Вектор прерывания | Адрес | Описание |
---|---|---|---|
1 | RESET_vect | 0x0000 | Сброс микроконтроллера |
2 | INT0_vect | 0x0002 | Внешнее прерывание 0 |
3 | INT1_vect | 0x0004 | Внешнее прерывание 1 |
4 | PCINT0_vect | 0x0006 | Прерывание по изменению состояния пинов 0-7 |
5 | PCINT1_vect | 0x0008 | Прерывание по изменению состояния пинов 8-14 |
6 | PCINT2_vect | 0x000A | Прерывание по изменению состояния пинов 16-23 |
7 | WDT_vect | 0x000C | Прерывание сторожевого таймера |
8 | TIMER2_COMPA_vect | 0x000E | Сравнение таймера/счётчика 2, канал A |
9 | TIMER2_COMPB_vect | 0x0010 | Сравнение таймера/счётчика 2, канал B |
10 | TIMER2_OVF_vect | 0x0012 | Переполнение таймера/счётчика 2 |
11 | TIMER1_CAPT_vect | 0x0014 | Захват события таймера/счётчика 1 |
12 | TIMER1_COMPA_vect | 0x0016 | Сравнение таймера/счётчика 1, канал A |
13 | TIMER1_COMPB_vect | 0x0018 | Сравнение таймера/счётчика 1, канал B |
14 | TIMER1_OVF_vect | 0x001A | Переполнение таймера/счётчика 1 |
15 | TIMER0_COMPA_vect | 0x001C | Сравнение таймера/счётчика 0, канал A |
16 | TIMER0_COMPB_vect | 0x001E | Сравнение таймера/счётчика 0, канал B |
17 | TIMER0_OVF_vect | 0x0020 | Переполнение таймера/счётчика 0 |
18 | SPI_STC_vect | 0x0022 | Завершение передачи SPI |
19 | USART_RX_vect | 0x0024 | Прием данных USART |
20 | USART_UDRE_vect | 0x0026 | Готовность передатчика USART |
21 | USART_TX_vect | 0x0028 | Завершение передачи USART |
22 | ADC_vect | 0x002A | Завершение преобразования ADC |
23 | EE_READY_vect | 0x002C | Готовность EEPROM |
24 | ANALOG_COMP_vect | 0x002E | Прерывание аналогового компаратора |
25 | TWI_vect | 0x0030 | Прерывание TWI (I2C) |
26 | SPM_READY_vect | 0x0032 | Готовность SPM |
💡 Основные виды прерываний
- 🔌Внешние — INT0, INT1 (по фронту или спаду).
- ⏱️Таймерные — переполнение и совпадение таймеров 0/1/2.
- 🔄Изменение состояния порта — PCINT.
- 🕵️♂️Сторожевой таймер — WDT.
- 📡USART/SPI/I2C/ADC/… — периферийные события.
🛠️ Как работать с прерываниями
- Настраиваем регистры контроля (EICRA, TIMSKx, PCICR и др.).
- Разрешаем глобальные прерывания:
sei();
. - Определяем ISR:
ISR(вектор) {…}
.
Пример: INT0 — внешнее прерывание
#define F_CPU 1000000UL #include <avr/io.h> #include <avr/interrupt.h> void setup_int0() { DDRD &= ~(1 << PD2); PORTD |= (1 << PD2); EICRA |= (1 << ISC01); EIMSK |= (1 << INT0); sei(); } ISR(INT0_vect) { PORTB ^= (1 << PB0); } int main(void) { DDRB |= (1 << PB0); setup_int0(); while (1); }
Пример: TIMER0 — переполнение таймера
void timer0_setup() {
TCCR0A = 0;
TCCR0B |= (1 << CS02);
TIMSK0 |= (1 << TOIE0);
sei();
}
ISR(TIMER0_OVF_vect) {
// код ISR
}
📘 Основные регистры прерываний AVR
Для управления прерываниями в AVR используются следующие регистры:
Регистр | Назначение |
---|---|
EIMSK | Разрешение внешних прерываний |
EICRA / EICRB | Конфигурация триггера (спад, фронт и т.д.) |
PCICR / PCMSKx | Прерывания по изменению пинов |
GIMSK / GIFR | Прерывания в ATtiny |
TIMSKx | Разрешение таймерных прерываний |
⚠️ Рекомендации по использованию
- ISR должны быть быстрыми и минимальными.
- Перед длительными операциями отключайте прерывания —
cli()
/sei()
. - Используйте volatile-переменные для обмена с основным кодом.
- Для отладки добавляйте UART‑логи или мигание светодиода.
🧪 Отладка и тестирование
- Визуализируйте события: светодиоды, логический анализатор.
- Используйте Serial (UART) в ISR с care — буферы, быстро!
- Избегайте конфликтов: не вызывайте долгие функции внутри ISR.
🏁 Итог
Прерывания позволяют сделать управление устройством адаптивным и эффективным. Главное — грамотно организовать их, учесть приоритеты и не допустить блокировок. Удачи в прошивках!
P.S.
Прерывания по изменению состояния пинов (Pin Change Interrupts) на AVR
Для лучшего понимания материал разделён на три части:
-
Регистр портов, разрешающих прерывания —
PCICR
-
Маски прерываний по пинам —
PCMSKx
-
Генерация событий — обработчики ISR
Прерывания по изменению состояния пинов (Pin Change Interrupts) на AVR
Материал разделён на три части:
- Регистр портов, разрешающих прерывания (
PCICR
) - Маски для выбора пинов (
PCMSKx
) - Обработчики прерываний (
ISR
)
1. Регистр PCICR
Разрешает прерывания по изменению пинов целого порта:
PCICR |= 0b00000001; // Включить прерывания для порта B (PB)
PCICR |= 0b00000010; // Включить прерывания для порта C (PC)
PCICR |= 0b00000100; // Включить прерывания для порта D (PD)
PCICR |= 0b00000101; // Включить прерывания для портов B и D
2. Регистр PCMSKx
Позволяет указать, на каких конкретных пинах разрешить прерывания:
Регистр | Порт | Пины |
---|---|---|
PCMSK0 | PORTB | PB0–PB5 (D8–D13) |
PCMSK1 | PORTC | PC0–PC5 (A0–A5) |
PCMSK2 | PORTD | PD0–PD7 (D0–D7) |
PCMSK0 |= 0b00000100; // Разрешить прерывание на D10 (PB2)
PCMSK1 |= 0b00001000; // Разрешить прерывание на A3 (PC3)
3. Обработчики прерываний (ISR)
При срабатывании прерывания вызывается соответствующий вектор:
Вектор | Порт | Пины |
---|---|---|
PCINT0_vect | PORTB | PB0–PB5 (D8–D13) |
PCINT1_vect | PORTC | PC0–PC5 (A0–A5) |
PCINT2_vect | PORTD | PD0–PD7 (D0–D7) |
Примечание: прерывания по изменению состояния (PCINT) срабатывают на любой переход уровня (HIGH ↔ LOW и обратно). Если требуется детекция только фронта или только спада — используйте внешние прерывания INT0/INT1 с регистрами EIMSK
и EICRA
.
int counter = 0; void setup() { Serial.begin(9600); pinMode(4, INPUT_PULLUP); pinMode(5, INPUT_PULLUP); PCICR |= B00000100; // Enable interrupts on PD port PCMSK2 |= B00110000; // Trigger interrupts on pins D4 and D5
sei(); //enable interrupts } void loop() { Serial.print("The counter is now at: "); Serial.println(counter); delay(100); } ISR (PCINT2_vect) { if (digitalRead(4)) { counter++; } else if (digitalRead(5)) { counter--; } }
Тут главное понять суть. Активация прерываний идет двумя регистрами:
PCICR - здесь мы указываем порт.
PCMSK2 - здесь мы указываем конкретные ноги.
ISR (PCINT2_vect) { //Здесь наш обработчик. Можно прописать вызов какой-то функции, проинициированной ранее. }