1. Тема дня

Разбираем:

text
interrupt latency
приоритеты NVIC на STM32
configMAX_SYSCALL_INTERRUPT_PRIORITY
ESP32 interrupt levels
IRAM-safe ISR
ISR-safe FreeRTOS API
deferred interrupt processing
DMA half/full callbacks
измерение времени ISR

Главная мысль: ISR должна быстро зафиксировать аппаратное событие, очистить источник прерывания, передать минимальную информацию задаче и завершиться. Парсинг, фильтрация, recovery, логирование и работа с сетью выполняются не в ISR.

2. Зачем это нужно

EC25 UART

UART/DMA interrupt не должен разбирать +QMTRECV, выполнять strstr(), отправлять MQTT или перезапускать EC25. Правильный поток:

text
UART/DMA interrupt
→ зафиксировать позицию DMA/ring buffer
→ уведомить modem_task
→ modem_task забирает байты
→ AT parser
→ modem state machine

GPIO фаз

GPIO edge:

text
GPIO edge
→ timestamp + raw level
→ input_task
→ debounce / AC-window / confidence
→ phase_detector
→ phase_event

DMA callbacks

DMA half/full callbacks говорят, что половина буфера готова. Фильтрация ADC, CRC, UART parsing должны быть в task.

3. Теория

Верхняя и нижняя половина

ISR:

text
read status
clear flag
save minimal data
notify task
exit

Task:

text
parse UART
debounce
filter ADC
CRC
state machine
recovery
logs

NVIC priority

На Cortex-M меньшее число означает более высокий приоритет. Priority 0 - самый высокий. configMAX_SYSCALL_INTERRUPT_PRIORITY разделяет ISR:

text
0..4:
  FreeRTOS API нельзя
5..15:
  можно ...FromISR()

Пример при 4 priority bits:

text
UART DMA: priority 6 → можно RTOS API
ADC DMA: priority 7 → можно RTOS API
FDCAN: priority 8 → можно RTOS API
точный capture: priority 3 → RTOS API нельзя

Только ...FromISR()

Из ISR нельзя вызывать обычные:

c
xQueueSend();
xSemaphoreGive();
xTaskNotify();
vTaskDelay();

Нужно:

c
xQueueSendFromISR();
xSemaphoreGiveFromISR();
xTaskNotifyFromISR();
vTaskNotifyGiveFromISR();

Если разблокирована более приоритетная задача, нужен portYIELD_FROM_ISR().

ESP32 interrupts

ESP32 имеет interrupt levels и core affinity. Внешнее прерывание выделяется на том core, где вызвана allocation-функция. Выделять interrupt лучше из pinned task. ESP_INTR_FLAG_IRAM требует, чтобы вся цепочка ISR-кода и используемые данные были во внутренней памяти, а не только первая функция с IRAM_ATTR.

4. Типичные ошибки

  • Все IRQ имеют priority 0.
  • Обычный RTOS API вызывается из ISR.
  • Не вызывается portYIELD_FROM_ISR().
  • Парсинг EC25 выполняется в UART callback.
  • printf или ESP_LOGI в ISR.
  • ESP_INTR_FLAG_IRAM поставлен без полной IRAM-safe цепочки.
  • Interrupt выделен из migratable ESP32 task.
  • Shared ISR не проверяет status.
  • Event bit используется как счётчик.
  • DMA callback обрабатывает весь буфер.

5. Практическое задание

Создай INTERRUPT_POLICY.md.

markdown
# Interrupt policy
## Main rule
ISR acknowledges hardware, records minimal data,
notifies the owner task and exits.
## Forbidden in ISR
- blocking calls
- delays
- printf/full logging
- MQTT/TCP operations
- NVS/Flash writes
- modem recovery
- large CRC/parsing
- mutex waits
- malloc/free unless explicitly proven ISR-safe

Добавь таблицу IRQ:

text
UART EC25 RX/DMA: priority 6, calls RTOS, owner modem_task
GPIO phase: priority 9, owner input_task
ADC DMA: priority 7, owner adc_task
FDCAN RX: priority 8, owner can_task
Timer capture: priority 3, no RTOS API

6. Что попробовать дальше

  • Измерить latency через debug GPIO.
  • Добавить irq stats CLI.
  • Проверить STM32 configPRIO_BITS == __NVIC_PRIO_BITS.
  • Проверить, что все IRQ с RTOS API имеют priority ниже/равно разрешённой границе.

Задание

Объясните условия примера 0..4 forbidden и 5..15 allowed. Как проверить соответствие для другой конфигурации Cortex-M?

Критерии самопроверки: Отделить logical library priority numbers от shifted register encoding; меньший numerical priority означает большую urgency. Это условный пример, не универсальная IRQ map.

Показать ответ автора

Пример предполагает 4 реализованных priority bits и настроенный library syscall threshold 5. Проверить implemented priority bits, priority grouping, FreeRTOS port и encoded configMAX_SYSCALL_INTERRUPT_PRIORITY; применять фактическую границу платформы, а не копировать пример универсально.

Задание

UART callback разбирает большой response и пишет log. Перепроектируйте путь для ограниченной ISR latency без незаметной потери bytes.

Критерии самопроверки: Ограничить ISR work; выбрать ...FromISR() там, где разрешено, и объяснить yield decision и buffer-lifetime contract.

Показать ответ автора

ISR фиксирует готовую область или position, очищает source и уведомляет owning task через ISR-safe primitive. Task владеет parsing и logging; задать buffer ownership и overflow detection, чтобы задержка обработки наблюдалась.