irq handling reworked (using tasknotify instead of semaphores)
This commit is contained in:
parent
6e47a60f32
commit
30d22aa896
@ -1,22 +1,12 @@
|
||||
#ifdef HAS_BUTTON
|
||||
|
||||
#include "globals.h"
|
||||
#include "button.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
portMUX_TYPE mutexButton = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void IRAM_ATTR ButtonIRQ() {
|
||||
portENTER_CRITICAL(&mutexButton);
|
||||
ButtonPressedIRQ++;
|
||||
portEXIT_CRITICAL(&mutexButton);
|
||||
}
|
||||
|
||||
void readButton() {
|
||||
portENTER_CRITICAL(&mutexButton);
|
||||
ButtonPressedIRQ = 0;
|
||||
portEXIT_CRITICAL(&mutexButton);
|
||||
ESP_LOGI(TAG, "Button pressed");
|
||||
payload.reset();
|
||||
payload.addButton(0x01);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "senddata.h"
|
||||
|
||||
void IRAM_ATTR ButtonIRQ(void);
|
||||
void readButton(void);
|
||||
void IRAM_ATTR ButtonIRQ();
|
||||
void readButton();
|
||||
|
||||
#endif
|
@ -7,15 +7,9 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
portMUX_TYPE mutexHomeCycle = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// do all housekeeping
|
||||
void doHousekeeping() {
|
||||
|
||||
portENTER_CRITICAL(&mutexHomeCycle);
|
||||
HomeCycleIRQ = 0;
|
||||
portEXIT_CRITICAL(&mutexHomeCycle);
|
||||
|
||||
// update uptime counter
|
||||
uptime();
|
||||
|
||||
@ -26,8 +20,8 @@ void doHousekeeping() {
|
||||
// task storage debugging //
|
||||
ESP_LOGD(TAG, "Wifiloop %d bytes left",
|
||||
uxTaskGetStackHighWaterMark(wifiSwitchTask));
|
||||
ESP_LOGD(TAG, "Stateloop %d bytes left",
|
||||
uxTaskGetStackHighWaterMark(stateMachineTask));
|
||||
ESP_LOGD(TAG, "IRQhandler %d bytes left",
|
||||
uxTaskGetStackHighWaterMark(irqHandlerTask));
|
||||
#ifdef HAS_GPS
|
||||
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
||||
#endif
|
||||
@ -70,12 +64,6 @@ void doHousekeeping() {
|
||||
}
|
||||
} // doHousekeeping()
|
||||
|
||||
void IRAM_ATTR homeCycleIRQ() {
|
||||
portENTER_CRITICAL(&mutexHomeCycle);
|
||||
HomeCycleIRQ++;
|
||||
portEXIT_CRITICAL(&mutexHomeCycle);
|
||||
}
|
||||
|
||||
// uptime counter 64bit to prevent millis() rollover after 49 days
|
||||
uint64_t uptime() {
|
||||
static uint32_t low32, high32;
|
||||
|
@ -15,10 +15,6 @@ const char lora_datarate[] = {"100908078CNA121110090807"};
|
||||
|
||||
uint8_t volatile DisplayState = 0;
|
||||
|
||||
hw_timer_t *displaytimer;
|
||||
|
||||
portMUX_TYPE mutexDisplay = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// helper function, prints a hex key on display
|
||||
void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
|
||||
const uint8_t *p;
|
||||
@ -31,17 +27,6 @@ void DisplayKey(const uint8_t *key, uint8_t len, bool lsb) {
|
||||
|
||||
void init_display(const char *Productname, const char *Version) {
|
||||
|
||||
// setup display refresh trigger IRQ using esp32 hardware timer
|
||||
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
|
||||
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
|
||||
displaytimer = timerBegin(0, 80, true);
|
||||
// interrupt handler DisplayIRQ, triggered by edge
|
||||
timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
|
||||
// reload interrupt after each trigger of display refresh cycle
|
||||
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
|
||||
// enable display interrupt
|
||||
timerAlarmEnable(displaytimer);
|
||||
|
||||
// show startup screen
|
||||
uint8_t buf[32];
|
||||
u8x8.begin();
|
||||
@ -107,10 +92,6 @@ void init_display(const char *Productname, const char *Version) {
|
||||
|
||||
void refreshtheDisplay() {
|
||||
|
||||
portENTER_CRITICAL(&mutexDisplay);
|
||||
DisplayTimerIRQ = 0;
|
||||
portEXIT_CRITICAL(&mutexDisplay);
|
||||
|
||||
// set display on/off according to current device configuration
|
||||
if (DisplayState != cfg.screenon) {
|
||||
DisplayState = cfg.screenon;
|
||||
@ -206,10 +187,4 @@ void refreshtheDisplay() {
|
||||
|
||||
} // refreshDisplay()
|
||||
|
||||
void IRAM_ATTR DisplayIRQ() {
|
||||
portENTER_CRITICAL_ISR(&mutexDisplay);
|
||||
DisplayTimerIRQ++;
|
||||
portEXIT_CRITICAL_ISR(&mutexDisplay);
|
||||
}
|
||||
|
||||
#endif // HAS_DISPLAY
|
@ -46,14 +46,11 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
|
||||
batt_voltage; // display values
|
||||
extern std::set<uint16_t> macs; // temp storage for MACs
|
||||
extern hw_timer_t *channelSwitch, *sendCycle;
|
||||
extern volatile uint8_t SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
|
||||
ChannelTimerIRQ, ButtonPressedIRQ;
|
||||
|
||||
extern std::array<uint64_t, 0xff>::iterator it;
|
||||
extern std::array<uint64_t, 0xff> beacons;
|
||||
|
||||
extern SemaphoreHandle_t xWifiChannelSwitchSemaphore;
|
||||
extern TaskHandle_t stateMachineTask, wifiSwitchTask;
|
||||
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||
|
||||
#ifdef HAS_GPS
|
||||
#include "gps.h"
|
||||
|
74
src/irqhandler.cpp
Normal file
74
src/irqhandler.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include "irqhandler.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
// irq handler task, handles all our application level interrupts
|
||||
void irqHandler(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
|
||||
uint32_t InterruptStatus;
|
||||
|
||||
// task remains in blocked state until it is notified by an irq
|
||||
for (;;) {
|
||||
xTaskNotifyWait(
|
||||
0x00, // Don't clear any bits on entry
|
||||
ULONG_MAX, // Clear all bits on exit
|
||||
&InterruptStatus, // Receives the notification value
|
||||
portMAX_DELAY); // wait forever (missing error handling here...)
|
||||
|
||||
// button pressed?
|
||||
#ifdef HAS_BUTTON
|
||||
if (InterruptStatus & BUTTON_IRQ)
|
||||
readButton();
|
||||
#endif
|
||||
|
||||
// display needs refresh?
|
||||
#ifdef HAS_DISPLAY
|
||||
if (InterruptStatus & DISPLAY_IRQ)
|
||||
refreshtheDisplay();
|
||||
#endif
|
||||
|
||||
// are cyclic tasks due?
|
||||
if (InterruptStatus & CYCLIC_IRQ)
|
||||
doHousekeeping();
|
||||
|
||||
// is time to send the payload?
|
||||
if (InterruptStatus & SENDPAYLOAD_IRQ)
|
||||
sendPayload();
|
||||
}
|
||||
vTaskDelete(NULL); // shoud never be reached
|
||||
}
|
||||
|
||||
// esp32 hardware timer triggered interrupt service routines
|
||||
// they notify the irq handler task
|
||||
|
||||
void IRAM_ATTR ChannelSwitchIRQ() {
|
||||
xTaskNotifyGive(wifiSwitchTask);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
void IRAM_ATTR homeCycleIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
void IRAM_ATTR SendCycleIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, SENDPAYLOAD_IRQ, eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
void IRAM_ATTR DisplayIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_BUTTON
|
||||
void IRAM_ATTR ButtonIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits, NULL);
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
#endif
|
18
src/irqhandler.h
Normal file
18
src/irqhandler.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef _IRQHANDLER_H
|
||||
#define _IRQHANDLER_H
|
||||
|
||||
#define DISPLAY_IRQ 0x01
|
||||
#define BUTTON_IRQ 0x02
|
||||
#define SENDPAYLOAD_IRQ 0x04
|
||||
#define CYCLIC_IRQ 0x08
|
||||
|
||||
#include "globals.h"
|
||||
#include "cyclic.h"
|
||||
#include "button.h"
|
||||
#include "display.h"
|
||||
#include "cyclic.h"
|
||||
#include "senddata.h"
|
||||
|
||||
void irqHandler(void *pvParameters);
|
||||
|
||||
#endif
|
81
src/main.cpp
81
src/main.cpp
@ -31,10 +31,10 @@ wifiloop 0 4 rotates wifi channels
|
||||
ledloop 0 3 blinks LEDs
|
||||
gpsloop 0 2 reads data from GPS over serial or i2c
|
||||
spiloop 0 2 reads/writes data on spi interface
|
||||
statemachine 0 1 switches application process logic
|
||||
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||
|
||||
looptask 1 1 arduino core -> runs the LMIC LoRa stack
|
||||
irqhandler 1 1 executes tasks triggered by irq
|
||||
IDLE 1 0 ESP32 arduino scheduler
|
||||
|
||||
ESP32 hardware timers
|
||||
@ -55,16 +55,8 @@ uint8_t volatile channel = 0; // channel rotation counter
|
||||
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
|
||||
batt_voltage = 0; // globals for display
|
||||
|
||||
// hardware timer for cyclic tasks
|
||||
hw_timer_t *channelSwitch, *sendCycle, *homeCycle;
|
||||
|
||||
// this variables will be changed in the ISR, and read in main loop
|
||||
uint8_t volatile ButtonPressedIRQ = 0, ChannelTimerIRQ = 0,
|
||||
SendCycleTimerIRQ = 0, DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
|
||||
|
||||
TaskHandle_t stateMachineTask, wifiSwitchTask;
|
||||
|
||||
SemaphoreHandle_t xWifiChannelSwitchSemaphore;
|
||||
hw_timer_t *channelSwitch, *sendCycle, *homeCycle, *displaytimer; // irq tasks
|
||||
TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||
|
||||
std::set<uint16_t> macs; // container holding unique MAC adress hashes
|
||||
|
||||
@ -227,26 +219,32 @@ void setup() {
|
||||
#ifdef HAS_DISPLAY
|
||||
strcat_P(features, " OLED");
|
||||
DisplayState = cfg.screenon;
|
||||
init_display(PRODUCTNAME, PROGVERSION);
|
||||
|
||||
// setup display refresh trigger IRQ using esp32 hardware timer
|
||||
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
|
||||
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
|
||||
displaytimer = timerBegin(0, 80, true);
|
||||
// interrupt handler DisplayIRQ, triggered by edge
|
||||
timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
|
||||
// reload interrupt after each trigger of display refresh cycle
|
||||
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
|
||||
#endif
|
||||
|
||||
// setup send cycle trigger IRQ using esp32 hardware timer 2
|
||||
sendCycle = timerBegin(2, 8000, true);
|
||||
timerAttachInterrupt(sendCycle, &SendCycleIRQ, true);
|
||||
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
|
||||
timerAlarmEnable(sendCycle);
|
||||
|
||||
// setup house keeping cycle trigger IRQ using esp32 hardware timer 3
|
||||
homeCycle = timerBegin(3, 8000, true);
|
||||
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
|
||||
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
|
||||
timerAlarmEnable(homeCycle);
|
||||
|
||||
// setup channel rotation trigger IRQ using esp32 hardware timer 1
|
||||
xWifiChannelSwitchSemaphore = xSemaphoreCreateBinary();
|
||||
channelSwitch = timerBegin(1, 800, true);
|
||||
timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true);
|
||||
timerAlarmWrite(channelSwitch, cfg.wifichancycle * 1000, true);
|
||||
timerAlarmEnable(channelSwitch);
|
||||
|
||||
// show payload encoder
|
||||
#if PAYLOAD_ENCODER == 1
|
||||
@ -287,35 +285,35 @@ void setup() {
|
||||
|
||||
#ifdef HAS_GPS
|
||||
ESP_LOGI(TAG, "Starting GPSloop...");
|
||||
xTaskCreatePinnedToCore(gps_loop, /* task function */
|
||||
"gpsloop", /* name of task */
|
||||
1024, /* stack size of task */
|
||||
(void *)1, /* parameter of the task */
|
||||
2, /* priority of the task */
|
||||
&GpsTask, /* task handle*/
|
||||
0); /* CPU core */
|
||||
xTaskCreatePinnedToCore(gps_loop, // task function
|
||||
"gpsloop", // name of task
|
||||
1024, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
2, // priority of the task
|
||||
&GpsTask, // task handle
|
||||
0); // CPU core
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPI
|
||||
ESP_LOGI(TAG, "Starting SPIloop...");
|
||||
xTaskCreatePinnedToCore(spi_loop, /* task function */
|
||||
"spiloop", /* name of task */
|
||||
2048, /* stack size of task */
|
||||
(void *)1, /* parameter of the task */
|
||||
2, /* priority of the task */
|
||||
&SpiTask, /* task handle*/
|
||||
0); /* CPU core */
|
||||
xTaskCreatePinnedToCore(spi_loop, // task function
|
||||
"spiloop", // name of task
|
||||
2048, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
2, // priority of the task
|
||||
&SpiTask, // task handle
|
||||
0); // CPU core
|
||||
#endif
|
||||
|
||||
// start state machine
|
||||
ESP_LOGI(TAG, "Starting Statemachine...");
|
||||
xTaskCreatePinnedToCore(stateMachine, /* task function */
|
||||
"stateloop", /* name of task */
|
||||
2048, /* stack size of task */
|
||||
(void *)1, /* parameter of the task */
|
||||
1, /* priority of the task */
|
||||
&stateMachineTask, /* task handle */
|
||||
0); /* CPU core */
|
||||
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||
"irqhandler", // name of task
|
||||
2048, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
1, // priority of the task
|
||||
&irqHandlerTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
// start led loop
|
||||
@ -339,6 +337,15 @@ void setup() {
|
||||
&wifiSwitchTask, // task handle
|
||||
0); // CPU core
|
||||
|
||||
// start timer triggered interrupts
|
||||
ESP_LOGI(TAG, "Starting Interrupts...");
|
||||
#ifdef HAS_DISPLAY
|
||||
timerAlarmEnable(displaytimer);
|
||||
#endif
|
||||
timerAlarmEnable(sendCycle);
|
||||
timerAlarmEnable(homeCycle);
|
||||
timerAlarmEnable(channelSwitch);
|
||||
|
||||
} // setup()
|
||||
|
||||
void loop() {
|
||||
|
@ -11,6 +11,6 @@
|
||||
#include "cyclic.h"
|
||||
#include "beacon_array.h"
|
||||
#include "ota.h"
|
||||
#include "statemachine.h"
|
||||
#include "irqhandler.h"
|
||||
|
||||
#endif
|
@ -1,8 +1,6 @@
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
|
||||
portMUX_TYPE mutexSendCycle = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||
void SendData(uint8_t port) {
|
||||
|
||||
@ -39,10 +37,6 @@ void SendData(uint8_t port) {
|
||||
// interrupt triggered function to prepare payload to send
|
||||
void sendPayload() {
|
||||
|
||||
portENTER_CRITICAL(&mutexSendCycle);
|
||||
SendCycleTimerIRQ = 0;
|
||||
portEXIT_CRITICAL(&mutexSendCycle);
|
||||
|
||||
// append counter data to payload
|
||||
payload.reset();
|
||||
payload.addCount(macs_wifi, cfg.blescan ? macs_ble : 0);
|
||||
@ -68,13 +62,6 @@ void sendPayload() {
|
||||
SendData(COUNTERPORT);
|
||||
} // sendpayload()
|
||||
|
||||
// interrupt handler used for payload send cycle timer
|
||||
void IRAM_ATTR SendCycleIRQ() {
|
||||
portENTER_CRITICAL(&mutexSendCycle);
|
||||
SendCycleTimerIRQ++;
|
||||
portEXIT_CRITICAL(&mutexSendCycle);
|
||||
}
|
||||
|
||||
void flushQueues() {
|
||||
#ifdef HAS_LORA
|
||||
xQueueReset(LoraSendQueue);
|
||||
|
@ -1,39 +0,0 @@
|
||||
#include "statemachine.h"
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = "main";
|
||||
|
||||
void stateMachine(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
|
||||
// initialize display - caution: must be done on core 1 in arduino loop!
|
||||
#ifdef HAS_DISPLAY
|
||||
init_display(PRODUCTNAME, PROGVERSION);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
|
||||
#ifdef HAS_BUTTON
|
||||
if (ButtonPressedIRQ)
|
||||
readButton();
|
||||
#endif
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
if (DisplayTimerIRQ)
|
||||
refreshtheDisplay();
|
||||
#endif
|
||||
|
||||
// check housekeeping cycle and if due do the work
|
||||
if (HomeCycleIRQ)
|
||||
doHousekeeping();
|
||||
|
||||
// check send cycle and if due enqueue payload to send
|
||||
if (SendCycleTimerIRQ)
|
||||
sendPayload();
|
||||
|
||||
// give yield to CPU
|
||||
vTaskDelay(2 / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelete(NULL); // shoud never be reached
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
#ifndef _STATEMACHINE_H
|
||||
#define _STATEMACHINE_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "cyclic.h"
|
||||
|
||||
void stateMachine(void *pvParameters);
|
||||
void stateMachineInit();
|
||||
|
||||
#endif
|
@ -46,21 +46,12 @@ void wifi_sniffer_init(void) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
|
||||
}
|
||||
|
||||
// IRQ Handler
|
||||
void IRAM_ATTR ChannelSwitchIRQ() {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
// unblock wifi channel rotation task
|
||||
xSemaphoreGiveFromISR(xWifiChannelSwitchSemaphore, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
// Wifi channel rotation task
|
||||
void switchWifiChannel(void *parameter) {
|
||||
while (1) {
|
||||
// task is remaining in block state waiting for channel switch timer
|
||||
// interrupt event
|
||||
xSemaphoreTake(xWifiChannelSwitchSemaphore, portMAX_DELAY);
|
||||
// rotates variable channel 1..WIFI_CHANNEL_MAX
|
||||
channel = (channel % WIFI_CHANNEL_MAX) + 1;
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // waiting for channel switch timer
|
||||
channel =
|
||||
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
|
||||
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
|
||||
ESP_LOGD(TAG, "Wifi set channel %d", channel);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user