⚙️ Полное руководство по прерываниям в микроконтроллерах 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) {
//Здесь наш обработчик. Можно прописать вызов какой-то функции, проинициированной ранее.
}
