Как определить контекст прерывания? Печать
Добавил(а) microsin   

При использовании FreeRTOS и других ОСРВ часто возникает необходимость определить, в каком контексте выполняется текущий код - из прерывания, или это обычный код потока? Например, существуют разные версии некоторых функций RTOS - некоторые можно вызывать из обычного кода, а некоторые предназначены для вызова из прерывания (например публикация семафора имеет две соответствующие версии API: xSemaphoreGive и xSemaphoreGiveFromISR). Как определить runtime, какую функцию вызывать?

Решить эту задачу поможет проверка поля VECTACTIVE регистра ICSR (Interrupt Control State Register) в секции регистров SCB (System Control Block), эта секция имеется в ядрах процессоров Cortex-M. Ниже на картинке показана структура бит регистра ICSR процессора Cortex-M4. Следует иметь в виду, что все процессоры Cortex-M используют младшие 9 бит регистра ICSR, чтобы можно было правильно определить количество работающих в настоящий момент прерываний (прерывания могут быть вложены друг в друга).

ARM Cortex M SCB ICSR reg

Значение бит VECTACTIVE регистра ICSR говорит нам об активности прерываний, и его можно интерпретировать следующим образом:

0: ни одно из прерываний не активно, текущий контекст выполнения это обычный код (поток или код, вызванный из функции main).
> 0: активно прерывание, текущий код запущен из-под ISR.

Таким образом, нужно просто прочитать эти биты из регистра, и если они больше нуля, то текущий контекст выполнения это обработчик прерывания. Ниже приведен пример кода из статьи [1]:

/**
 * Пример проекта Keil, где определяется выполнение кода из прерывания
 *
 * Перед компиляцией и запуском выберите целевой процессор (target).
 *
 * @author    Tilen Majerle
 * @email     tilen@majerle.eu
 * @website   stm32f4-discovery.net
 * @ide       Keil uVision 5
 * @conf      Параметры PLL установлены в разделе "Options for Target" -> "C/C++" -> "Defines"
 * @packs     Требуется STM32F4xx Keil version 2.4.0 или более новый тулчейн.
 * @stdperiph Требуются стандартные драйверы периферийных устройств STM32F4xx
 *            версии 1.5.0 или более новые.
 */
#include "stm32f4xx.h"
#include "core_cm4.h"
#include "tm_stm32f4_delay.h"
#include "tm_stm32f4_disco.h"
#include "tm_stm32f4_usart.h"
 
// В этой функции происходит проверка контекста выполнения:
void SendString(void)
{
   // Проверка: откуда запущен код: из ISR или нет:
   if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
   {
      /* Мы находимся в прерывании */
      TM_USART_Puts(USART2, "Вызвано из прерывания\n");
   }
   else
   {
      /* Обычный вызов, не из контекста прерывания */
      TM_USART_Puts(USART2, "Обычный контекст, вызов из тела main\n");
   }
}
 
int main(void)
{
   // Инициализация системы:
   SystemInit();
    
   // Инициализация задержки и прерываний каждую 1 миллисекунду:
   TM_DELAY_Init();
    
   // Инициализация USART2, TX на ножке PA2, скорость 921600 бод:
   TM_USART_Init(USART2, TM_USART_PinsPack_1, 921600);
    
   while (1)
   {
      // Вызов функции SendString из обычного потока каждые 500 мс:
      if (TM_DELAY_Time() >= 500)
      {
         // Сброс времени:
         TM_DELAY_SetTime(0);
         SendString();
      }
   }
}
 
// Эта функция будет вызвана, когда TM DELAY
// генерирует прерывание каждую 1 мс:
void TM_DELAY_1msHandler(void)
{
   static uint32_t x;
   // Вызов функции SendString из прерывания каждые 300 мс:
   if (++x >= 300)
   {
      x = 0;
      SendString();
   }
}

Результат работы этой программы:

Вызвано из прерывания
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Вызвано из прерывания
Обычный контекст, вызов из тела main
Вызвано из прерывания
Вызвано из прерывания
..

[Ссылки]

1. Get interrupt execution status on Cortex-M processors site:stm32f4-discovery.net.
2. Safely detect, if function is called from an ISR? site:stackoverflow.com.