Two things. First, you must use the FromISR variants, xQueueSendFromISR, xSemaphoreGiveFromISR, xTaskNotifyFromISR, etc., because the regular APIs assume task context and can block or manipulate kernel state in ways that are unsafe from an interrupt; calling a non-FromISR API from an ISR can corrupt the kernel. The FromISR calls are non-blocking and take a pxHigherPriorityTaskWoken flag, which you then pass to portYIELD_FROM_ISR() on exit so that if the call unblocked a higher-priority task, a context switch happens immediately rather than waiting for the next tick. Second, and Cortex-M-specific, the ISR's hardware priority matters: any interrupt that calls a FreeRTOS API must run at or below configMAX_SYSCALL_INTERRUPT_PRIORITY (numerically equal or higher, since Cortex-M priority numbers are inverted), because the kernel masks interrupts up to that level to create its critical sections, an ISR above that level could preempt a kernel critical section and corrupt internal state, so such high-priority ISRs must never call kernel APIs (they're useful for ultra-low-latency work that doesn't touch the RTOS). Getting this priority wrong is one of the most common and baffling FreeRTOS bugs, and configASSERT (when enabled) catches many such misuses. So: FromISR APIs, a yield on exit, and correct interrupt-priority configuration.
RTOS & Real-Time Concepts · Interview question
What's special about calling FreeRTOS APIs from an ISR on a Cortex-M?
A strong answer
What a weak answer sounds like
You know the answer. Do you know what gets you dinged?
Pro breaks down the answer most candidates actually give to this question — and the specific reason an interviewer marks it down. It’s the difference between sounding correct and sounding senior, on all 472 questions.
From the lesson
FreeRTOS: A Practical Tour
Tying it together: the FreeRTOS API mapped to every concept (tasks, queues, mutexes, timers, notifications), a worked ISR-to-task app, key FreeRTOSConfig.h knobs, heap schemes, and debugging.