Refactor ISR logic into FreeRTOS tasks via semaphore and queue

This commit is contained in:
2026-05-07 09:01:28 +02:00
parent 47631f4de4
commit 54534296f9
+98 -81
View File
@@ -9,62 +9,115 @@
#include "InterruptHandler.h" #include "InterruptHandler.h"
#define LOOP_COUNTS_FOR_PAUSE 2u
#define RF_QUEUE_SIZE 32u
SemaphoreHandle_t beepSemaphore;
#define LOOP_COUNTS_FOR_PAUSE (2u) QueueHandle_t rfEventQueue;
/*****************************************************/
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) 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); attachInterrupt(digitalPinToInterrupt(DIN_FUNK), pinLevelChangeIsr, CHANGE);
#if 1 timer = timerBegin(0, 80, true); /* prescaler 80 → 1 MHz */
timer = timerBegin(0, 80, true); /* 80 -> 1 Mhz */
timerAttachInterrupt(timer, &timerIsr, true); timerAttachInterrupt(timer, &timerIsr, true);
timerAlarmWrite(timer, ISR_TIME_US, true); timerAlarmWrite(timer, ISR_TIME_US, true);
timerAlarmEnable(timer); timerAlarmEnable(timer);
#endif
} }
/** /**
* called every 10 ms * Called every ISR_TIME_US (10 ms).
* * Kept minimal: advance tDecis and wake beepTask via semaphore.
* used to update tDecis, and for beeping
*/ */
IRAM_ATTR void timerIsr() static IRAM_ATTR void timerIsr(void)
{ {
static uint32_t tshortPiepStartedMs = 0;
static uint8_t count = 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)) if (++count == MS_TO_ISR_COUNT(100uL))
{ {
/* increment tDecis every 100ms */
count = 0; count = 0;
tDecis++; tDecis++;
} }
BaseType_t woken = pdFALSE;
xSemaphoreGiveFromISR(beepSemaphore, &woken);
portYIELD_FROM_ISR(woken);
}
/**
* Called on every 433 MHz pin edge.
* Kept minimal: measure pulse width and push to rfEventQueue for rfDecodeTask.
*/
static IRAM_ATTR void pinLevelChangeIsr(void)
{
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()
{
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.
*/
static void beepTask(void *pvParameters)
{
static uint32_t tshortPiepStartedMs = 0;
static bool toneOn = false;
for (;;)
{
xSemaphoreTake(beepSemaphore, portMAX_DELAY);
const uint32_t tMillis = millis();
if (bRequestShortBeep) if (bRequestShortBeep)
{ {
@@ -74,9 +127,9 @@ IRAM_ATTR void timerIsr()
if (tshortPiepStartedMs > 0u) if (tshortPiepStartedMs > 0u)
{ {
if (tMillis < (tshortPiepStartedMs + 30uL)) if (tMillis < tshortPiepStartedMs + 30u)
{ {
if (false == toneOn) if (!toneOn)
{ {
TONE_REQUEST_ON(TONE_REQUEST_SOURCE_SHORTPIEP); TONE_REQUEST_ON(TONE_REQUEST_SOURCE_SHORTPIEP);
toneOn = true; toneOn = true;
@@ -94,63 +147,27 @@ IRAM_ATTR void timerIsr()
piepPattern->TaskCyclic(); piepPattern->TaskCyclic();
} }
if (toneRequest != 0u) digitalWrite(DOUT_PIEP_VCC, toneRequest != 0u ? HIGH : LOW);
{
digitalWrite(DOUT_PIEP_VCC, HIGH);
} }
else
{
digitalWrite(DOUT_PIEP_VCC, LOW);
}
}
IRAM_ATTR void InterruptHandler_Stop(void)
{
timerAlarmDisable(timer);
detachInterrupt(digitalPinToInterrupt(DIN_FUNK));
}
IRAM_ATTR void InterruptHandler_PauseReceive()
{
loopCountSincePause = 0u;
} }
/** /**
* * 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.
*/ */
IRAM_ATTR void pinLevelChangeIsr(void) static void rfDecodeTask(void *pvParameters)
{ {
static bool lastVal = LOW; RfEvent_t ev;
static uint32_t lastMics = 0; for (;;)
static uint32_t MINIMAL_DT_MICROS_FOR_RECEIVERS = 100u;
uint32_t dtMicros = micros() - lastMics;
bool pinFunkVal = digitalRead(DIN_FUNK);
if (lastVal == pinFunkVal)
{ {
return; if (xQueueReceive(rfEventQueue, &ev, portMAX_DELAY) == pdTRUE)
}
else
{ {
lastMics = micros(); receiverSw.Isr(ev.dtMicros, ev.pinValue);
receiverAiggend.Isr(ev.dtMicros, ev.pinValue);
lastVal = pinFunkVal; receiverOval.Isr(ev.dtMicros, ev.pinValue);
if (loopCountSincePause < LOOP_COUNTS_FOR_PAUSE) receiverKerui.Isr(ev.dtMicros, ev.pinValue);
{ receiverThermo.Isr(ev.dtMicros, ev.pinValue);
/* 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);
//digitalWrite(LED_BUILTIN, pinFunkVal);
} }
} }
} }