Page 2 of 2

Re: Reduce external interrupt latency

Posted: Mon Apr 22, 2019 7:04 pm
by HelWeb
If you use a one-way-signal as a global variable the communication is much faster. One-Way means Task 1 only writes, Tasks 2 only reads. (volatile).
Using this mechanism it is possible to get cycles of 300ns. But there is a drawback:
The task in core 0 MUST run without a taskswitch, delay etc. That is possible, but it must have the lowest priority.
And that means, it will run in a perfect uninterrupted loop - until a forced taskswitch after one ms will interrupt this loop.
Maybe more often depending on the other tasks running. And the delay than my be some ms !
There are different solutions. One is to use the semaphore (s.o.). That needs 2 µs latency to start the waiting task RTOS_2 in core 0.. After that you get a cylcetime of ~300ns (disable interrupts for core 0).
Here is the source to show superfast interaction:
External interrupt detected by task Core1 --300ns--> RTOS_2 (core 0) reacts.
Use it with a scope or a logic analyser: 2700000 served interrupts/s
(Pins 18 and 19 must be shortened)

BTW: a cooperative multitasking using only core 1 may be an other solution ;) because core 1 never will be interrupted by RTOS.

Code: Untitled.c Select all


/*
* FastIRQGlobalVar
*
* Copyright (c) 2019, Dipl. Phys. Helmut Weber.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Helmut Weber ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the CoopOS library.
*
* Author: Helmut Weber <Dph.HelmutWeber@web.de>
*
* $Id: Task.h,v 1.1 2019/04/02 helmut Exp $
*/

//#pragma GCC optimize ("O2")

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/portmacro.h"

#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_task_wdt.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "rom/uart.h"
#include "driver/touch_pad.h"
#include "driver/gpio.h"
#include "soc/gpio_periph.h"
#include "freertos/semphr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"


// Dimensions the buffer that the task being created will use as its stack.
// NOTE: This is the number of bytes the stack will hold, not the number of
// words as found in vanilla FreeRTOS.
#define STACK_SIZE 4096

// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;

// Buffer that the task being created will use as its stack. Note this is
// an array of StackType_t variables. The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];



#define PinA GPIO_NUM_18 // output shortcut > _____
#define PinB GPIO_NUM_19 // input < _____|----- scope


TaskHandle_t xHandle1 = NULL;
TaskHandle_t xHandle2 = NULL;

volatile int LinkCore1Core0=0;
#define LONG_TIME 0xffff



/*
* Macro to check the outputs of TWDT functions and trigger an abort if an
* incorrect code is returned.
*/
#define CHECK_ERROR_CODE(returned, expected) ({ \
if(returned != expected){ \
printf("TWDT ERROR\n"); \
abort(); \
} \
})



inline uint32_t IRAM_ATTR micros()
{
uint32_t ccount;
asm volatile ( "rsr %0, ccount" : "=a" (ccount) );
return ccount;
}

void IRAM_ATTR delayMicroseconds(uint32_t us)
{
if(us){
uint32_t m = micros();
while( (micros() - m ) < us ){
asm(" nop");
}
}
}




void Core1( void* p) {

gpio_set_direction(PinA, GPIO_MODE_OUTPUT);
gpio_set_direction(PinB, GPIO_MODE_INPUT );


printf("Start Core 1\n");
vTaskDelay(1000);

// I do not want an RTOS-Tick here
portDISABLE_INTERRUPTS(); // YEAH

while(1) {
register int level;
level=REG_READ(GPIO_IN_REG) & (1<< PinB );
if (level) {
REG_WRITE(0x3ff4400c, 1<<PinA);// Low at 160 ns
LinkCore1Core0=1;
}
}
}



// Function that creates a task to be pinned at Core 1
void StartCore1( void )
{
TaskHandle_t xHandle = NULL;

xHandle = xTaskCreateStaticPinnedToCore(
Core1, // Function that implements the task.
"Core1", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+2,
xStack, // Array to use as the task's stack.
&xTaskBuffer, // Variable to hold the task's data structure.
1); // Core 1

}


