|
Иногда (особенно в случае изучения чужого кода) встает вопрос: как узнать, в каком контексте сейчас выполняется код - в обработчике прерывания (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() |
|