ESP32 Guru Meditation Error in Interrupt Service Routine

Hallo,
I have an ESP-WROOM-32 connected to accelerometer ADXL345 through SPI. When the accelerometer collects 16 samples of x,y,z data, it toggles its interrupt pin, which is connected to ESP32 GPIO pin 12 - see the code below. I have attached an Interrupt Service Routine to this pin to fill the data into a larger array. The guru mediation error occurs (see the attached error message), when I try to write data into global variable Lambda in ISR(). When I remove this line everything works fine. I do not understand why. Can anyone explain to me why this error occurs?

Thanks a lot!

#include <SparkFun_ADXL345.h>         // SparkFun ADXL345 Library

ADXL345 adxl = ADXL345(SS);           // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);

#define accSamples 16

void printByte(byte b);
void printByte(int b);

volatile float ** volatile xyzData;
volatile float ** volatile set1  = new volatile float * [3];
volatile float ** volatile set2  = new volatile float * [3];
volatile bool debugPrint (false);

const int accINTpin (12);
const int accPowPin (15);

void ISR();
volatile int i (0);
bool setFlag (0);
const int N (514);

volatile float Lambda (0.f);
volatile long cas1 (0);
volatile long cas2 (0);
volatile byte ODR;

/******************** SETUP ********************/
/*          Configure ADXL345 Settings         */
void setup(){
  byte rangeSet = B00000000;
  double dtr;
  byte bDtr;
  bool intActive; 
  byte fifoCtlReg;
  byte enabledINT;
  byte INTmapping;
  double temp [3];
  
  set1[0] = new volatile float [N];
  set1[1] = new volatile float [N];
  set1[2] = new volatile float [N];

  set2[0] = new volatile float [N];
  set2[1] = new volatile float [N];
  set2[2] = new volatile float [N];

  Serial.begin(9600);                 // Start the serial terminal
  Serial.println("SETUP");
  Serial.println();

  pinMode(accINTpin, INPUT);
  pinMode(13, INPUT);
  pinMode(accPowPin, OUTPUT);

  
  digitalWrite(accPowPin,LOW);
  delay(100);
  digitalWrite(accPowPin,HIGH);
  
  adxl.powerOn();                     // Power on the ADXL345 in standby mode

  adxl.offsets[0] = -3;
  adxl.offsets[1] = 0;
  adxl.offsets[2] = 12;

  adxl.gains[0] = 1;
  adxl.gains[1] = 1;
  adxl.gains[2] = 1;
  
  adxl.setRangeSetting(16);           // Give the range settings
                                      // Accepted values are 2g, 4g, 8g or 16g
                                      // Higher Values = Wider Measurement Range
                                      // Lower Values = Greater Sensitivity

  adxl.setSpiBit(0);                  // Configure the device to be in 4 wire SPI mode when set to '0' or 3 wire SPI mode when set to 1
                                      // Default: Set to 1
                                      // SPI pins on the ATMega328: 11, 12 and 13 as reference in SPI Library 
   
  adxl.getRangeSetting(&rangeSet);
  Serial.println("Range setting");
  printByte(rangeSet);
  
  adxl.set_bw(ADXL345_BW_100);
  bDtr = adxl.get_bw_code();
  Serial.print("BW code = ");
  printByte(bDtr);
  
  ODR = dtr = adxl.getRate(); // bity D3 - D0 z registu BW_RATE
  Serial.println("Data rate = ");
  Serial.println(dtr);
   
  adxl.disableAllINT();
  enabledINT = adxl.getEnabledINT();
  Serial.print("INT ENABLE reg = ");
  printByte(enabledINT);
   
  adxl.setFifoStream();
  fifoCtlReg = adxl.getFifoCtl();
  Serial.print("fifo ctl registr = ");
  printByte(fifoCtlReg);
  
  adxl.setInterruptMapping(ADXL345_WATERMARK,0);
  INTmapping = adxl.getInterruptMapping();
  Serial.print("Interrupt mapping = ");
  printByte(INTmapping);

  fifoCtlReg = adxl.WatermarkINT(1,accSamples);
  intActive = adxl.isInterruptEnabled(ADXL345_WATERMARK);
  enabledINT = adxl.getEnabledINT();
  Serial.print("FIFO ctl reg = ");
  printByte(fifoCtlReg);
  
  Serial.print("Is watermark INT activated? : ");
  Serial.println(intActive);
  Serial.print("Activated interrupts: ");
  printByte(enabledINT);

  Serial.println("FIFO flush");
  for(int h=0;h<31;++h){
    adxl.get_Gxyz(temp);
  }
 
  if(!digitalRead(accINTpin)){
    attachInterrupt(digitalPinToInterrupt(accINTpin), ISR , RISING);
    Serial.println("ISR set!");
    delay(1000);
  }
  adxl.setToMeasure();

  xyzData = set1;
}