void RTOS_1(void *p) {
while(1) {
esp_task_wdt_reset();
printf("RTOS-1\n"); // demo for full function
vTaskDelay(1000);
}
}


void RTOS_2(void *p) {
while(1) {
REG_WRITE(0x3ff44008, 1<<PinA);// High
while (LinkCore1Core0==0);

REG_WRITE(0x3ff44008, 1<<PinA);// High
// ... do something

}
}


void app_main(void)
{

gpio_set_direction(PinA, GPIO_MODE_OUTPUT);
gpio_set_direction(PinB, GPIO_MODE_INPUT );



xTaskCreate(
RTOS_1, // Function that implements the task.
"RTOS-1", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+2,
&xHandle1); // Variable to hold the task's data structure.


// Watchdog satisfied by RTOS_1:
CHECK_ERROR_CODE(esp_task_wdt_init(2 /*sec*/, false), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_add(xHandle1), ESP_OK);


xTaskCreate(
RTOS_2, // Function that implements the task.
"RTOS-2", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+1,
&xHandle2); // Variable to hold the task's data structure.




StartCore1();


while(1) {
vTaskDelay(10000);
}

}

Re: Reduce external interrupt latency

Posted: Fri Apr 26, 2019 2:42 am
by HelWeb
Update2019/12:
This listing is for esp-idf Version 3.1
For version 4.1 see below !


I am awaiting a shit storm ;)

There are some people trying to start a program without RTOS using core 1.
It is not easy. You may think using make menuconfig to set RTOS to core0
and write a function start_cpu1 should do the job - but it does not!
If you look at components/esp32/cpu_start.c and components/freertos/port.c
you will find, that this way is not possible the easy way.
Well, I did'nt succeed.
But why should you want to do it?
One reason is the interrupt latency of 2 µs. Compared to other MCUs with one
core and less speed this value is not acceptable.
I am sure for many people in development departments interested in the whole
bunch of advatages of the Esp32 the big interrupt latency is a reason
not to use this part.
Espressif should do something to not lose a good reputation! Its not enough
to explain how it should be done. Its not so easy - otherwise we had a solution
since years.
Espressif should do it for the customers!

If someone gives me a tool to use core1 without RTOS AND a really fast interrupt
would be my hero.

Until then I have to live with my workaround and maybe some others would like to
test it. A workaround is far away from beeing a perfect solution - but better than nothing.

As I have explained, running an RTOS task (just one!) pinned to core 1 and disabling
all interrupts let us perform some functions without preemption.
This task may be (ab)used to scan pins and detect rising edges very fast - and call
"interrupt routines".
Here is a template to read the first 32 pins, mask the interesting input pins and
call interrupt routines (up to 32 different are possible) from a table of function pointers.
Fastest reaction time (from rising edge to first line in the interrupt routine) is about
160 ns. The interrupts have a priority: lower pin numbers get served first.
There are some examples in the program.

Fore some people 2 µs interrupt latency may be no problem. They should use the normal way.
But some others need a very fast interrupt response - and maybe this can help.
The times printed in ns may have an error +/- 20 ns or even more. Use a scope to test.

Until Espressif delivers the miracle of the 12 cycle interrupt latency mentioned here:
https://www.sciencedirect.com/topics/co ... pt-latency
this workaround may help.

Code: Untitled.c Select all


/*
* FastIRQ32
*
* Copyright (c) 2019, Dipl. Phys. Helmut Weber.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Helmut Weber ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the CoopOS library.
*
* Author: Helmut Weber <Dph.HelmutWeber@web.de>
*
* $Id: Task.h,v 1.1 2019/04/02 helmut Exp $
*/

//#pragma GCC optimize ("O2")

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/portmacro.h"

#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_task_wdt.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "rom/uart.h"
#include "driver/touch_pad.h"
#include "driver/gpio.h"
#include "soc/gpio_periph.h"
#include "freertos/semphr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"

