⚙️ Полное руководство по прерываниям в микроконтроллерах 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/… — периферийные события.

🛠️ Как работать с прерываниями

  1. Настраиваем регистры контроля (EICRA, TIMSKx, PCICR и др.).
  2. Разрешаем глобальные прерывания: sei();.
  3. Определяем 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

Для лучшего понимания материал разделён на три части:

  1. Регистр портов, разрешающих прерыванияPCICR

  2. Маски прерываний по пинамPCMSKx

  3. Генерация событий — обработчики ISR

Прерывания по изменению состояния пинов (Pin Change Interrupts) на AVR

Материал разделён на три части:

  1. Регистр портов, разрешающих прерывания (PCICR)
  2. Маски для выбора пинов (PCMSKx)
  3. Обработчики прерываний (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) {
//Здесь наш обработчик. Можно прописать вызов какой-то функции, проинициированной ранее.
}