/****************** MAIN CODE ******************/
/*     Accelerometer Readings and Interrupt    */
void loop(){
}

void printByte(byte b){
  for(int i=7; i>=0 ; i--){
    Serial.print(bitRead(b,i));  
  }
  Serial.println("");
}

void printByte(int b){
  for(int i=15; i>=0 ; i--){
    Serial.print(bitRead(b,i));  
  }
  Serial.println("");
}

void ISR(){
  byte INTreg;
  bool watermarkBit;
  int k;
  int index;
  double xyz [3];
  
  INTreg = adxl.getInterruptSource();
  watermarkBit = (INTreg >> 1) & 1; 
  
  if(watermarkBit){
    for(k=0;k<accSamples;++k){
      adxl.get_Gxyz(xyz);
      
      index = accSamples*i+k;
      
      xyzData[0][index] = xyz[0];
      xyzData[1][index] = xyz[1];
      xyzData[2][index] = xyz[2];

    }
     ++i;  
  
    if(i==N/accSamples){
      
      cas2 = micros();
      Lambda = float(cas2-cas1)/1000.f;
      
      /*xyzData[0][N-2] = Lambda;
      xyzData[1][N-2] = Lambda;
      xyzData[2][N-2] = Lambda;
  
      xyzData[0][N-1] = (float)ODR;
      xyzData[1][N-1] = (float)ODR;
      xyzData[2][N-1] = (float)ODR;
      */
      cas1 = micros();
      // switch of field for storege of data
      setFlag = !setFlag;
          
      if(setFlag){
        xyzData = set2;
      }
      else{
        xyzData = set1;
      }
      i = 0;
      debugPrint=false; 
    }
    else{
      debugPrint=true;  
    }
  }
}

Output of the serial monitor is the following:

SETUP

Range setting
00001011
BW code = 00001011
Data rate = 
200.00
INT ENABLE reg = 00000000
fifo ctl registr = 10010000
Interrupt mapping = 00000000
FIFO ctl reg = 10010000
Is watermark INT activated? : 1
Activated interrupts: 00000010
FIFO flush
ISR set!
Guru Meditation Error: Core  1 panic'ed (Coprocessor exception)
Core 1 register dump:
PC      : 0x400d0ebb  PS      : 0x00060231  A0      : 0x80080ef4  A1      : 0x3ffbe6b0  
A2      : 0x003b4f1d  A3      : 0x00000001  A4      : 0x3ffc0168  A5      : 0x00000000  
A6      : 0x3ffc015c  A7      : 0x3ffb91fc  A8      : 0x800d0ea0  A9      : 0x3ffbe750  
A10     : 0x003b4f1d  A11     : 0x447a0000  A12     : 0x00000000  A13     : 0x43720000  
A14     : 0x80000000  A15     : 0x00000000  SAR     : 0x0000001d  EXCCAUSE: 0x00000004  
EXCVADDR: 0x00000000  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xffffffff  
Core 1 was running in ISR context:
EPC1    : 0x400d0ebb  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x40086e4f

Backtrace: 0x400d0ebb:0x3ffbe6b0 0x40080ef1:0x3ffbe770 0x40080ef1:0x3ffbe7b0 0x40084db1:0x3ffbe7d0 0x400d3e2d:0x3ffb1fb0 0x400889d9:0x3ffb1fd0

Rebooting...

Core 1 panic'ed (Coprocessor exception)

Core 1 is used mainly for the Wifi / BT. It needs some time for its specific tasks. Maybe add a few yield(); in your ISR function.

By the way, an interrupt function must be as small as possible : you should try rewriting it just for toggling a flag. Then you check the flag in your loop and do the rest of the current ISR tasks if the flag is ON.

volatile float Lambda = 0.0f; instead of volatile float Lambda (0.f);

With this indicator: EXCVADDR: 0x00000000, I'd suspect a memory overrun issue of some sort; such as access info outside of an array boundary.

This void ISR() should be void IRAM_ATTR ISR(), I'd name the ISR something else so as not to use a ESP32 code word.

And don't expect the ISR to work with all the code contained within the ISR. I'd use freeRTOS events to trigger, from the ISR, a function to run instead of running the code in an ISR.

For example. You could declare a volatile bool variable that gets its state changed in the ISR to true. In loop() have a routine to detect the true state, run code and put the bool back to false. With an ESP32, I'd not run any code in the loop() under freeRTOS. code example of my use of a ISR