#include "xtensa/core-macros.h"
#include "sdkconfig.h"
#include "soc/rtc.h"
//#include "esp32/clk.h"
#include "esp_system.h"
//#include "test_utils.h"


// Dimensions the buffer that the task being created will use as its stack.
// NOTE: This is the number of bytes the stack will hold, not the number of
// words as found in vanilla FreeRTOS.
#define STACK_SIZE 4096

// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;

// Buffer that the task being created will use as its stack. Note this is
// an array of StackType_t variables. The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];



#define PinA GPIO_NUM_22 // output shortcut >------|
#define PinB GPIO_NUM_23 // input <------|-----> scope


TaskHandle_t xHandle1 = NULL;
TaskHandle_t xHandle2 = NULL;

uint32_t ccount, StartTime, EndTime;

volatile int IsrOk=0;
#define LONG_TIME 0xffff



/*
* Macro to check the outputs of TWDT functions and trigger an abort if an
* incorrect code is returned.
*/
#define CHECK_ERROR_CODE(returned, expected) ({ \
if(returned != expected){ \
printf("TWDT ERROR\n"); \
abort(); \
} \
})




// Here we prepare the Interrupt service Routines for
// interrupts (rising edge) for pins 0-31

void Isr0(int param) {
}

void Isr1(int param) {
}

void Isr2(int param) {
uint32_t Pin2Time;
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (Pin2Time));
printf("here is pin2 at : %u\n", Pin2Time);
}

void Isr3(int param) {
}

void Isr4(int param) {
}

void Isr5(int param) {
}

void Isr6(int param) {
}

void Isr7(int param) {
}

void Isr8(int param) {
}

void Isr9(int param) {
}

void Isr10(int param) {
}

/// .....

void Isr23(int param) {
uint32_t diff;

// this is only to measure latency
// it is not part of the interrupt detecting

REG_WRITE( GPIO_OUT_W1TC_REG, 1<<PinA);// Low: Stop Pin HIGH of pinA set in RTOS_2
IsrOk=1;
printf(" Isr23 ready\n");

}

// .......

void Isr30(int param) {
}

void Isr31(int param) {
printf("IRQ 31\n");
}

void IsrDefault(int param) {
printf("Default ISR: Pin %d\n", param);
// for bouncing contacts you should insert a delay
}


// This is the table of function pointers to the Interrupt Service Routines 0-31
typedef void (*Func)(int);
Func Isrs[32] = { IsrDefault, IsrDefault, Isr2, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault,
IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault,
IsrDefault, IsrDefault, IsrDefault, Isr23, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault, IsrDefault,
IsrDefault, IsrDefault};
// ------ !!!!! -------



