Skip to content

Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed) ESP32 ARDUINO IDE #855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
igrr opened this issue Nov 22, 2017 · 31 comments
Labels
Type: For reference Common questions & problems

Comments

@igrr
Copy link
Member

igrr commented Nov 22, 2017

From @jt040-jetnet on November 21, 2017 10:21

i am using esp32 in arduino core
i am using touch key feature in interrupt
getting below error and my esp32 gets restart ,
please suggest how to fix it,
Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed)
Register dump:
PC : 0x400d13f4 PS : 0x00060034 A0 : 0x40082388 A1 : 0x3ffc0bf0
A2 : 0x00000000 A3 : 0x3ffc3590 A4 : 0x00000001 A5 : 0x40086408
A6 : 0x00000003 A7 : 0x00060e23 A8 : 0x80081142 A9 : 0x04000c01
A10 : 0x00060e23 A11 : 0x00000000 A12 : 0x00060023 A13 : 0x3ffc0bd0
A14 : 0x3ffc0c18 A15 : 0x00000001 SAR : 0x0000001e EXCCAUSE: 0x00000007
EXCVADDR: 0x00000000 LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xffffffff

Backtrace: 0x400d13f4:0x3ffc0bf0 0x40082385:0x3ffc0c10 0x4008f54f:0x00000000

Rebooting...
ets Jun 8 2016 00:22:57

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0010,len:4
load:0x3fff0014,len:716
load:0x40078000,len:0
load:0x40078000,len:11572
entry 0x40078a14

Copied from original issue: espressif/esp-idf#1299

@igrr
Copy link
Member Author

igrr commented Nov 22, 2017

Fwiw, this looks like it is caused by interrupt firing while cache is disabled, see #489.

@stickbreaker
Copy link
Contributor

the code that was triggered by the interrupt did not have the IRAM_ATTR attribute.

Most O/S library function cannot be called from an ISR. Review your code, make sure that every function it calls is local. and has the IRAM_ATTR attribute. For example, you Cannot call Serial.print() from an ISR.

Chuck.

@jt040-jetnet
Copy link

sorry to say but i didnt get how to fix the issue as i am using arduino ide

@stickbreaker
Copy link
Contributor

Read through this info for a more detailed discussion:

General Notes

The function you attach to the interrupt:

static volatile uint16_t intTriggerCount=0; // this variable will be changed in the ISR, and Read in main loop
// static: says this variable is only visible to function in this file, its value will persist, it is a global variable
// volatile: tells the compiler that this variables value must not be stored in a CPU register, it must exist
//   in memory at all times.  This means that every time the value of intTriggeredCount must be read or
//   changed, it has be read from memory, updated, stored back into RAM, that way, when the ISR 
//   is triggered, the current value is in RAM.  Otherwise, the compiler will find ways to increase efficiency
//   of access.  One of these methods is to store it in a CPU register, but if it does that,(keeps the current
//   value in a register, when the interrupt triggers, the Interrupt access the 'old' value stored in RAM, 
//   changes it, then returns to whatever part of the program it interrupted.  Because the foreground task,
//   (the one that was interrupted) has no idea the RAM value has changed, it uses the value it 'know' is 
//   correct (the one in the register).  

void IRAM_ATTR isr(){  //IRAM_ATTR tells the complier, that this code Must always be in the 
// ESP32's IRAM, the limited 128k IRAM.  use it sparingly.
intTriggeredCount++;
}

void setup(){
Serial.begin(115200);
pinMode(5,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(5),isr,FALLING);
}

void loop(){
// magic happens her
static uint16_t old_Value=0; // this variable is only visible inside loop(),
// but it is persistent, It is only init'ed
// once, and each time through loop() it remembers its prior value

if(old_Value != intTriggeredCount){
  old_Value = intTriggeredCount; // something to compare against
  Serial.printf(" Someone grounded pin 5 again, it is the %d's time! Better call the Cops!",intTriggeredCount);
}

Chuck.

@jt040-jetnet
Copy link

thanks stickbreaker
IRAM_ATTR works for my application

@tcsaba101
Copy link

image

Thanks stickbreaker!
Also working in our case!

@copercini copercini added the Type: For reference Common questions & problems label Nov 24, 2017
@copercini
Copy link
Contributor

Closed due it was solved =)

