ESP32 - not possible to use I2C functions in an ISR? - Page 1
Forum section: electronics / general

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 16, 2021, 04:05:02 pm
content: 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 frameworkattachInterrupt), 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 usedelay, etc. within an ISR. But I'm not doing so here - just calling a sequence of IDFi2c_*functions. The only relation I can see is thati2c_master_cmd_begintakes 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.

author: rstofer
author_rank: Super Contributor
timestamp: October 16, 2021, 04:15:56 pm
content: 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.

author: rstofer
author_rank: Super Contributor
timestamp: October 16, 2021, 04:26:09 pm
content: 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.https://esp32.com/viewtopic.php?t=6480

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 16, 2021, 06:22:00 pm
content: I note that the ESP-IDF documentationsays the followingabouti2c_master_cmd_begin:QuoteDoes an ISR function count as a separate 'task'?

author: rstofer
author_rank: Super Contributor
timestamp: October 16, 2021, 07:54:55 pm
content: 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 197https://freertos.org/fr-content-src/uploads/2018/07/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdfThe 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!

author: PlainName
author_rank: Super Contributor
timestamp: October 17, 2021, 12:27:32 am
content: QuoteYes - it's how tasks are switched in a RTOS, after all. If the API docs say code isn't re-entrant then you really don't want to call it from an ISR.Also, I would be quite circumspect about calling library code from an ISR simply because it's not known what it might be doing. Although, actually, you know " that i2c_master_cmd_begin takes a timeout value in ticks" so therefore must sit around waiting in order to have a timeout. Definitely an ISR no-no.As already mentioned, use the ISR to notify a non-ISR task that the I2C stuff needs handling. It's easy enough to do in FreeRTOS, but I don't know Arduino-speak so can't say how it might look there.

author: ejeffrey
author_rank: Super Contributor
timestamp: October 17, 2021, 02:56:10 am
content: Quote from: HwAoRrDk on October 16, 2021, 06:22:00 pmYes, basically. It's actually more restrictive than that.At the simplest level a function is not thread safe if, while executing, it leaves global memory in a state where a second call to the function would not behave properly, It doesn't matter if these are from two threads or a main thread and ISR.The difference is that generally an ISR should never try to acquire a mutex or take any operation that blocks or waits. The reason is that the main task is not able to continue executing until the ISR completes and returns. So if you try to acquire a mutex and it is already held you will just block. Some functions are thread safe due to using a lock to protect shared data: these are not safe to call from an ISR.

author: rstofer
author_rank: Super Contributor
timestamp: October 17, 2021, 07:31:35 am
content: https://www.digikey.com/en/maker/projects/introduction-to-rtos-solution-to-part-9-hardware-interrupts/3ae7a68462584e1eb408e1638002e9edThe entire series might be worthwhile.