IRAM_ATTR and function pointer and other
Posted: Wed May 05, 2021 5:54 pm
I wrote some class to handle some SPI device, mainly it was written under non RTOS env. Now i try to adapt it to ESP32 and RTOS environment. The task 'IMU_handler' just wait for 'Notify' from interrupt and then download data from SPI, preprocess etc, everythink at 1kHz rate, currently in case 8kHz interrupts then it process every 8th interrupt.
However rarely my class notice that some interrupts are missing (not processed at handler) even if it is the only task running at core 1, rest thing like display,SD,USB serial,bluetooth etc i put into core 0.
Accoring to that I try to play using IRAM_ATTR to keep SPI devices interrupt as fast as possible, even if it is not solution to my problem then I want to understand how to use it properly, and now I have few questions:
1) Do i need to put somewhere IRAM_ATTR to function pointer "void (*intlist[4])(void) = {IMU0_INTERRUPT,IMU1_INTERRUPT,andSoOn};" assuming that "void IRAM_ATTR IMU0_INTERRUPT(void);"
2) does IRAM_ATTR need to be placed at both, declaration and definition?
3) can I create specified methot with IRAM_ATTR, for example IMU.proc()? Or I need to put whole class at IRAM, if so then how?
4) does my interrupt function IMU0_INTERRUPT() path is fully IRAM or some components still require some tuning?
5) Can I somehow check if question #4 is true or not, some tools?
6) is it possible that core0 tasks somehow lock core1 tasks assuming that there is not any locking mechanism like mutex etc and core1 task has highest possible priority? Or some Arduino stuff might preemption my IMU_handler()?
7) does portYIELD_FROM_ISR() switch to task_IMU_handler or highest prio task if any? (see userDefinedInterrupt())
my device: TTGO T-DISPLAY
IDE: platformIO
flamework: Arduino (platform = espressif32@3.2.0)
However rarely my class notice that some interrupts are missing (not processed at handler) even if it is the only task running at core 1, rest thing like display,SD,USB serial,bluetooth etc i put into core 0.
Accoring to that I try to play using IRAM_ATTR to keep SPI devices interrupt as fast as possible, even if it is not solution to my problem then I want to understand how to use it properly, and now I have few questions:
1) Do i need to put somewhere IRAM_ATTR to function pointer "void (*intlist[4])(void) = {IMU0_INTERRUPT,IMU1_INTERRUPT,andSoOn};" assuming that "void IRAM_ATTR IMU0_INTERRUPT(void);"
2) does IRAM_ATTR need to be placed at both, declaration and definition?
3) can I create specified methot with IRAM_ATTR, for example IMU.proc()? Or I need to put whole class at IRAM, if so then how?
4) does my interrupt function IMU0_INTERRUPT() path is fully IRAM or some components still require some tuning?
5) Can I somehow check if question #4 is true or not, some tools?
6) is it possible that core0 tasks somehow lock core1 tasks assuming that there is not any locking mechanism like mutex etc and core1 task has highest possible priority? Or some Arduino stuff might preemption my IMU_handler()?
7) does portYIELD_FROM_ISR() switch to task_IMU_handler or highest prio task if any? (see userDefinedInterrupt())
my device: TTGO T-DISPLAY
IDE: platformIO
flamework: Arduino (platform = espressif32@3.2.0)
Code: Select all
MAIN.CPP
#include "Arduino.h"
#include "CLASS.H"
TaskHandle_t task_IMU_handler = NULL;
void IRAM_ATTR userDefinedInterrupt(){
if(task_IMU_handler==NULL) return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR( task_IMU_handler, &xHigherPriorityTaskWoken );
if(xHigherPriorityTaskWoken==pdTRUE){
portYIELD_FROM_ISR(); // does this switch to task_IMU_handler or highest prio task if any?
}
}
IMU_class IMU;
setup(){
IMU.begin();
xTaskCreatePinnedToCore(IMU_handler,"IMU_handler",4000,NULL,configMAX_PRIORITIES-1,&task_IMU_handler,1);
IMU.onInterrupt = userDefinedInterrupt; // call external funcion on interrupt
// just to adapt my class to RTOS and 'task driven by Notify'
}
void IMU_handler(void *param){
for(;;){
uint32_t isrsN = ulTaskNotifyTake(pdTRUE,IMU_isrTimeout);
if( isrsN > 0 ){
IMU.proc(); // rarely it detect some ISR was not processed
// download 14 bytes from SPI device
// some data transformation,filtration etc (standarize data before main processing)
// send flag to another thread about new data ready
// it took around 100[us]
} else {
// IMU.proc() is responsible to detect missing samples by watching "available0" counter, not handler
}
}
vTaskDelete(NULL);
}
Code: Select all
CLASS.CPP
#include "CLASS.H"
static IMU_class *LIST[4]; // list of class instances
static uint8_t LISTN=0; // number of instances
static void (*intlist[4])(void) = {IMU0_INTERRUPT}; // where to put IRAM_ATTR? Or its not required?
static volatile uint32_t available0 = 0;
void IRAM_ATTR IMU0_INTERRUPT(){
IMU_class *IMU = LIST[0]; // get instance because its not possible to attachInterrupt to method
someFunc();
if(IMU->onInterrupt) IMU->onInterrupt(); // call external interrupt function, userDefinedInterrupt() for RTOS handling
}
void IRAM_ATTR someFunc(){
available0++; // count interrupts
}
void IMU_class::begin(){
LIST[LISTN] = this;
IMU_class::Interrupt_enable(1,intlist[LISTN]); // handling multiple instances
LISTN++;
}
void IMU_class::Interrupt_enable(bool en,void (*interruptFunction)(void)){
if(en){
attachInterrupt(MPU_INT_PIN, interruptFunction, RISING);
// interruptFunction=IMU0_INTERRUPT() for first instace
}
else{
}
}
Code: Select all
CLASS.H
#ifndef CLASS_H
#define CLASS_H
class IMU_class
{
public:
void begin(void);
void proc(void); // can I put that to IRAM?
void (*onInterrupt)(void); // where to put IRAM_ATTR?
void Interrupt_enable(bool,void (*interruptFunction)(void));
};
void IRAM_ATTR IMU0_INTERRUPT(void);
void IRAM_ATTR someFunc(void);
#endif