@echoGee
Copy link

echoGee commented Apr 25, 2018

@stickbreaker @igrr : I logged an issue #1352 . Could you kindly look at it to see if its similar to #855 ? The IRAM_ATTR solution mentioned here didn't work though.

@felixqueisler
Copy link

felixqueisler commented Apr 26, 2018

Beware, not only the ISR (Interrupt) has to be in IRAM!
Every function called from the ISR also needs to be declared as IRAM_ATTR yourFunction() {}.

I made the mistake of using a normal function in my ISR and my ESP32 kept panicing when my code wanted to use the Preferences library (saving data to flash memory). I fixed it by also writing IRAM_ATTR before those function names.

Edit: It still happened and I found that you can't use analogRead() in the ISR either. I fixed it by setting a flag for it in the ISR. Now my code reads the pin in the superloop. A disadvantage is, that the value lags behind by 1 ISR.

@stickbreaker
Copy link
Contributor

@freemind64 Please edit your post, put three backticks(left apostrophe) on a separate line before your code, and three backticks on a new line after your code. It makes it easier to read.

Chuck.

@freemind64
Copy link

freemind64 commented Jun 5, 2018

sorry, I couldn't edit that code. the main issue are these function:

void IRAM_ATTR doEncoderA()
void IRAM_ATTR doEncoderB()

when I test encoder in a separate program , it doesn't show any error .
esp_encoder.zip

but in this one :
gsm_esp32_gc.zip

when I want to start calibration process, esp32 begin to reset with error in this topic.
please help me about this error, thank you.

@stickbreaker
Copy link
Contributor

@freemind64

//====================================

void  doEncoderA() {

  // look for a low-to-high on channel A

  if (digitalRead(encoder0PinA) == HIGH) {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }

    else {
      encoder0Pos = encoder0Pos - 1;         // CCW

    }
  }

  else   // must be a high-to-low edge on channel A
  {

    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == HIGH) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}


void  doEncoderB() {

  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH) {
    // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  // Look for a high-to-low on channel B
  else {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinA) == LOW) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}
//====================================

These two functions need to be resident in IRAM whenever the interrupt occurs. The ESP32 stores code in FLASH ROM, but it has to copy it into RAM before it can execute it. It uses a small RAM cache (64kB) as a buffer scratch pad, normal code is automatically copied from FLASH to ICACHE when needed, but interrupt code is assumed to always be present in RAM. So, You must tell the compiler to place these functions, and all the code they execute into IRAM. This is accomplished by:

