irq handling reworked (using tasknotify instead of semaphores)

This commit is contained in:
Klaus K Wilting 2018-10-04 22:08:54 +02:00
parent 6e47a60f32
commit 30d22aa896
13 changed files with 146 additions and 168 deletions

View File

@ -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);

View File

@ -3,7 +3,7 @@
#include "senddata.h"
void IRAM_ATTR ButtonIRQ(void);
void readButton(void);
void IRAM_ATTR ButtonIRQ();
void readButton();
#endif

View File

@ -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;

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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() {

View File

@ -11,6 +11,6 @@
#include "cyclic.h"
#include "beacon_array.h"
#include "ota.h"
#include "statemachine.h"
#include "irqhandler.h"
#endif

View File

@ -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);

View File

@ -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
}

View File

@ -1,10 +0,0 @@
#ifndef _STATEMACHINE_H
#define _STATEMACHINE_H
#include "globals.h"
#include "cyclic.h"
void stateMachine(void *pvParameters);
void stateMachineInit();
#endif

View File

@ -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);
}