-->
Page 1 of 1

Exception with Wire (I2C) in ISR

PostPosted: Wed Mar 27, 2019 9:53 am
by btidey
I'm reading an ADS1115 ADC in continuous mode using an interrupt service routine triggered by the alert signal. The routine is fairly short just reading value into a cyclic buffer and with a i2c clock of 350KHz takes about 120 uSec to execute.

That works fine and will run forever no problem.

However, in the foreground loop I have server.handleClient(); to respond to web requests. When I do a request then I normally get an exception (occasionally the first one succeeds).

If I take Wire code out of the ISR then there are no exceptions; the ISR is still being triggered by the alert and executed at the same rate.

ISR code is
Code: Select allvoid ICACHE_RAM_ATTR adsAlertIsr() {
   readCount++;
   Wire.requestFrom(ADS_ADDRESS,2);
   ADSBuffer[ADSBufferHead] = (Wire.read() << 8) | Wire.read();
   ADSBufferHead = (ADSBufferHead + 1) & (ADS_BUFFER_SIZE - 1);
}


Exception stack trace is
Code: Select allException 0: Illegal instruction
PC: 0x40210fa8: TwoWire::requestFrom(int, int) at D:\Utils\Arduino\hardware\espressif\esp8266\libraries\Wire\Wire.cpp line 132
EXCVADDR: 0x00000000

Decoding stack results
0x40100e38: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 165
0x40100d78: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 133
0x40100e49: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 169
0x40100e38: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 165
0x40100d78: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 133
0x40210fb7: TwoWire::requestFrom(int, int) at D:\Utils\Arduino\hardware\espressif\esp8266\libraries\Wire\Wire.cpp line 134
0x40100d78: interrupt_handler(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring_digital.cpp line 133
0x4021ac89: spiffs_phys_rd(spiffs*, u8_t, spiffs_file, u32_t, u32_t, u8_t*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_cache.cpp line 163
0x4021866d: spiffs_obj_lu_find_entry_visitor(spiffs*, spiffs_block_ix, int, u8_t, spiffs_obj_id, spiffs_visitor_f, void const*, void*, spiffs_block_ix*, int*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_nucleus.cpp line 186
0x40213c74: esp_yield() at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_main.cpp line 91
0x401002c1: __wrap_spi_flash_read(uint32_t, uint32_t*, size_t) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_phy.cpp line 309
0x402112d5: EspClass::flashRead(unsigned int, unsigned int*, unsigned int) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\Esp.cpp line 639
0x40215df8: spiffs_hal_read(unsigned int, unsigned int, unsigned char*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs_hal.cpp line 67
0x4021802b: spiffs_object_find_object_index_header_by_name_v(spiffs*, spiffs_obj_id, spiffs_block_ix, int, void const*, void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_nucleus.cpp line 1675
0x4021aca5: spiffs_phys_rd(spiffs*, u8_t, spiffs_file, u32_t, u32_t, u8_t*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_cache.cpp line 155
0x40220e11: _vsnprintf_r at ../../../../../../dl/newlib-xtensa/newlib/libc/stdio/vsnprintf.c line 73
0x40218610: spiffs_obj_lu_find_entry_visitor(spiffs*, spiffs_block_ix, int, u8_t, spiffs_obj_id, spiffs_visitor_f, void const*, void*, spiffs_block_ix*, int*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_nucleus.cpp line 169
0x40217fb0: spiffs_object_find_object_index_header_by_name_v(spiffs*, spiffs_obj_id, spiffs_block_ix, int, void const*, void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_nucleus.cpp line 1664
0x40212bfb: String::String(String const&) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 38
0x40219f8a: spiffs_object_find_object_index_header_by_name(spiffs*, u8_t const*, spiffs_page_ix*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_nucleus.cpp line 1705
0x40216c89: realloc(void*, size_t) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\umm_malloc\umm_malloc.cpp line 1508
0x40212968: String::changeBuffer(unsigned int) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 179
0x40217a51: SPIFFS_stat(spiffs*, char const*, spiffs_stat*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs\spiffs_hydrogen.cpp line 769
0x40212968: String::changeBuffer(unsigned int) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 179
0x402157b2: SPIFFSImpl::exists(char const*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\spiffs_api.cpp line 63
0x402129e6: String::reserve(unsigned int) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 148
0x402169c0: _umm_free(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\umm_malloc\umm_malloc.cpp line 1304
0x40216de4: free(void*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\umm_malloc\umm_malloc.cpp line 1764
0x4021cc01: fs::FS::exists(char const*) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\FS.cpp line 229
0x402113fe: fs::FS::exists(String const&) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\FS.cpp line 233
0x40202283: handleFileRead(String) at D:\Documents\Arduino\PowerMeterDev/PowerMeterDev.ino line 338
0x402129e6: String::reserve(unsigned int) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 148
0x40212be0: String::operator=(String const&) at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\WString.cpp line 259
0x40202386: std::_Function_handler ::_M_invoke(const std::_Any_data &) at D:\Documents\Arduino\PowerMeterDev/PowerMeterDev.ino line 718
0x40207412: std::function ::operator()() const at d:\utils\arduino\hardware\espressif\esp8266\tools\xtensa-lx106-elf\xtensa-lx106-elf\include\c++\4.8.2/functional line 2465
0x4020753a: ESP8266WebServer::_handleRequest() at D:\Utils\Arduino\hardware\espressif\esp8266\libraries\ESP8266WebServer\src\ESP8266WebServer.cpp line 607
0x40100c50: millis() at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_wiring.cpp line 186
0x4021c0a8: WiFiServer::available(unsigned char*) at D:\Utils\Arduino\hardware\espressif\esp8266\libraries\ESP8266WiFi\src\WiFiServer.cpp line 116
0x4020772c: ESP8266WebServer::handleClient() at D:\Utils\Arduino\hardware\espressif\esp8266\libraries\ESP8266WebServer\src\ESP8266WebServer.cpp line 308
0x40203520: loop() at D:\Documents\Arduino\PowerMeterDev/PowerMeterDev.ino line 733
0x40213d24: loop_wrapper() at D:\Utils\Arduino\hardware\espressif\esp8266\cores\esp8266\core_esp8266_main.cpp line 125


Anybody doing something similar without problems or who has an idea what is going on?

My best guess is that somehow there is a deadly embrace with WIre somewhere and the ISR gets blocked and re-entered.

I did also try using the brzo i2c library instead of Wire but got the exceptions in the same way.

Re: Exception with Wire (I2C) in ISR

PostPosted: Mon Apr 08, 2019 4:02 am
by btidey
After a bit more investigation I have found that this is not directly related to the web server code interacting with the Wire in the ISR.

Instead it is related to the use of SPIFFS which was being used within most of the server handler routines.

If I have a server handler which just returns html that is generated by code then all is fine and I can even see on a logic analyser that the ISR is successfully interrupting the server handler code with no side effects.

However, if I add a piece of code that performs any SPIFFS function then that will trigger the exception.

So it is something within SPIFFS that causes the incompatibility with using Wire in the ISR.

Re: Exception with Wire (I2C) in ISR

PostPosted: Tue Apr 09, 2019 4:45 pm
by btidey
While I am still trying to understand the fundamental issue, I now have a reasonable work-around.

I wrap each piece of code that uses Spiffs calls (dir, read, write etc) with functions that pause and resume the ADS alert interrupts using detachInterrupt and attachInterrupt.

I then minimise the use of those areas of code. E.g. live status html GET requests don't need this wrapping.

The net result is that for most usage the ADS sampling remains continuous. On those occasions such as switching web pages or saving data to a file then there is a small pause in sampling of about 200mSec.