8L-курс, частина 3 - переривання, exti

Контролер переривань (ITC - InTerrupt Controller) в стмках підтримує вкладені переривання і розділяє їх за пріоритетами. Це означає, що якщо у нас зараз виконується оброблювач якогось не надто важливого переривання (наприклад від таймера), і раптом сталося переривання з більш високим пріоритетом, то переривання від таймера буде перервано і МК негайно кинеться обробляти інше. А якщо пріоритети однакові (або пріоритет нового нижче того, що зараз обробляється) - нове переривання почне оброблятися тільки після завершення попереднього.

А що буде, якщо два переривання виникли одночасно? Або кілька переривань сталися під час обробки більш важливого? А тоді першим піде оброблятися то, у якого вище пріоритет. А якщо і пріоритети однакові? - запитає допитливий читач. Тоді першим буде виконуватися то переривання, яке стоїть ближче до початку в таблиці переривань.

8L-курс, частина 3 - переривання, exti

Всього є 29 векторів переривань + два без номера: RESET - скидання МК, і TRAP - програмне переривання (викликається ассемблерной командою TRAP). Строго кажучи, є два типи пріоритетів - програмний (який ми можемо налаштовувати для кожного переривання) і апаратний - це якраз порядок векторів в таблиці переривань. За табличці видно, що найвищий апаратний пріоритет у переривання FLASH пам'яті, а переривання від I2C взагалі обділена увагою, перебуваючи в самому кінці таблиці. Втім, ніхто не забороняє зробити його найважливішим, виставивши високий програмний пріоритет - він важливіше, ніж апаратний.

Переривання TRAP взагалі плює на все програмні пріоритети. Незалежно від того, який пріоритет має теперішнє переривання, виконання команди TRAP тут-же забирає нас в обробник.

А для інших переривань можна налаштувати пріоритет через регістри ITC_SPR1 - ITC_SPR8.

8L-курс, частина 3 - переривання, exti

Кожному вектору тут виділено по два біти, що дає 4 можливих значення. Але насправді програмних пріоритетів всього 3:

Значення 10, якого немає в списку, відповідає пріоритету основної програми, і встановити його не можна. Та й безглуздо, якщо пригадати як контролер разруливает чергу переривань: переривання з таким-же пріоритетом як у основної програми, ніколи її не перерве.

Після того, як потрібні переривання дозволені і налаштовані, треба глобально вирішити обробку переривань. У STM8 для цього служить асемблерна команда RIM (А для заборони переривань - SIM) Причому працюють вони дуже цікавим чином. Замість установки будь-якого прапора дозволу / заборони переривань (як було наприклад в AVR) тут вони змінюють пріоритет коду, який зараз виконується. Команда RIM виставляє низький пріоритет (10, той самий який не можна виставити для переривань) і тепер будь-яке переривання може перервати виконання програми. А команда SIM ставить поточного коду найвищий пріоритет і ні одне переривання вже не може перешкодити виконанню коду.

У кожного переривання є свій прапор, який позначає, що подія трапилася і пора бігти в обробник. У STM8 ці прапори, як правило, самі не скидаються і їх доводиться опускати вручну. Інакше ми після виходу з обробника знову потрапимо в нього-ж. А що? Прапор піднято, значить треба обробити переривання.

Правда, скидання прапорів в різних випадках організований по-різному. Іноді для цього потрібно скинути прапор самому, іноді - прочитати певний регістр (наприклад в перериванні від АЦП - прочитати результат вимірювання). Далі, при описі різної периферії я буду давати шаблони обробників, і вказувати яким саме чином там скидаються прапорці переривань.

Коли переривання відбувається в режимі WFI, МК переходить в обробник без зайвих затримок. На практиці, час входу в переривання скорочується на 5 тактів. ось:

8L-курс, частина 3 - переривання, exti

Для входу в WFI треба виконати асемблерну команду WFI (Капітан Очевидність активно допомагав писати статтю)

Тепер ядро ​​зупинено до наступного переривання. Тільки не забудьте це саме переривання налаштувати і дозволити, а то ж можна і не прокинутися :)

Ну ось, тепер деяке уявлення про те, як працюють переривання в STM8 у нас є. Плавно переходимо до EXTI - EXT ernal I nterrupts, тобто зовнішнім переривань.

Якщо подивитися на таблицю переривань, видно, що на EXTI виділено 11 векторів.
Три з них мають в назві літеру, підозріло нагадує назва порту (EXTIE / F, EXTIB.). А решта 8 - цифру, не менше підозріло схожу на номер Піна. RM0031 розвіює всі наші сумніви і підтверджує підозри - через те що векторів переривань банально не вистачить на все Піни, система організована дуже хитромудрим чином.