void Core1( void* p) {
volatile uint32_t inReg;
volatile uint32_t oldInReg;
uint32_t inMask=0;

gpio_set_direction(PinA, GPIO_MODE_OUTPUT);
gpio_set_direction(PinB, GPIO_MODE_INPUT );
//gpio_set_pull_mode(PinB, GPIO_PULLUP_ONLY); // B


// set a mask to filter the pins which produce an interrupt on rising edge
inMask |= (1<<PinB); // 23
inMask |= (1<<2); // for experiments with pin2: Connect pin2 to 3.3V with a button (or wire)
inMask |= (1<<4); // for experiments with pin4: Connect pin2 to 3.3V with a button (or wire)



// prepare the start
inReg=(REG_READ(GPIO_IN_REG) & inMask);
oldInReg=inReg;

printf("Start Core 1\n");
vTaskDelay(1000);

// I do not want an RTOS-Tick here
portDISABLE_INTERRUPTS(); // YEAH

while(1) {

do {
while(inReg == oldInReg) {
inReg=(REG_READ(GPIO_IN_REG) & inMask);
}
oldInReg=inReg;
} while (inReg == 0);

// rising edge detected

// TESTTIME


//printf("EndTime %d\n",EndTime);
// now one of out pins in mask got level change
// we only react on rising edges

// ---------------------------------------- scope values
// possibility 1)
// fastest:
// to measure the time until the rising edge is detected: 220 ns
// REG_WRITE( GPIO_OUT_W1TC_REG, 1<<PinA);// Low
// Without REG_WRITE, which costs 50ns, we are here after
// about 170 ns
// ----------------------------------------

//printf("Level changed %x\n", inReg);

// possibility 2)
// call interrupt routine
// to measure time until the rising edge reaches IRQ-Routine 296 ns
// (direct calling)
//if (inReg & (1<<PinB)) Isrs[23]( 111);

// possibility 3)
// test all Pins (0-31) and dispatch to interrupt routine
// to measure time if ALL pins are tested 2 µs

// the mask is prepared to allow interrupts at pin2
// and pin 4. Try it with a button or a wire against 3.3V

//for (register int i=0; i<32; i++) {
//if (inReg & (1<<i)) Isrs[i]( i);
//}

// possibility 4)
// test 4 pins and measure the time for 4. pin
// of the pins to test

register uint32_t inRegTest = inReg;

// Example: here we test pins 20 to 31
// our testpin 23 is the 4. pin to test: 495 ns
inRegTest>>=20;
for (register int i=20; i<31; i++) {
if (inRegTest & 1) Isrs[i](i);
inRegTest>>=1;
}

// Example: here we test pins 23 to 31
// our testpin 23 is the 1. pin to test: 375 ns
//inRegTest>>=23;
//for (register int i=23; i<31; i++) {
//if (inRegTest & 1) Isrs[i](i);
//inRegTest>>=1;
//}


// Example: here we test pins 0 to 31
// here we can test Pin 2 and/or Pin4
// Simple priority: first tested is serviced first
// All Level-Changes at time TESTTIME are serviced, even if the changed
// again in the meantime.
// All short pulses between TESTTIME and end of all ServiceRoutines are lost
// But it is possible to test inside service routines again: complicated and
// Error-prone!
// Since the "Interrupt-Routines" are normal functions all RTOS-Commands to
// send Information to Core 0 are allowed.
// Core 1 function "Core1" does not yield !!! and should not be considered
// as an RTOS-Function though it is.

//inRegTest>>=0;
//for (register int i=0; i<31; i++) {
//if (inRegTest & 1) Isrs[i](i);
//inRegTest>>=1;
//}




}



}




// Function that creates a task to be pinned at Core 1
void StartCore1( void )
{
TaskHandle_t xHandle = NULL;

xHandle = xTaskCreateStaticPinnedToCore(
Core1, // Function that implements the task.
"Core1", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+2,
xStack, // Array to use as the task's stack.
&xTaskBuffer, // Variable to hold the task's data structure.
1); // Core 1

}


void RTOS_1(void *p) {
while(1) {
esp_task_wdt_reset();
printf("RTOS-1\n"); // demo for full function
vTaskDelay(1000);
}
}


// This task simulates an interrupt at PinA
void RTOS_2(void *p) {
vTaskDelay(2000); // let's start the interrupt controller first
while(1) {

printf("----\n>>> RTOS-2: Set PinA\n");
// here we only produce a rising edge
// the "Interrupt Routine" resets the level of PinA to measure latency
//

IsrOk=0;
REG_WRITE( GPIO_OUT_W1TS_REG, 1<<PinA);// High

// very dirty, well, it is a workaround ;)
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (StartTime));
//while(REG_READ( GPIO_IN_REG) & (1<<PinB) ) ;
while (IsrOk==0); // Wait for handshake of interrupt routine Isr23
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (EndTime));
// this is not as precise as a scope - but not everyone has it
printf("<<< EndTime-StartTime %d 240MHz-CPU-Cycles",EndTime-StartTime );
printf(" = %d ns \n----\n",1000*(EndTime-StartTime)/240-80);


vTaskDelay(500);
// ... do something

}
}


