Description
Selecting the MAX31865 as an input driver causes the device to hang after the boot screen.
Root Cause
The hang occurs in SPIDevice::beginTransaction() (the Arduino SPI.beginTransaction() call), which blocks forever on xSemaphoreTake().
The display on M5Stack Core uses the VSPI peripheral, driven by LovyanGFX using its own low-level ESP32 SPI driver. The Arduino SPI object also defaults to VSPI. They share the same hardware peripheral.
In the LVGL flush callback (lv_disp_cb), _lgfx.startWrite() is called followed by pushImageDMA(), which starts a DMA transfer and returns immediately — the bus is still held. endWrite() is only called from lv_flush_wait_cb, which LVGL invokes lazily at the start of the next flush cycle.
Between gui.loop() returning and pid->loop() executing in main.cpp, the LovyanGFX DMA transfer is in flight and the SPI bus is still owned. When MAX31865Driver::read() calls into SPIDevice, SPI.beginTransaction() tries to take the VSPI semaphore and blocks forever.
Affected code
lib-soogh/src/soogh-lgfx.cpp — lv_disp_cb / lv_flush_wait_cb
src/main.cpp — loop(), ordering of gui.loop() and pid->loop()
src/inputdrv.h — MAX31865Driver uses SPI (VSPI)
Proposed Fix
Add lgfx_check_flush() to lib-soogh: if DMA is still busy, wait and call _lgfx.endWrite() to release the bus. Call this from main.cpp::loop() after gui.loop() and before pid->loop().
// soogh-lgfx.cpp
void lgfx_check_flush() {
if (!_lgfx.dmaBusy()) return;
while (_lgfx.dmaBusy()) yield();
_lgfx.endWrite();
}
// main.cpp loop()
gui.loop();
lgfx_check_flush(); // release SPI bus before PID drivers use it
for (PIDLoop* pid : pids) pid->loop();
Description
Selecting the MAX31865 as an input driver causes the device to hang after the boot screen.
Root Cause
The hang occurs in
SPIDevice::beginTransaction()(the ArduinoSPI.beginTransaction()call), which blocks forever onxSemaphoreTake().The display on M5Stack Core uses the VSPI peripheral, driven by LovyanGFX using its own low-level ESP32 SPI driver. The Arduino
SPIobject also defaults to VSPI. They share the same hardware peripheral.In the LVGL flush callback (
lv_disp_cb),_lgfx.startWrite()is called followed bypushImageDMA(), which starts a DMA transfer and returns immediately — the bus is still held.endWrite()is only called fromlv_flush_wait_cb, which LVGL invokes lazily at the start of the next flush cycle.Between
gui.loop()returning andpid->loop()executing inmain.cpp, the LovyanGFX DMA transfer is in flight and the SPI bus is still owned. WhenMAX31865Driver::read()calls intoSPIDevice,SPI.beginTransaction()tries to take the VSPI semaphore and blocks forever.Affected code
lib-soogh/src/soogh-lgfx.cpp—lv_disp_cb/lv_flush_wait_cbsrc/main.cpp—loop(), ordering ofgui.loop()andpid->loop()src/inputdrv.h—MAX31865DriverusesSPI(VSPI)Proposed Fix
Add
lgfx_check_flush()to lib-soogh: if DMA is still busy, wait and call_lgfx.endWrite()to release the bus. Call this frommain.cpp::loop()aftergui.loop()and beforepid->loop().