Почнемо її розбирати з самого початку. Для кожного Піна можна індивідуально дозволити або заборонити переривання. Це робиться, якщо пам'ятаєте попередню частину, через регістр Px_CR2, де x - літера порту (перед цим треба налаштувати пін на вхід). Таким чином можна вирішити переривання тільки для потрібних пинов, а решта ніяк не заважатимуть.

Далі, для кожної половини порту (тобто для 4 молодших ніжок і для 4 старших) ми можемо вибрати, куди піде сигнал - на переривання пинов (EXTI0, EXTI1.), Або портів (EXTIB, EXTID.). У першому випадку ми отримуємо індивідуальне переривання для кожного Піна. Але переривання з інших портів будуть приходити сюди-же. У другому випадку у нас буде один обробник для сигналу з усіх чотирьох пінів (якщо ми звичайно дозволимо переривання з усіх), але зате індивідуальний для кожного порту. Все це налаштовується через регістри EXTI_CONF1 і EXTI_CONF2:

Кожен біт тут відповідає старшої або молодшої половині будь-якого порту. Наприклад, PDLIS (Port D Low Interrupt select) відповідає за Піни 0..3 порту D.

Установка біта в одиницю означає, що ця половина порту буде давати одне переривання на все Піни (наприклад EXTID). А нуль соответсвенно означає, що кожен з чотирьох пінів буде давати своє переривання з групи EXTI0..EXTI7.

Крім того, для кожного вектора (саме вектора, а не окремого Піна) можна налаштувати фронт за яким спрацює переривання. Робиться це через регістри EXTI_CR1 - EXTI_CR4.

Кожному перериванню тут відводиться по два біти. Відповідно, є 4 режими роботи переривання:
00 - Спрацьовує по переходу з 1 в 0 (задній фронт) і за низьким рівнем. Тобто обробник буде викликатися постійно, поки піне низький рівень.
01 - За переходу з 0 в 1 (передній фронт)
10 - Знову по задньому фронту, але вже без низького рівня
11 - За обох фронтах. Інакше кажучи, спрацьовує при будь-якій зміні рівня.

Назви для бітів формуються просто. P1IS - Pin 1 Interrupt Sensitivity, PBIS - Port B Interrupt Sensitivity і так далі.

Прапори переривань (про які я вище писав, що їх треба скидати) лежать в регістрах EXTI_SR1 і EXTI_SR2.


По одному біту на вектор. І скидати їх треба записом одиниці (а не нуля, як міг би подумати капітан очевидність).

Ось така ось хитра система. Спочатку може здатися, що вона не дуже зручна в роботі. Але насправді, це незручність проявляється тільки якщо потрібно ловити переривання відразу від цілої купи ніжок (а таке потрібно не часто). У звичайних задачах ніяких особливих незручностей не виникає.

Що стосується STM8S. то там з перериваннями не так заплутано (але і можливостей менше). По суті, є тільки переривання від портів. Як PCINT в AVR. На один вектор сходяться сигнали з кожного з пинов даного порту. Вирішувати переривання можна індивідуально для кожного Піна, а ось фронт налаштовується тільки для кожного вектора - тобто для цілого порту.

Давайте складемо черговий даремний приклад, щоб розібратися як воно працює на практиці. За апаратну основу візьмемо приклад з минулої частини (де був індикатор) і прикрутимо до нього ще одну кнопку. На кожній кнопці буде висіти переривання.
У обробнику першого переривання ми будемо в циклі (так, я дивний) перемикати цифри на індикаторі. А в обробнику другого (якому поставимо більш високий пріоритет) будемо блимати світлодіодом.

Якщо затиснути першу кнопку, МК зависне в обробнику переривання і почне перебирати цифри. Але при натисканні на другу кнопку, перше переривання перерветься більш важливим, і почне блимати світлодіод. Якщо відпустити другу кнопку, то індикатор продовжить перебирати цифри з тієї, на якій зупинився - обробник першого переривання продовжить виконуватися. Просто, наочно.

Для початку потрібно налаштувати Піни. Нехай перша кнопка висить на D6, а друга - на D7 (ці Піни найзручніше підключати до кнопок на Pinboard).

Регістр DDR ми не чіпаємо - він і так обнулений при старті МК.

У регістрі EXTI_CONF1 (який розподіляє сигнали на переривання пинов / портів) при старті теж нулі, значить кожен пін буде давати нам окреме переривання. Так і залишимо.

Ще потрібно налаштувати фронт, по якому спрацює переривання. Кнопки при натисканні замикаються на землю, значить ловити ми повинні задній фронт, якому відповідає значення 2 (10):

