Перейти к содержанию

Прерывание по изменению вывода (pin change interrupt)

У ATmega328P всего два внешних прерывания INT0 и INT1 с выбором фронта. Но прерывание можно получить почти с любого вывода — через механизм pin change: оно срабатывает при каждом изменении уровня, а выводы сгруппированы по портам.

Обновлено Редакция GAW

Прерывание по изменению вывода (pin change interrupt, PCINT) — механизм AVR, при котором любое изменение уровня на разрешённом выводе вызывает прерывание. В отличие от внешних прерываний INT0 и INT1, у него нет выбора фронта, а выводы объединены в группы с общим вектором. Зато такой вывод — почти любой: у ATmega328P это 23 вывода в трёх группах (DS40002061B, разд. 13).

Группы ATmega328P

ГруппаВыводыМаскаРазрешениеФлагВектор
PCI0PCINT0…7 = PB0…PB7PCMSK0PCIE0PCIF0PCINT0
PCI1PCINT8…14 = PC0…PC6PCMSK1PCIE1PCIF1PCINT1
PCI2PCINT16…23 = PD0…PD7PCMSK2PCIE2PCIF2PCINT2

Регистры — по DS40002061B (разд. 13.2.4–13.2.8), соответствие выводам — по распиновке там же. Бит в PCMSKx разрешает конкретный вывод, бит PCIEx в PCICR — всю группу; при глобальном разрешении прерываний изменение на любом разрешённом выводе группы ставит флаг PCIFx и вызывает вектор группы. Флаг сбрасывается при входе в обработчик или записью единицы.

Особенности

  • Один вектор на группу. Какой вывод сработал, программа выясняет сама, сравнивая текущее значение PINx с прошлым.
  • Любое изменение. И фронт, и спад вызывают прерывание; нужное направление определяют по уровню вывода.
  • Работает и на выходе. Прерывание срабатывает, даже если вывод настроен на выход — так Microchip предлагает получать программное прерывание.
  • Пробуждение. Изменения обнаруживаются асинхронно, поэтому PCINT будит кристалл из любых режимов сна, а не только из Idle.

Механические кнопки на PCINT стоит защищать от дребезга: каждое касание даст серию изменений. У ATtiny25/45/85 то же устроено проще: одно внешнее прерывание INT0 и группа PCINT0…5 с одним вектором (даташит 2586Q). У STM32 похожую задачу решают линии EXTI, но там у каждой линии свой фронт. Распиновка ATmega328P с номерами PCINT — на странице ATmega328P.

Частые вопросы

Как узнать, какой именно вывод вызвал прерывание PCINT?

Аппаратно — никак: на группу выводов один вектор и один флаг (DS40002061B, разд. 13). Наш вывод: в обработчике читают регистр PINx нужного порта и сравнивают с сохранённым прошлым значением — изменившиеся биты и есть источники. Если изменения быстрые, часть их можно пропустить, пока обработчик не успел прочитать порт.

Можно ли выбрать фронт для PCINT?

Нет, прерывание срабатывает при любом изменении уровня — и на фронте, и на спаде. Выбор фронта, спада или низкого уровня есть только у INT0 и INT1 через регистр EICRA (DS40002061B, разд. 13). Нужный фронт определяют программно по текущему уровню вывода.

Разбудит ли PCINT микроконтроллер из Power-down?

Да. Microchip указывает, что изменения на выводах PCINT23…0 обнаруживаются асинхронно, поэтому эти прерывания будят кристалл и из режимов сна глубже Idle, в том числе из Power-down. Для кнопок это удобно, но дребезг контактов даст несколько пробуждений подряд.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B — разд. 13 — внешние прерывания от INT0, INT1 и PCINT23…0, срабатывают и при выводе, настроенном на выход; PCI2 — при изменении любого разрешённого PCINT[23:16], PCI1 — PCINT[14:8], PCI0 — PCINT[7:0]; маски PCMSK2…0; изменения обнаруживаются асинхронно, поэтому будят из любых режимов сна, не только Idle; INT0/INT1 — фронт, спад или низкий уровень (EICRA); 13.2.4 — PCICR: PCIE2…0 разрешают группы; 13.2.5 — PCIFR: флаги PCIF2…0 сбрасываются при входе в обработчик или записью 1; табл. векторов — PCINT0, PCINT1, PCINT2; распиновка — PCINT0…7 на PB0…7, PCINT8…14 на PC0…6, PCINT16…23 на PD0…7
  2. Microchip. ATtiny25/45/85 Data Sheet, 2586Q — разд. 9.1 — один вектор PCINT0 (Pin Change Interrupt Request 0); 9.2 — внешние прерывания от INT0 и PCINT[5:0], маска PCMSK, срабатывают и на выходе

Нашли ошибку?