ESP-IDF: основные методы определения контекста Печать
Добавил(а) microsin   

Иногда (особенно в случае изучения чужого кода) встает вопрос: как узнать, в каком контексте сейчас выполняется код - в обработчике прерывания (ISR) или просто в задаче?

В ESP-IDF есть несколько способов определить контекст выполнения кода:

1. FreeRTOS-функция xPortInIsrContext().

#include "freertos/FreeRTOS.h"

if (xPortInIsrContext())
{
// Код выполняется в прерывании (ISR)
... }
else
{
// Код выполняется в задаче
printf("Выполняется в задаче\n"); }

Для отладки можно создать макрос:

#define DEBUG_CONTEXT() do { \
if (xPortInIsrContext()) \
printf("[ISR] %s:%d\n", __FILE__, __LINE__); \
else \
printf("[TASK] %s:%d\n", __FILE__, __LINE__); \
} while(0)

2. Проверка состояния кода через xTaskGetSchedulerState().

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
// Код выполняется до запуска планировщика
printf("До запуска планировщика\n"); } else if (xPortInIsrContext()) {
// В прерывании
... } else {
// В задаче
printf("В задаче\n"); }

3. Определение типа обработчика прерывания.

// В самом обработчике прерывания - всегда ISR контекст.
// Функции обработчиков прерывания должны определяться
// с атрибутом IRAM_ATTR (не должны находиться в FLASH).
void IRAM_ATTR my_interrupt_handler(void* arg) {
// Этот код всегда выполняется в ISR контексте
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; // Отправка данных в задачу из ISR
xQueueSendFromISR(queue, &data, &xHigherPriorityTaskWoken);

if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
} }

[Практические примеры использования]

Пример 1: Универсальная функция для разных контекстов

void safe_send_to_queue(void* data) {
if (xPortInIsrContext()) {
// Вызов из прерывания - используем FromISR версию
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(my_queue, data, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
} else {
// Вызов из задачи - обычная версия
xQueueSend(my_queue, data, portMAX_DELAY);
} }

Пример 2: Безопасное использование мьютексов

void critical_operation(void) {
if (xPortInIsrContext()) {
// В ISR нельзя использовать мьютексы - используем атомарные операции
uint32_t old_int_status = portSET_INTERRUPT_MASK_FROM_ISR();
// Критическая секция
portCLEAR_INTERRUPT_MASK_FROM_ISR(old_int_status);
} else {
// В задаче - обычный мьютекс
xSemaphoreTake(my_mutex, portMAX_DELAY);
// Критическая секция
xSemaphoreGive(my_mutex);
} }

Таблица различий контекстов:

Операция ISR контекст Контекст задачи
Функции очередей xQueueSendFromISR() xQueueSend()
Семафоры xSemaphoreGiveFromISR() xSemaphoreGive()
Задержки Нельзя использовать vTaskDelay() Можно использовать vTaskDelay()
Мьютексы Запрещены Разрешены
Вызов yield portYIELD_FROM_ISR() taskYIELD()