І нарешті, треба знизити пріоритет у переривання від першої кнопки (D6), щоб переривання від другої могло переривати обробник першого:

Дивіться в таблицю переривань - вектор EXTI6 у нас йде під номером 14 і йому відповідає група VECT14SPR. Значення 0, нагадаю, означає «середній» пріоритет.

Налаштування індикатора і світлодіода перекочувала з минулих прикладів, тому я її тут наводити не буду - дивіться вихідні.

Залишилося тільки глобально вирішити переривання. Робиться це ассемблерной командою RIM

На цьому з main () закінчили і переходимо до обробникам переривань. У IAR обробник оголошується ось таким хитрим чином:

Щоб кожен раз не лізти в даташит за номером потрібного вектора, можна взяти дефайни з iostm8l151k6.h. В самому кінці файлу прописані всі вектори. Ось шматок для прикладу:

Ой, а чому вектору EXTI6 відповідає номер 0x10 (16), а не 14, як в табличці з даташіта? А просто тут враховуються перші два переривання - RESET і TRAP. А в таблиці вони йдуть без номерів.

А назва обробника - це просто назва функції, і ніяких особливих вимог до нього немає.

Виходить, що обробник переривання EXTI6 ми можемо позначити так:

Любителі лаконічності можуть, якщо захочуть, згорнути це в дефайн:

Тоді переривання будуть позначатися так:

Що, погодьтеся, виглядає приємніше.

Обробники переривань в нашому прикладі будуть майже однакові (за винятком того, що в першому перемикаються цифри, а в другому блимає діод). Тому покажу тільки обробник першої кнопки:

Думаю тут все повинно бути ясно. У другому обработчике ми кліпаємо світлодіодом на D5, до тих пір поки не з'явиться високий рівень на піне D7.

Ось начебто і все щодо зовнішніх переривань.

На останок розповім про цікаву фічу на ім'я Activation Level, яка теж має відношення до переривань.
Досить часто виходить так, що вся програма (ну крім початкової настройки) розкидана по обробникам переривань. А в main красується один жалюгідний нескінченний цикл-затичка. У нас як-раз такий випадок. А якщо наш пристрій живиться від батарей і йому необхідно економити енергію, то між перериваннями воно буде знаходитися в сплячому режимі і виходити з нього тільки для виконання обробників.

Хлопці з STM вирішили полегшити нам життя при побудові подібних алгоритмів і запив біт AL. Знаходиться він в регістрі CFG_GCR (Configiration - General Configuration Register)

Якщо записати в нього одиничку, і піти в сплячий режим, то після того як МК прокинеться по перериванню і виконає обробник, він не потрапить в main () а відразу полетить назад в сплячий режим. Про сплячі режими ми ще нічого толком не знаємо, але спробувати цю фічу хочеться.

Давайте замінимо цикл-заглушку в кінці на таку конструкцію:

HALT - це найглибший режим енергозбереження, при якому відключається тактовий генератор, а пробудження можливе тільки за зовнішнім переривання, переривання від пари інтерфейсів або RTC.

Після запуску поведінку програми ніяк не змінилося, але при цьому ніякого циклу-затримки в кінці main () вже немає і ядро ​​не витрачає енергію даремно. Відразу після завершення обробника, МК йде в сплячий режим.

Ой, а чому вектору EXTI6 відповідає номер 0x10 (16), а не 14, як в табличці з даташіта? А просто тут враховуються перші два переривання - RESET і TRAP. А в таблиці вони йдуть без номерів. коли вперше на це напоровся - дуже сильно матюкається ... У чому криється таємний сенс оного?
Ще вбиває те, що в визначеннях Іара багато вектори названі так, що фіг знайдеш потрібне переривання. Для ADC, наприклад. Простіше самому оголосити, чи просто писати магічну цифру.

Тоді «хто перший встав - того і тапки» Помилкова фраза. а переривання від I2C взагалі обділена увагою Ага, у вашій таблиці його взагалі немає :)
Спочатку може здатися, що вона не дуже зручна в роботі. Мені спочатку здалося, що вона дуже укуренного. Потім теж, втім.

P.S. Треба ще приписати, що б не намагалися це повторити на STM8S-серії :)

Табличку пофиксил (там продовження на іншій сторінці було, я його не захопив відразу), про STM8S написав, навіть тапки прибрав :)

А система виглядає дивно, ага. Схоже вони взяли основу від STM8S і спробували розширити, але зробили це вельми вигадливо. Ну хоча б у нас є інтеррапт з будь-ніжки

Схожі статті