Products > Embedded Computing

ESP32 - not possible to use I2C functions in an ISR?

(1/2) > >>

I was playing around with an ESP32 yesterday, writing some code that communicated with an I2C slave device. I'm using the Arduino IDE and environment, as I couldn't be arsed to install all the ESP-IDF Eclipse toolchain. The slave device has an interrupt output signal that I wanted to handle, so I created an ISR (using Arduino framework attachInterrupt), within which I performed an I2C read of a register in the slave device. But whenever the ISR executed, the ESP32 would crash and reset, with "Guru Meditation Error: Core 1 panic’ed (Interrupt wdt timeout on CPU1)".

All the info I could find with a brief search was related to getting that error when you try to use delay, etc. within an ISR. But I'm not doing so here - just calling a sequence of IDF i2c_* functions. The only relation I can see is that i2c_master_cmd_begin takes a timeout value in ticks. Does that behind the scenes use similar mechanisms that don't work in an ISR?

I don't know anything about the inner workings of the ESP32 RTOS (FreeRTOS, right?), so I don't know how ISRs are actually handled or what mechanisms delay/timeout code would use that are incompatible with that, but I am curious.

ETA:  What follows is probably not a good solution considering an RTOS...

I don't know anything about ESP32 interrupts but usually, when you have responded to an interrupt and entered an ISR, further interrupts are disabled until a Return-From-Interrupt instruction is issued.  This type of thing is often handled by the compiler.  It knows when it is executing interrupt code versus mainline code.  This blocking means the WDT interrupt won't happen and the timer times out before the I2C interrupt is complete.  Some CPUs don't have a Return-From-Interrupt instruction and rely on the programmer deliberately enabling interrupts just before returning from the handler.  Are you re-enabling interrupts?

Depending on the overall architecture of the project in terms of interrupts used and whether there is a priority scheme in place, it is sometimes as easy as just re-enabling interrupts while in the handler knowing that you won't get another pin interrupt (specific to that pin) until you leave the handler.

That's why many ARM devices use the NVIC interrupt controller.  It allows nested interrupts in a priority order and each level can be turned off and on individually.

If you're using an RTOS, you need to rethink doing much of anything in the ISR.  You should post something to an event queue and let the RTOS handle scheduling.

I note that the ESP-IDF documentation says the following about i2c_master_cmd_begin:

--- Quote ---The I2C APIs are not thread-safe, if you want to use one I2C port in different tasks, you need to take care of the multi-thread issue.
--- End quote ---

Does an ISR function count as a separate 'task'?

I wouldn't think so, you don't start up an ISR the way you do a task.  That's why there are separate '...FromISR()' functions that set queues and semaphores from an unscheduled function (interrupt handler).

Like xSemaphoreGiveFromISR() on page 197

The interrupt happens without notifying the RTOS, it is strictly hardware.  Then when the ISR gets activated, it sets a semaphore which the RTOS scheduler notices (because you called a ..FromISR() function to tell it) and then schedules a task to respond to the interrupt semaphore.  Essentially nothing gets done in the interrupt handler itself.  It simply sets a semaphore or inserts a value into a queue.

Keep the interrupt handler short!


[0] Message Index

[#] Next page

There was an error while thanking
Go to full version