void ATTR IRAM doEncoderA() {
//cut
void ATTR IRAM doEncoderB() {
//cut

Something else you might consider, I have see comments about pin interrupts triggering on CHANGE even though you have specified RISING or FALLING. I have not had time to test these conjectures, but since it would effect your code you might want to test it.
I would create two static booleans to hold the last state of the pins and compare it to the supposed new state:

static volatile bool lastState0A=false; //persistent, will be changed during interrupt
static volatile bool lastState0B=false;
static volatile int encoder0Pos=0; // Why did you define pos as a FLOAT? it is an integer value?

void  ATTR IRAM doEncoderA() {

bool currentState = digitalRead(encoder0PinA);

if(currentState == lastState) return; // glitch nothing to do

lastState0A = currentState; // Store current pin0 State

  // look for a low-to-high on channel A
if (currentState == HIGH) {
    // check channel B to see which way encoder is turning
  if (lastState0B == LOW) {
    encoder0Pos = encoder0Pos + 1;         // CW
    }
  else {
    encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }
else{   // must be a high-to-low edge on channel A
     // check channel B to see which way encoder is turning
  if (lastState0B == HIGH) {
    encoder0Pos = encoder0Pos + 1;          // CW
    }
  else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}

Chuck.

@freemind64
Copy link

freemind64 commented Jun 5, 2018

Using this
void ATTR IRAM doEncoderA() {

and define encoder0Pos as an int solved my problem.
I know its an int but is there any deference to save any variable except the space they needed?
but I used old function in my program.
thanks @stickbreaker stickbreaker

@stickbreaker
Copy link
Contributor

@freemind64 The major difference between integer and float data types is speed of execution.
The ESP32 has a FPU(floating Point Unit), basically a sub processor to handle floating point math, but there is a performance penalty initializing, handing data off, waiting for results.

A float is a complex structure, that stores an approximation of a number. It usually cannot exactly record a value.

simple explanation of internal FLOAT storage

better explanation of FLOAT storage

Both of these explanations show how complex FLOATs are. Think about how you would code a function to update the bit value of a FLOAT directly, it is tough. So, unless you need decimal value approximations, use integer types for performance.

Chuck.

@freemind64
Copy link

freemind64 commented Jun 6, 2018

@stickbreaker again another problem that might be about this issue.
in function
void gc()
I want to control a dc motor position but the code in the while loop didn't work.
prr is the setpoint
gpr in motor position

 while (prr != gpr); 
{
      gpr = (int)(abs(encoder0Pos) * anglm);

      Serial.println("pr > gp");
      Serial.print("gp:"); Serial.println(gpr);
      Serial.print("dp:"); Serial.println(dpr);
      yield();
    }

even serial print didn't work.
gsm_esp32_gc.zip

@stickbreaker
Copy link
Contributor

stickbreaker commented Jun 6, 2018

@freemind64 use three backtick's ``` on a line by themselves to bracket your code.

What was the while loop supposed to do? Did it not compile? Did it not loop?

If you want my help you are going to have to make it easy for me. Why would you expect me to correctly guess what you expected the code to do? Without even telling me what you observed the code doing?
Couple of comments please?
what is gc,gp,aglm,gpr,prr,pr,dpr.

You do realize that vowels do not cost extra?

What are you trying to achieve with:

void gc() {
  void doEncoderA();
  void doEncoderB();

Full text of function:, Snarky comments added by me

void gc() {
  void doEncoderA();
  void doEncoderB();
  int dpr = 0; // depreciation rate
  int z = 0; // last letter of the alphabet
  encoder0Pos = (int)(gp / anglm); // reduce gross product by angular mass
  gpr = gp; // set gross product rate to gross product
  prr = pr * 10000; // set prior rate to prior times ten thousand?
  Serial.print("encoder0Pos:"); Serial.println(encoder0Pos);
  if (prr < gpr) { // if prior rate less than gross product rate then cow it.
    cw();
    do {
      gpr = (int)(abs(encoder0Pos) * anglm) ;
      dpr = gpr - prr;
      Serial.println("pr < gp");
      Serial.print("gp:"); Serial.println(gpr);
      Serial.print("dp:"); Serial.println(dpr);
      yield();
    } while (dpr  == 0);
    stp();
    Serial.println(gpr);
  }
  if (prr > gpr) {
    ccw();
    Serial.println("pr > gp");
    while (prr != gpr); {
      gpr = (int)(abs(encoder0Pos) * anglm);
    //  dpr = prr - gpr;
      Serial.println("pr > gp");
      Serial.print("gp:"); Serial.println(gpr);
      Serial.print("dp:"); Serial.println(dpr);
      yield();
    }
    Serial.print("gp:"); Serial.println(gpr);
    stp();
  }
  Serial.print("encoder0Pos:"); Serial.println(encoder0Pos);
  encoder0Pos = 0;
  EEPROM.writeInt(30,  gp);
  EEPROM.commit();
}
//=============================

Chuck.

@freemind64
Copy link

freemind64 commented Jun 6, 2018

ok.
first ,thanks to teach me to ask my question in right way.
gc() is the function to control the motor position.
anglm is the (100/anglt) and anglt is total motor angular rotation from close to open.
dpr is the difference between setpoin (pr*10000=prr) and gp (gate position = gpr) . I used prr and gpr to be same as encoder0Pos.
prr = pr * 10000; because doing everything in integer .
when you send the set point command , it stored in pr. ( for example 10 % ).
then we want potion of 10 % openning.

void gc() {
 int dpr = 0; // dpr is the difference between setpoin (pr=prr) and gp (gate position = gpr) 
 encoder0Pos = (int)(gp / anglm); //anglm is the (100/anglt) and anglt is total motor angular 
//rotation from close to open.
 gpr = gp; // gate position = gpr
 prr = pr * 10000; // setpoin (pr=prr) / because doing everything in integer .?
 Serial.print("encoder0Pos:"); Serial.println(encoder0Pos);
 if (prr < gpr) { // if set point is less than gate position then 
   cw(); // clockwise rotation
   do {
     gpr = (int)(abs(encoder0Pos) * anglm) ;
     dpr = gpr - prr;
     Serial.println("pr < gp");
     Serial.print("gp:"); Serial.println(gpr);
     Serial.print("dp:"); Serial.println(dpr);
     yield();
   } while (dpr  == 0);
   stp();
   Serial.println(gpr);
 }
 if (prr > gpr) {    //if set point is greatar than gate position then 
   ccw(); // counter clockwise rotation
   Serial.println("pr > gp");
   while (prr != gpr); {
     gpr = (int)(abs(encoder0Pos) * anglm);
   //  dpr = prr - gpr;
     Serial.println("pr > gp");
     Serial.print("gp:"); Serial.println(gpr);
     Serial.print("dp:"); Serial.println(dpr);
     yield();
   }
   Serial.print("gp:"); Serial.println(gpr);
   stp();
 }
 Serial.print("encoder0Pos:"); Serial.println(encoder0Pos);
 encoder0Pos = 0;
 EEPROM.writeInt(30,  gp);
 EEPROM.commit();
}

@stickbreaker
Copy link
Contributor

@freemind64
The way I would code this function is a little different.

static volatile int32_t currentPos=0; // init to 0
static volatile bool motorRunning=false; // motor is not moving
static volatile bool lastStateA = false;
static volatile bool lastStateB = false;

#define clockWiseLimitPin 15 //when gpio15 is low, limitswitch is active.
#define counterClockWiseLimitPin 16 //when gpio16 is low, limitswitch is active.
#define encoderAPin 17
#define encoderBPin 18

bool ATTR IRAM stopRotation(){ //IRAM because this function can also be called from Interrupt
// actually control the motor here.  If successful, mark motorRunning state.
motorRunning = false;
return !motorRunning;
}

bool rotateClockWise(){
// actually control the motor here.  If successful, mark motorRunning state.
motorRunning = true;
return motorRunning;
}

void ATTR IRAM encoderA(){ // a/b rotation encoder, 
bool currentState = digitalRead(encoderAPin); //
if ( lastStateA == currentState) return;  // spurious interrupt, ignore it 
lastStateA = currentState;
if ( currentState ) { //pin went high
  if( !lastStateB ) { // clockWise rotation, because B is Low
    currentPos++; // increment position
    if( !digitalRead(clockWiseLimitPin)) { // limit is active, stop motor
      stopRotation();
      }
    }
  else { // counterClockWise rotation
    currentPos--; // decrement position 
    if( !digitalRead(counterClockWiseLimitPin)) { // limit is active stop motor
      stopRotation();
      }
    }
  }
else { // pin went low
  if( lastStateB ){ // clockWise Rotation, because B is high and A went Low
    currentPos++;
    if( !digitalRead(clockWiseLimitPin)) { // limit is active, stop motor
      stopRotation();
      }
    }
  else { // counterClockWise rotation
    currentPos--;
    if( !digitalRead(counterClockWiseLimitPin)) { // limit is active stop motor
      stopRotation();
      }
    }
  }
}

bool gotoPosition(int32_t newPos, uint32_t timeOut){

uint32_t tick=millis(); // movement starting tick, in milliseconds

bool successfulMove = false;

If (newPos > currentPos){ // move clockwise
  if (!rotateClockWise()) {  // motor failed when attempting clockwise rotation
    // this failure could be because the motor is at the clockWise limit also
    return false;
    }
  while(((millis()-tick)<timeOut) && (newPos > currentPos) && motorRunning){  
    // motorRunning is volatile, updated if rotation limit exceeded.
    // currentPos is volatile, updated by encoder interrupts.
    // timeOut will handle millis() overflow    
    yield(); // wait for motor to move enough
    }
  if (!stopRotation()){ // Motor stop failed
    return false;
    }   
  successfulMove = (currentPos <= newPos);
  }
else if ( newPos < currentPos){
  if (!rotateCounterClockWise()) { // motor failed when attempt counterClockWise rotation
    return false;
    }
  while(((millis()-tick)<timeout) && (newPos < currentPos) && motorRunning){
    yield(); 
    }
  if (!stopRotation()){ // Motor stop failed
    return false;
    }
  successfulMove = (currentPos<=newPos);
  }
else { // newPos == currentPos, so nothing to do
  successfulMove = true;
  }
return successfulMove;
}

Calculate the new position before you pass it to the function, all the angular scaling should happen
at a higher level of code. Make your motor driver calls as primitive as possible.

Short, simple and descriptive.

Don't use abbreviate variable names.

Think about how you would support this code in 10 years. I have code I wrote in 1986 in Pascal on a
PC/XT that I still support. That was 32 years ago. Every few years I have to relearn it just to support it.

Chuck.

@freemind64
Copy link

thanks Chuck. It was helpful, just you have to know entire rotation from close to open, when you want to write control loop to reach new position.
the hardware design is :

gpio 12 : close limit switch ---> input pin
gpio 13 : open limit switch ---> input pin
gpio 18 : L298 in1  ---> output pin  in1=1, in2=0 to clockwise  / in1=0 in2=0 to stop
gpio 19 : L298 in2  ---> output pin  in1=0, in2=1 to counter clockwise
gpio 34: encoder phase A
gpio 35: encoder phase B

we need a calibration process to know entire rotation , then we know current position as you wrote in your code.
what are these pins?

#define clockWiseLimitPin 15 //when gpio15 is low, limitswitch is active.
#define counterClockWiseLimitPin 16 //when gpio16 is low, limitswitch is active

they control rotation or limits of motor because they are different.

In Arduino-IDE we must use this IRAM_ATTR ,your code is ATTR IRAM.
much thanks for your precious time.

@stickbreaker
Copy link
Contributor

@freemind64 You call them close limit switch and open limit switch.

I don't know your circuit, so I just created two LOW active switches that activate when a clockwise or counterclockwise limit is reached. I just wanted to show that the limits switches should be monitored inside the encoder interrupt routine. That way the limits are enforced immediately, if a limit is reached the motor should stop. Also, you should add a limit check before you start moving the motor. If the motor is at a clockwise limit, you should not move the motor clockwise. Have the CW() Return an error.

Good catch on the IRAM.

Chuck.

@freemind64
Copy link

@stickbreaker , the code was ok and worked for me , just to complete the issue .
use interrupt for encoder purpose and have the "Guru Meditation Error".
setup :

  void doEncoderA();
  void doEncoderB();

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(34, doEncoderA, RISING);

  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(35, doEncoderB, RISING);

encoder position :

void IRAM_ATTR doEncoderA() {
  // look for a low-to-high on channel A

  if (digitalRead(encoder0PinA) == HIGH) {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }

    else {
      encoder0Pos = encoder0Pos - 1;         // CCW

    }
  }

  else   // must be a high-to-low edge on channel A
  {

    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == HIGH) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}
void IRAM_ATTR doEncoderB() {
  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH) {
    // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  // Look for a high-to-low on channel B
  else {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinA) == LOW) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}

and control function :<thanks to chuck @stickbreaker >

bool gotoPosition(int32_t newPos, uint32_t timeOut) {
  timeOut = 20000;
  gp = EEPROM.readInt(30);
  encoder0Pos = (int32_t)(gp * anglt / 100);
  newPos = (int32_t)(pr * anglt / 100);
  uint32_t tick = millis(); // movement starting tick, in milliseconds

  Serial.print("encoder0Pos:"); Serial.println(encoder0Pos);
  Serial.print("gp:"); Serial.println(gp);
  Serial.print("newPos:"); Serial.println(newPos);
  bool successfulMove = false;

  if (newPos > encoder0Pos) { // move clockwise
    cw();
    if (!rotateClockWise) {  // motor failed when attempting clockwise rotation
      // this failure could be because the motor is at the clockWise limit also
      return false;
    }
    while (((millis() - tick) < timeOut) && (newPos > encoder0Pos) && motorRunning) {
      // motorRunning is volatile, updated if rotation limit exceeded.
      // currentPos is volatile, updated by encoder interrupts.
      // timeOut will handle millis() overflow
      yield(); // wait for motor to move enough
    }
    Serial.print("encoder0Pos: "); Serial.println(encoder0Pos);
    Serial.print("tick: "); Serial.println(tick);
    Serial.print("gp-cw: "); Serial.println(gp);
    EEPROM.writeInt(30,  pr);
    EEPROM.commit();
    stp();
    if (!stopRotation()) { // Motor stop failed
      return false;
    }
    successfulMove = (encoder0Pos <= newPos);
  }
  else if ( newPos < encoder0Pos) {
    ccw();
    if (!rotateCounterClockWise) { // motor failed when attempt counterClockWise rotation
      return false;
    }
    while (((millis() - tick) < timeOut) && (newPos < encoder0Pos) && motorRunning) {
      yield();
    }
    Serial.print("encoder0Pos: "); Serial.println(encoder0Pos);
    Serial.print("tick: "); Serial.println(tick);
    Serial.print("gp-ccw: "); Serial.println(gp);
    EEPROM.writeInt(30,  pr);
    EEPROM.commit();
    stp();
    if (!stopRotation()) { // Motor stop failed
      return false;
    }
    successfulMove = (encoder0Pos <= newPos);
  }
  else { // newPos == currentPos, so nothing to do
    successfulMove = true;
  }
  return successfulMove;
}

@human890209
Copy link

Hi, @stickbreaker
I got the

Cache disabled but cached memory region accessed

Crash error.
I checked my ISR functions. I use ESP.getCycleCount() in it, and it doesn't get the IRAM_ATTR.
I'm wondering if ESP.getCycleCount()is the problem?
And the crash sometimes happens not 100% happens.
Is ESP32's Cached not always disabled or enabled?
Is ESP32 only crashes when cache disabled? So it is not a certain crash?

@stickbreaker
Copy link
Contributor

@human890209, the esp32 has to disable the iCache(instruction Cache, the RAM buffer used to execute code stored in the external FLASH chip) whenever it needs to programmatically access the Data in the FLASH chip(SPIFFS, NVS, PREFERENCES).

The problem occurs because changing data in the FLASH is a 3 step process, first read the PAGE(4kB) into the iCache buffer, that needs to be changed. Second, tell the FLASH chip to erase the selected PAGE(takes about 32ms for the chips hardware to accomplish). Third, apply the changes to the iCache buffer image, then write this updated page(4kB) back to the FLASH.

During this entire sequence NO code can be executed from iCache, only code in IRAM, or ROM is available.

Your ISR(interrupt service routine) must not call any code in iCache. You have to Verify all code you call is in IRAM. AND IRAM is only 128kB total. IRAM is used by the FreeRTOS core, and all of the hardware drivers. So, only a small amount is free for user code.

Chuck

@human890209
Copy link

human890209 commented Sep 23, 2018

Hi, @stickbreaker
Thanks a lot. I made an ESPMOD.getCycleCount() and add the IRAM_ATTR, there is no more Crash.

IRAM is only 128kB total. IRAM is used by the FreeRTOS core, and all of the hardware drivers. So, only a small amount is free for user code.

I see these 2 lines in the table which is displayed after compiling. I see about 46k of IRAM usage. Are all the drivers and FreeRTOS codes in this 46k or it's only a total sum of user code?

section size addr
.iram0.vectors 1024 1074266112
.iram0.text 46464 1074267136

@stickbreaker
Copy link
Contributor

@human890209 should be all of the resident code, this is the recipe from 'tools/sdk/ld/esp32.common.ld'

.iram0.text :
{
/* Code marked as runnning out of IRAM /
_iram_text_start = ABSOLUTE(.);
(.iram1 .iram1.)
libfreertos.a:(.literal .text .literal. .text.
)
libheap.a:multi_heap.(.literal .text .literal.* .text.)
libheap.a:multi_heap_poisoning.(.literal .text .literal.
.text.)
libesp32.a:panic.(.literal .text .literal.
.text.)
libesp32.a:core_dump.(.literal .text .literal.
.text.)
libapp_trace.a:(.literal .text .literal. .text.
)
libxtensa-debug-module.a:eri.(.literal .text .literal.* .text.)
librtc.a:(.literal .text .literal. .text.
)
libsoc.a:rtc_.(.literal .text .literal. .text.)
libsoc.a:cpu_util.(.literal .text .literal.
.text.)
libhal.a:(.literal .text .literal. .text.
)
libgcc.a:lib2funcs.(.literal .text .literal.* .text.)
libspi_flash.a:spi_flash_rom_patch.(.literal .text .literal.
.text.)
libgcov.a:(.literal .text .literal. .text.
)
INCLUDE esp32.spiram.rom-functions-iram.ld
_iram_text_end = ABSOLUTE(.);
_iram_end = ABSOLUTE(.);
} > iram0_0_seg

The more code placed in IRAM the less RAM available for data.

Chuck.

@markosole
Copy link

Hi folks,

I have same problem and I did set up my function like this:
void ATTR IRAM nextsong(){
// my code...
}

But I am getting error: error: expected initializer before 'IRAM'
What could be problem?

Thanks.

@copercini
Copy link
Contributor

@markosole it's missing a underscore, the correct is IRAM_ATTR

@markosole
Copy link

Thanks a lot that works now. Here is sample which may be handy for someone.
interruptNextSong is than used in Loop and has been checked.

portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; 
void IRAM_ATTR handleInterruptNext() {
  portENTER_CRITICAL_ISR(&mux);
  interruptNextSong++;
  portEXIT_CRITICAL_ISR(&mux);
}

Thanks guys.

@QuickSynchro
Copy link

@felixqueisler I have the same problem with the preferences library, that´s why I want to ask if you could share the code you talked about in your post? This is the first time i have encountered such a problem as i am fairly new to ESP32, that´s also why i couldn´t quite understand the solutions posted in this thread. Please, your help would be greatly appreciated.
Thank you.

@felixqueisler
Copy link

@felixqueisler I have the same problem with the preferences library, that´s why I want to ask if you could share the code you talked about in your post? This is the first time i have encountered such a problem as i am fairly new to ESP32, that´s also why i couldn´t quite understand the solutions posted in this thread. Please, your help would be greatly appreciated.
Thank you.

You have an interrupt function defined...

Inside that function you only do the bare minimum. You check for a condition and when it's true you either set a boolean = true or an integer ++; In the main loop you then check for those flags to execute more code and then set the flag false or -- or 0.

@blotfi
Copy link

blotfi commented Sep 19, 2021

Beware, not only the ISR (Interrupt) has to be in IRAM!
Every function called from the ISR also needs to be declared as IRAM_ATTR yourFunction() {}.

I made the mistake of using a normal function in my ISR and my ESP32 kept panicing when my code wanted to use the Preferences library (saving data to flash memory). I fixed it by also writing IRAM_ATTR before those function names.

Edit: It still happened and I found that you can't use analogRead() in the ISR either. I fixed it by setting a flag for it in the ISR. Now my code reads the pin in the superloop. A disadvantage is, that the value lags behind by 1 ISR.

yes there is definitely a problem with analogRead from an ISR (e.g. void IRAM_ATTR onTimer())
Is there another possibility to force the analogRead to be copied to IRAM for such use.
The error guru meditation error: core 1 panic ed cache disabled but cached memory region accessed can be reproduced with:

    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    timerStop(timer);
    request->send(SPIFFS, "/index.html");
    timerRestart(timer);
});

and in the timer ISR:

  void IRAM_ATTR onTimer() { // ici on est toutes les Tsamp = 1 ms
    ValADC = analogRead(Iadc_pin);  // 0-4095
    ...

Stoping the timer does nothing
if someone has a better solution than rising a flag and pulling the analogRead in the loop()
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: For reference Common questions & problems
Projects
None yet
Development

No branches or pull requests