void app_main(void)
{

gpio_set_direction(PinA, GPIO_MODE_OUTPUT);
gpio_set_direction(PinB, GPIO_MODE_INPUT );



xTaskCreate(
RTOS_1, // Function that implements the task.
"RTOS-1", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+2,
&xHandle1); // Variable to hold the task's data structure.


// Watchdog satisfied by RTOS_1:
CHECK_ERROR_CODE(esp_task_wdt_init(2 /*sec*/, false), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)), ESP_OK);
CHECK_ERROR_CODE(esp_task_wdt_add(xHandle1), ESP_OK);


xTaskCreate(
RTOS_2, // Function that implements the task.
"RTOS-2", // Text name for the task.
STACK_SIZE, // Stack size in bytes, not words.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY+1,
&xHandle2); // Variable to hold the task's data structure.




StartCore1();


while(1) {
vTaskDelay(10000);
}

}


Re: Reduce external interrupt latency

Posted: Tue Jun 18, 2019 1:46 pm
by edigi32
Unfortunatelly what you've described here as a solution is not a real solution except maybe for the simplest cases.
Don't misunderstand me, I have stumbled to this issue as well, and asked for solution.
viewtopic.php?t=9418

What you describe here (if I understand it correctly) is basically dedicating the entire app core to polling GPIOs. The trap here that when you have to act on some GPIO change the time spent on that has to be minimized as every clock cycle spent in acting GPIO change is blind time for watching GPIO change.
Not only this, but some events are not directly related to GPIO change. E.g. in my case I use the built in PCNT to count external edges but since the counter is only 16 bits (or rather 15 due to sign bit/direction) overflow must be handled via interrupt.
So while it's very nice that you can watch 32 GPIOs with a single read (ESP32 has more but let's forget it now) in many cases it's not sufficient to watch only for GPIO but maybe for other things as well (ESP32 internal interrupts and it's not just my PCNT example).
Pretty quickly this won't scale well and you get into the magnitude of normal interrupt latency.

Even if in a simple case the gains are significant the price to be paid is high. You have to do some very strict linear programming in app core and relocate what's supposed to be on app core to pro core. Basically mess up structured code logic to fit to this peculiar architecture and work around the interrupt latency weekness.
If it appeals and works to you nothing is wrong with that.

However most of the people who need really fast interupt reaction time will just use the right tool for that and in the present form ESP32 does not belong to that circle.

Again don't misunderstand me, ESP32 is a fantastic package (good value) for many things and I like to work with it many cases, however when it comes to interrupt latency it's simply not competitive enough and it's very hard if not impossible to make a generic enough improvement to this. Anyone with some micro experience can immediatelly come to this kind of polling solution that you've created but can see also that it's not good enough for most cases, especially that it means sacrifying an entire core to it.

Re: Reduce external interrupt latency

Posted: Thu Dec 12, 2019 2:46 am
by HelWeb
@edigi32
You are right!
As I mentioned: "this workaround may help."
It is an act of despair. But the prices of ESP32 are so low ;)
Maybe for some of us it is a solution.
:D

Re: Reduce external interrupt latency

Posted: Sat Dec 21, 2019 3:23 am
by HelWeb
The ESP32 has 2 cores and for most applications one core running RTOS-tasks is enough.
If we dedicate the 2. core to a single - never interrupted - task we can achieve - while running a WebServer !

- count and react on more than 3,000,000 external "interrupts" per second
- building pulses with a granularity of 50 ns (Nanos!)
- react on an external interrupt within 170 ns


For some it is a waste of a core - for others it is the solution they have searched for :D

Source and README.md: (for esp-idf Version 4.1)
https://github.com/MacLeod-D/ESp32-Fast-external-IRQs

Re: Reduce external interrupt latency

Posted: Fri Dec 22, 2023 5:37 pm
by DrMickeyLauer
Resurrecting this thread, since there is an amazing patch from the community that might change the opportunities for software that requires very low latency: Take a look at https://github.com/espressif/esp-idf/pull/12800, and please test and comment, if you're interested!