/* Producer–Consumer, ISR VARIANT — deferring work out of an interrupt.
* Wokwi, ST Nucleo-C031C6. Library Manager -> + Add -> "STM32duino FreeRTOS".
*
* This extends the SENSOR->FILTER->ALERT lab: same queue "workhorse," but now the PRODUCER
* is an interrupt. An ISR must return FAST, so it captures the event and DEFERS the heavy
* work to a task, using the three-part ISR handshake:
* xQueueSendFromISR(q, &item, &xHigherPriorityTaskWoken);
* portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
* This one example ties together interrupts + queues + the scheduler.
*
* WHY WE "FAKE" THE TRIGGER: Wokwi's STM32 sim doesn't reliably fire a real GPIO/timer IRQ,
* so a LOW-priority "IsrSource" task stands in for the hardware and calls SimulatedISR().
* The BODY of SimulatedISR() is EXACTLY what your real EXTI/TIM handler contains on the F767
* — and there you MUST use the ...FromISR() variants because it runs in interrupt context.
*/
#include <STM32FreeRTOS.h>
typedef struct { uint32_t seq; uint32_t t_ms; } Event_t;
QueueHandle_t evtQ;
/* ---- WORKER: does the HEAVY work in TASK context (never in the ISR). HIGH priority. ---- */
static void WorkerTask(void *pv) {
(void)pv; Event_t e;
for (;;) {
if (xQueueReceive(evtQ, &e, portMAX_DELAY) == pdTRUE) {
Serial.print(" [WORKER] woke -> processing seq="); Serial.println(e.seq);
vTaskDelay(pdMS_TO_TICKS(120)); // pretend this is slow work (unsafe in an ISR)
Serial.print(" [WORKER] done seq="); Serial.println(e.seq);
}
}
}
/* ---- THE ISR BODY: short. capture -> defer -> yield. This IS your real IRQ handler. ---- */
static void SimulatedISR(uint32_t seq) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; // 1) must start pdFALSE
Event_t e = { seq, (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS) };
xQueueSendFromISR(evtQ, &e, &xHigherPriorityTaskWoken); // 2) FromISR variant (not xQueueSend!)
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); // 3) if a higher-prio task woke, switch NOW
}
/* ---- FAKE TRIGGER: stands in for the hardware firing the IRQ. LOW priority on purpose,
* so the woken WORKER (higher priority) visibly preempts it via the yield. ---- */
static void IsrSourceTask(void *pv) {
(void)pv; uint32_t seq = 0;
for (;;) {
vTaskDelay(pdMS_TO_TICKS(1000)); // ~1 Hz "interrupt"
seq++;
Serial.print("[IRQ] fired seq="); Serial.print(seq);
Serial.println(" (ISR: capture + defer, returns fast)");
SimulatedISR(seq); // <-- on real HW: EXTIx_IRQHandler() / TIMx_IRQHandler()
Serial.print("[IRQ] handler returned seq="); Serial.println(seq);
}
}
void setup() {
Serial.begin(115200);
evtQ = xQueueCreate(8, sizeof(Event_t));
xTaskCreate(WorkerTask, "WORKER", 256, NULL, 3, NULL); // HIGH (the deferred work)
xTaskCreate(IsrSourceTask, "IRQSRC", 256, NULL, 1, NULL); // LOW (the interrupted context)
vTaskStartScheduler();
}
void loop() {}
/* ================= WHAT THIS BECOMES ON REAL HARDWARE (F767) =================
* Delete IsrSourceTask + SimulatedISR. Configure the pin's EXTI line, then:
*
* void EXTI15_10_IRQHandler(void) {
* BaseType_t xHigherPriorityTaskWoken = pdFALSE;
* Event_t e = { g_seq++, HAL_GetTick() };
* xQueueSendFromISR(evtQ, &e, &xHigherPriorityTaskWoken);
* __HAL_GPIO_EXTI_CLEAR_IT(BTN_PIN); // clear the interrupt flag
* portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
* }
*
* Same three lines of handshake — only the trigger changes from a fake task to the NVIC. */
Loading
st-nucleo-c031c6
st-nucleo-c031c6