6. STM32. Програмування STM32F103. NVIC


19.08.2016

NVIC (Nested vectored interrupt controller) - модуль контролю переривань. Він виконує наступні функції:

  • дозволяє/забороняє переривання
  • назначає пріоритет переривань (від 0 до 15. 0 - максимальній пріоритет, 15 - мінімальний пріоритет)
  • автоматично зберігає дані при виконанні одиноких чи вкладених переривань.
Цей контролер керує усіма перериваннями: і зовнішніми і перериваннями модулів самого контролера, і, як Ви вже зрозуміли, у STM32 є пріоритети переривань. А це значить, що переривання з більшим пріоритетом може перервати виконання обробки переривання з меншим пріоритетом.

Існують переривання пріоритет яких змінити неможливо:

Номер Переривання Пріоритет Опис
1 Reset_Handler -3(найвищий) Reset
2 NMI_Handler -2 NMI переривання
3 HardFault_Handler -1 Аварійні стани
Існує поняття PriorityGroup. Для встановлення групи пріоритетів використовується функція NVIC_PriorityGroupConfig. Можна встановити одну з 5 груп. Можливі комбінації пріоритетів та суб-пріоритетів (NVIC_IRQChannelPreemptionPriority та NVIC_IRQChannelSubPriority) при обраній PriorityGroup вказані у наступній таблиці:
 The table below gives the allowed values of the pre-emption priority and subpriority according
 to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function
  ============================================================================================================================
    NVIC_PriorityGroup   | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority  | Description
  ============================================================================================================================
   NVIC_PriorityGroup_0  |                0                  |            0-15             |   0 bits for pre-emption priority
                         |                                   |                             |   4 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------
   NVIC_PriorityGroup_1  |                0-1                |            0-7              |   1 bits for pre-emption priority
                         |                                   |                             |   3 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_2  |                0-3                |            0-3              |   2 bits for pre-emption priority
                         |                                   |                             |   2 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_3  |                0-7                |            0-1              |   3 bits for pre-emption priority
                         |                                   |                             |   1 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_4  |                0-15               |            0                |   4 bits for pre-emption priority
                         |                                   |                             |   0 bits for subpriority                       
  ============================================================================================================================
В залежності від того, яка група встановлена, буде доступна різна кількість пріоритетів та субпріоритетів.

Пріоритети дозволяють реалізувати механізм вкладених переривань, тоді як субпріоритет визначають послідовність обробки переривань з однаковим пріоритетом при одночасному надходженні.

Тобто, якщо ми встановили NVIC_PriorityGroup_0, згідно таблиці ми можемо встановити перериванням лише один пріоритет NVIC_IRQChannelPreemptionPriority = 0 та назначити різним перериванням субпріоритет NVIC_IRQChannelSubPriority від 0 до 15. А це означає що обробка переривання не буде перериватися іншим перериванням, оскільки у всіх однаковий пріоритет. А субпріоритет NVIC_IRQChannelSubPriority визначає тільки послідовність обробки при одночасному надходженні переривань.

Якщо встановимо NVIC_PriorityGroup_1, ми можемо одним перериванням (більш пріоритетним) встановити NVIC_IRQChannelPreemptionPriority = 0, а менш пріоритетним NVIC_IRQChannelPreemptionPriority = 1. До того ж усім перериванням ми можемо встановити субпріоритет від 0 до 7, який визначає порядок обробки при одночасному надходженні переривань з однаковим пріоритетом. У цьому випадку переривання з пріоритетом 0 (найвищим) зможуть переривати обробку переривання з пріоритетом 1. А якщо одночасно виникнуть переривання з однаковим пріоритетом, то першим буде оброблено те, у якого вищий субпріоритет. У попередній статті ми виконували налаштування переривання USART1_IRQn наступним чином:


NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

Як бачимо, ми встановили


NVIC_IRQChannelPreemptionPriority = 0;
NVIC_IRQChannelSubPriority = 0;

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

Давайте роздивимось приклад налаштування двох переривань з однаковими пріоритетами і різними субпріоритетами:


NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

У цьому прикладі ми встановили NVIC_IRQChannelSubPriority = 1; для EXTI0_IRQn. А це означає, що коли EXTI0_IRQn та USART1_IRQn стануться одночасно, першим буде оброблено USART1_IRQn. Та жодне з цих переривань не зможе перервати обробку іншого, бо в них однаковий пріоритет NVIC_IRQChannelPreemptionPriority=0;

Тепер розглянемо приклад, коли EXTI0_IRQn і EXTI1_IRQn мають більший пріоритет і мають право перервати обробку USART1_IRQn:


/* Set NVIC Priority Group */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

Ми встановили NVIC_PriorityGroup_1. Згідно вище наведеної таблиці ми можемо встановити два пріоритети. В даному випадку різні субпріоритети логічно встановлювати лише для переривань з однаковим пріоритетом. Що ми і зробили для EXTI0_IRQn та EXTI1_IRQn. Тепер EXTI0_IRQn або EXTI1_IRQn можуть перервати обробку USART1_IRQn. Але не можуть перервати обробку один одного бо в них однаковий пріоритет. А якщо EXTI0_IRQn та EXTI1_IRQn виникнуть одночасно, тоді першим буде оброблений EXTI0_IRQn - в нього вищий субпріоритет.

Приблизно так працюють пріоритети переривань у STM32.

Якщо ви вважаєте що одночасне надходження переривань річ дуже малоймовірна, я Вас розчарую: це стається частіше, ніж Ви могли собі уявити. Коли кажуть про одночасне надходження переривання, мається на увазі не людське сприйняття одночасності, як ми собі його уявляємо, а те, як це сприймає мікроконтролер. Для мікроконтролера одночасне надходження стається досить часто. Наприклад, ви у програмі заборонили обробку переривань і Ваша програма щось досить важливе досить довго робила, а за цей час надійшло 3 різних переривання. І ось ви дозволяєте обробку переривань. Мікроконтролер кидається обробляти їх, а там стирчить 3 прапорці від переривань. Для мікроконтролера це одночасне надходження. Така сама ситуація може статися коли іде обробка переривання і за цей час надходить декілька інших перериваннь з таким самим або меншим пріоритетом. Після завершення обробки мікроконтролер бачить декілька перериваннь які треба обробити і для нього це одночасне надходження переривань.

P.S. Ще через NVIC можна задавати адресу таблиці векторів переривань. Дивись функцію NVIC_SetVectorTable. Це нам знадобиться значно пізніше, якщо ми доберемося до свого bootloader-a.

Пам`ятка

Всі переривання і їх номери описані у файлі stm32f10x.h. Шукай typedef enum IRQn.

Бажаю успіхів!

Дивись також:

STM32
Коментарі:
Den говорить:
15.01.2021 15:05
Очень ясно и доходчиво) спасибо) 

Степан говорить:
08.11.2023 13:13
Велике Дякую. Нарешті розібрався.

Додати коментар
Code
* - обов'язкові поля

Архіви