From 54534296f9dfa93da0040fa0b58d5c3dd35e7c79 Mon Sep 17 00:00:00 2001 From: Flokke Date: Thu, 7 May 2026 09:01:28 +0200 Subject: [PATCH] Refactor ISR logic into FreeRTOS tasks via semaphore and queue --- src/InterruptHandler.cpp | 233 +++++++++++++++++++++------------------ 1 file changed, 125 insertions(+), 108 deletions(-) diff --git a/src/InterruptHandler.cpp b/src/InterruptHandler.cpp index 3858f98..594ac04 100644 --- a/src/InterruptHandler.cpp +++ b/src/InterruptHandler.cpp @@ -9,148 +9,165 @@ #include "InterruptHandler.h" +#define LOOP_COUNTS_FOR_PAUSE 2u +#define RF_QUEUE_SIZE 32u +SemaphoreHandle_t beepSemaphore; +QueueHandle_t rfEventQueue; -#define LOOP_COUNTS_FOR_PAUSE (2u) -/*****************************************************/ - -IRAM_ATTR void timerIsr(); -IRAM_ATTR void pinLevelChangeIsr(void); - -/*****************************************************/ - -hw_timer_t * timer; +hw_timer_t *timer; +static IRAM_ATTR void timerIsr(void); +static IRAM_ATTR void pinLevelChangeIsr(void); +static void beepTask(void *pvParameters); +static void rfDecodeTask(void *pvParameters); /** * */ void InterruptHandler_Init(void) { - /* setup interrupt */ + beepSemaphore = xSemaphoreCreateBinary(); + rfEventQueue = xQueueCreate(RF_QUEUE_SIZE, sizeof(RfEvent_t)); + + /* beepTask: replaces beep logic that was in timerIsr — high priority so it responds within one tick */ + xTaskCreatePinnedToCore(beepTask, "beepTask", 2048, NULL, 5, NULL, 1); + /* rfDecodeTask: replaces receiver ISR calls that were in pinLevelChangeIsr */ + xTaskCreatePinnedToCore(rfDecodeTask, "rfTask", 3072, NULL, 4, NULL, 1); + attachInterrupt(digitalPinToInterrupt(DIN_FUNK), pinLevelChangeIsr, CHANGE); -#if 1 - timer = timerBegin(0, 80, true); /* 80 -> 1 Mhz */ + timer = timerBegin(0, 80, true); /* prescaler 80 → 1 MHz */ timerAttachInterrupt(timer, &timerIsr, true); timerAlarmWrite(timer, ISR_TIME_US, true); timerAlarmEnable(timer); -#endif - } - /** - * called every 10 ms - * - * used to update tDecis, and for beeping + * Called every ISR_TIME_US (10 ms). + * Kept minimal: advance tDecis and wake beepTask via semaphore. */ -IRAM_ATTR void timerIsr() +static IRAM_ATTR void timerIsr(void) { - static uint32_t tshortPiepStartedMs = 0; - static uint8_t count = 0; - static boolean toneOn = false; - uint32_t tMillis = millis(); - -#if 0 - static bool on = true; - if (on) digitalWrite(DOUT_TEST_TIMER, HIGH); - else digitalWrite(DOUT_TEST_TIMER, LOW); - on = !on; -#endif - - if (++count == MS_TO_ISR_COUNT(100uL) ) - { - /* increment tDecis every 100ms */ - count = 0; - tDecis++; - } - - if (bRequestShortBeep) - { - bRequestShortBeep = false; - tshortPiepStartedMs = tMillis; - } - - if (tshortPiepStartedMs > 0u) - { - if (tMillis < (tshortPiepStartedMs + 30uL)) - { - if (false == toneOn) - { - TONE_REQUEST_ON(TONE_REQUEST_SOURCE_SHORTPIEP); - toneOn = true; - } - } - else - { - TONE_REQUEST_OFF(TONE_REQUEST_SOURCE_SHORTPIEP); - tshortPiepStartedMs = 0; - toneOn = false; - } - } - else - { - piepPattern->TaskCyclic(); - } - - if (toneRequest != 0u) - { - digitalWrite(DOUT_PIEP_VCC, HIGH); - } - else - { - digitalWrite(DOUT_PIEP_VCC, LOW); - } + static uint8_t count = 0; + if (++count == MS_TO_ISR_COUNT(100uL)) + { + count = 0; + tDecis++; + } + BaseType_t woken = pdFALSE; + xSemaphoreGiveFromISR(beepSemaphore, &woken); + portYIELD_FROM_ISR(woken); } -IRAM_ATTR void InterruptHandler_Stop(void) +/** + * Called on every 433 MHz pin edge. + * Kept minimal: measure pulse width and push to rfEventQueue for rfDecodeTask. + */ +static IRAM_ATTR void pinLevelChangeIsr(void) { - timerAlarmDisable(timer); + static bool lastVal = LOW; + static uint32_t lastMics = 0; + + const uint32_t now = micros(); + const uint32_t dtMicros = now - lastMics; + const bool pinVal = digitalRead(DIN_FUNK); + + if (lastVal == pinVal) + return; + + lastMics = now; + lastVal = pinVal; + + if (loopCountSincePause < LOOP_COUNTS_FOR_PAUSE || dtMicros < 100u) + return; + + RfEvent_t ev = {dtMicros, pinVal}; + BaseType_t woken = pdFALSE; + xQueueSendFromISR(rfEventQueue, &ev, &woken); + portYIELD_FROM_ISR(woken); +} + +IRAM_ATTR void InterruptHandler_Stop(void) +{ + timerAlarmDisable(timer); detachInterrupt(digitalPinToInterrupt(DIN_FUNK)); } - -IRAM_ATTR void InterruptHandler_PauseReceive() +IRAM_ATTR void InterruptHandler_PauseReceive() { - loopCountSincePause = 0u; + loopCountSincePause = 0u; } /** + * Woken every 10 ms by timerIsr via beepSemaphore. + * Handles short-beep timing and the PiepPattern state machine, + * then drives the buzzer supply pin — work that was previously in timerIsr. * + * Both tasks (beepTask and loop) run on Core 1, so they interleave only at + * scheduling points and there is no true parallel access to piepPattern or + * bRequestShortBeep. */ -IRAM_ATTR void pinLevelChangeIsr(void) +static void beepTask(void *pvParameters) { - static bool lastVal = LOW; - static uint32_t lastMics = 0; - static uint32_t MINIMAL_DT_MICROS_FOR_RECEIVERS = 100u; + static uint32_t tshortPiepStartedMs = 0; + static bool toneOn = false; - uint32_t dtMicros = micros() - lastMics; - bool pinFunkVal = digitalRead(DIN_FUNK); + for (;;) + { + xSemaphoreTake(beepSemaphore, portMAX_DELAY); - if (lastVal == pinFunkVal) - { - return; - } - else - { - lastMics = micros(); + const uint32_t tMillis = millis(); - lastVal = pinFunkVal; - if (loopCountSincePause < LOOP_COUNTS_FOR_PAUSE) - { - /* after receiving a key, wait for the main loop to run x times before activating receiving again */ - return; - } - else if (dtMicros > MINIMAL_DT_MICROS_FOR_RECEIVERS) - { - receiverSw.Isr(dtMicros, pinFunkVal); - receiverAiggend.Isr(dtMicros, pinFunkVal); - receiverOval.Isr(dtMicros, pinFunkVal); - receiverKerui.Isr(dtMicros, pinFunkVal); - receiverThermo.Isr(dtMicros, pinFunkVal); + if (bRequestShortBeep) + { + bRequestShortBeep = false; + tshortPiepStartedMs = tMillis; + } - //digitalWrite(LED_BUILTIN, pinFunkVal); - } - } + if (tshortPiepStartedMs > 0u) + { + if (tMillis < tshortPiepStartedMs + 30u) + { + if (!toneOn) + { + TONE_REQUEST_ON(TONE_REQUEST_SOURCE_SHORTPIEP); + toneOn = true; + } + } + else + { + TONE_REQUEST_OFF(TONE_REQUEST_SOURCE_SHORTPIEP); + tshortPiepStartedMs = 0; + toneOn = false; + } + } + else + { + piepPattern->TaskCyclic(); + } + + digitalWrite(DOUT_PIEP_VCC, toneRequest != 0u ? HIGH : LOW); + } +} + +/** + * Drains rfEventQueue and runs all receiver state machines. + * Runs on Core 1 at priority 4 (above loop priority 1), so it pre-empts + * the loop task when RF edges arrive but never runs in true parallel with it. + */ +static void rfDecodeTask(void *pvParameters) +{ + RfEvent_t ev; + for (;;) + { + if (xQueueReceive(rfEventQueue, &ev, portMAX_DELAY) == pdTRUE) + { + receiverSw.Isr(ev.dtMicros, ev.pinValue); + receiverAiggend.Isr(ev.dtMicros, ev.pinValue); + receiverOval.Isr(ev.dtMicros, ev.pinValue); + receiverKerui.Isr(ev.dtMicros, ev.pinValue); + receiverThermo.Isr(ev.dtMicros, ev.pinValue); + } + } }