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
+123 -106
View File
@@ -9,148 +9,165 @@
#include "InterruptHandler.h" #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) hw_timer_t *timer;
/*****************************************************/
IRAM_ATTR void timerIsr();
IRAM_ATTR void pinLevelChangeIsr(void);
/*****************************************************/
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; if (++count == MS_TO_ISR_COUNT(100uL))
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); count = 0;
} tDecis++;
else
{
digitalWrite(DOUT_PIEP_VCC, LOW);
} }
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)); 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 tshortPiepStartedMs = 0;
static uint32_t lastMics = 0; static bool toneOn = false;
static uint32_t MINIMAL_DT_MICROS_FOR_RECEIVERS = 100u;
uint32_t dtMicros = micros() - lastMics; for (;;)
bool pinFunkVal = digitalRead(DIN_FUNK); {
xSemaphoreTake(beepSemaphore, portMAX_DELAY);
if (lastVal == pinFunkVal) const uint32_t tMillis = millis();
{
return;
}
else
{
lastMics = micros();
lastVal = pinFunkVal; if (bRequestShortBeep)
if (loopCountSincePause < LOOP_COUNTS_FOR_PAUSE) {
{ bRequestShortBeep = false;
/* after receiving a key, wait for the main loop to run x times before activating receiving again */ tshortPiepStartedMs = tMillis;
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); 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);
}
}
} }