void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iTicCount++;
  xEventGroupSetBitsFromISR(eg, OneMilliSecondGroupBits, &xHigherPriorityTaskWoken);
  if ( (iTicCount % 2) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_ReceiveSerial_LIDAR, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg, evtReceiveSerial_LIDAR, &xHigherPriorityTaskWoken); // trigger every 2mS, if not already processing
    }
  }
  if ( (iTicCount % 3) == 0 )
  {
    if ( (xSemaphoreTakeFromISR(sema_HexaPodAdjustment, &xHigherPriorityTaskWoken)) == pdTRUE ) // grab semaphore, no wait
    {
      xEventGroupSetBitsFromISR(eg1, evtWalk_Forward, &xHigherPriorityTaskWoken);
    }
  }
  if ( iTicCount == OneK )
  {
    xEventGroupSetBitsFromISR(eg, OneSecondGroupBits, &xHigherPriorityTaskWoken); // trigger every 1X a second
    // reset counter to start again
    iTicCount = 0;
  }
  if ( !bLIDAR_OK )
  {
    xSemaphoreTakeFromISR ( sema_iLIDAR_Power_Reset, &xHigherPriorityTaskWoken );
    ++iLIDAR_Power_Reset;
    xSemaphoreGiveFromISR ( sema_iLIDAR_Power_Reset,  &xHigherPriorityTaskWoken);
    if ( iLIDAR_Power_Reset >= 4000 )
    {
      xEventGroupSetBitsFromISR(eg, evtfLIDAR_Power_On, &xHigherPriorityTaskWoken);
    }
  }
} // void IRAM_ATTR onTimer()

What’s wrong with the ESP32’s A:D converters?

ESP32 GPIO pin 12 -

Do you mean GPIO12 or pin12 of the WROOM? If you are using GPIO12 of the WROOM, you may encounter issues if you do not understand the needs of GPIO12, which, is used during boot and must NOT change state during the boot process. GPIO12=TDI.

You should not use floating point in an ISR.

floatISRESP32.png

@ lesept

Where exactly should I put the yield() functions into the code of the ISR()?

@ Idahowalker

I will double check writing into the fields, if there is not any writing of data outside them.

Ok, I will change name of the ISR() and inicialization of the variable Lambda. Why is the initialization incorrect?

I wanted to avoid putting parts of code into IRAM since I am not familiar with it. I have read an article, where it is recommended to put ISR into IRAM because it can be loaded faster than from the flash memory (please correct me if I am wrong). But if this is done, all const variables used inside the ISR should be placed into IRAM together with all functions called from ISR. Since I am calling a member function from the ISR, I wanted to avoid this. I do not know if I can place only selected member functions into IRAM or If the whole class should be put into the IRAM. https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/general-notes.html#iram-instruction-ram

Can you send me a link to any tutorial or article related to calling event functions from an ISR(), please? I realized, that you use semaphores. Can send me any links with useful info about their usage in the ISRs, I will appreciate it.

I use GPIO12. By trial and error I realized what you mentioned - it must not be HIGH or changing state during boot. Therefore I power the accelerometer from GPIO15. The power is switched on in the setup().

What is the reason to not to run any code in loop in ESP32? Thanks.

@ Whandall

I have read this info. Since it is more than 2 years old, I have not payed any attention to it. I thought It is already solved. But I will try to play with type of Lambda, if anything changes. One can never be sure. Thanks.

@ All:

What are the reasons to keep ISRs as short as possible? I understand, that if an ISR is running, other interrupts are missed. But that is not my case now. The interrupts occurs approximately each 5 ms or more.

dee25:
I have read this info. Since it is more than 2 years old, I have not payed any attention to it. I thought It is already solved.

What is the best before date of rules or information?

Why should somebody fix a thing that is not broken?

You don’t want the overhead of saving/restoring all floating point registers for each ISR.

You are not supposed to use floating point in an ISR,
if you have to, you have to save and restore the floating point hardware state yourself,
or you will screw up the normal code floating point system.

dee25:
What are the reasons to keep ISRs as short as possible?
I understand, that if an ISR is running, other interrupts are missed.
But that is not my case now.

Reaction time. ISRs should react to events as timely as possible, so they have to be as short as possible.
You have no WiFi, no Serial running?
You are shure that the Flash accesses don’t need interrupts?

Which ESP32 GPIOs are safe to use for connection with external peripherals, that can generate signals?

Idahowalker: What's wrong with the ESP32's A:D converters?

Do you mean GPIO12 or pin12 of the WROOM? If you are using GPIO12 of the WROOM, you may encounter issues if you do not understand the needs of GPIO12, which, is used during boot and must NOT change state during the boot process. GPIO12=TDI.

This is what I wanted to know. Thank you for the explanation. I am new into the ESP32, so I can not answer the questions that you put :confused:. Yes. I get it.

Reaction time. ISRs should react to events as timely as possible, so they have to beas short as possible. You have no WiFi, no Serial running? You are shure that the Flash accesses don't need interrupts?

This is solution of the problem. I changed type of the Lambda variable from float to long. Since then I am not getting any error messages from the ISR.

Whandall:
You should not use floating point in an ISR.

floatISRESP32.png

Thank you Whandall and to all contributors for the effort to help me.

Answers for my questions related to ISRs are still welcomed!