Fix hardware-verified bugs: EPD stack overflow and busy-wait spin
Two crashes found flashing to real hardware: - epd_display_stream's 4KB SPI chunk buffer was a stack local, but the default main task stack (3584 bytes) is smaller than that alone -- Guru Meditation stack protection fault. Made it static instead, and bumped CONFIG_ESP_MAIN_TASK_STACK_SIZE to 8192 for headroom in the rest of the boot call chain (provisioning -> QR render -> eventually the HTTP fetch cycle all run in this one task). - epd_wait_busy() polled with a 1ms vTaskDelay, which rounds down to 0 FreeRTOS ticks at the default 100Hz tick rate -- so it never actually blocked, tight-spinning the CPU for the panel's real refresh time (15-30+s for a full-color pass) and starving the idle task long enough to trip the 5s task watchdog. Bumped to 20ms, safely >=1 tick regardless of tick rate. Also updates the EPD pin defaults to the board's actual wiring (CLK=20 MOSI=19 CS=18 DC=9 RST=10 BUSY=11), confirmed working on hardware.
This commit is contained in:
@@ -2,11 +2,17 @@ menu "E-Paper Display (epd7in3e) Configuration"
|
|||||||
|
|
||||||
config EPD_PIN_CLK
|
config EPD_PIN_CLK
|
||||||
int "SPI CLK (SCLK) GPIO"
|
int "SPI CLK (SCLK) GPIO"
|
||||||
default 18
|
default 20
|
||||||
help
|
help
|
||||||
Not physically wired yet -- these are sensible ESP32-C6 devkit
|
Defaults match the wiring on the reference build: CLK=20,
|
||||||
defaults (avoiding strapping pins 4/5/8/9/15 and the USB-JTAG
|
MOSI(DIN)=19, CS=18, DC=9, RST=10, BUSY=11. Override to match
|
||||||
pins 12/13). Override to match your actual wiring.
|
your own wiring if different.
|
||||||
|
|
||||||
|
Note: DC uses GPIO9, one of the ESP32-C6's strapping pins
|
||||||
|
(sampled at reset to help select boot mode). It's only sampled
|
||||||
|
during power-on/reset, so it's safe to drive as a normal output
|
||||||
|
once the app is running -- but if flashing/boot ever misbehaves,
|
||||||
|
check whether the EPD is pulling this line low during reset.
|
||||||
|
|
||||||
config EPD_PIN_MOSI
|
config EPD_PIN_MOSI
|
||||||
int "SPI MOSI (DIN) GPIO"
|
int "SPI MOSI (DIN) GPIO"
|
||||||
@@ -14,19 +20,19 @@ menu "E-Paper Display (epd7in3e) Configuration"
|
|||||||
|
|
||||||
config EPD_PIN_CS
|
config EPD_PIN_CS
|
||||||
int "SPI CS GPIO"
|
int "SPI CS GPIO"
|
||||||
default 20
|
default 18
|
||||||
|
|
||||||
config EPD_PIN_DC
|
config EPD_PIN_DC
|
||||||
int "Data/Command GPIO"
|
int "Data/Command GPIO"
|
||||||
default 21
|
default 9
|
||||||
|
|
||||||
config EPD_PIN_RST
|
config EPD_PIN_RST
|
||||||
int "Reset GPIO"
|
int "Reset GPIO"
|
||||||
default 22
|
default 10
|
||||||
|
|
||||||
config EPD_PIN_BUSY
|
config EPD_PIN_BUSY
|
||||||
int "Busy GPIO"
|
int "Busy GPIO"
|
||||||
default 23
|
default 11
|
||||||
|
|
||||||
config EPD_SPI_CLOCK_HZ
|
config EPD_SPI_CLOCK_HZ
|
||||||
int "SPI clock speed (Hz)"
|
int "SPI clock speed (Hz)"
|
||||||
|
|||||||
@@ -34,8 +34,14 @@ static void epd_delay_ms(uint32_t ms)
|
|||||||
/* BUSY: LOW = busy, HIGH = idle. */
|
/* BUSY: LOW = busy, HIGH = idle. */
|
||||||
static void epd_wait_busy(void)
|
static void epd_wait_busy(void)
|
||||||
{
|
{
|
||||||
|
/* A full refresh on this panel can hold BUSY low for 15-30+ seconds.
|
||||||
|
* A 1ms poll delay rounds down to 0 FreeRTOS ticks at the default
|
||||||
|
* 100Hz tick rate, so vTaskDelay() never actually blocks -- confirmed
|
||||||
|
* on hardware as a tight busy-spin that starves the idle task long
|
||||||
|
* enough to trip the 5s task watchdog. 20ms is safely >= 1 tick
|
||||||
|
* regardless of tick rate and still imperceptibly responsive here. */
|
||||||
while (gpio_get_level((gpio_num_t)CONFIG_EPD_PIN_BUSY) == 0) {
|
while (gpio_get_level((gpio_num_t)CONFIG_EPD_PIN_BUSY) == 0) {
|
||||||
epd_delay_ms(1);
|
epd_delay_ms(20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +213,12 @@ esp_err_t epd_display_stream(epd_read_fn_t read_fn, void *ctx)
|
|||||||
gpio_set_level((gpio_num_t)CONFIG_EPD_PIN_DC, 1);
|
gpio_set_level((gpio_num_t)CONFIG_EPD_PIN_DC, 1);
|
||||||
gpio_set_level((gpio_num_t)CONFIG_EPD_PIN_CS, 0);
|
gpio_set_level((gpio_num_t)CONFIG_EPD_PIN_CS, 0);
|
||||||
|
|
||||||
uint8_t chunk[EPD_SPI_CHUNK_SIZE];
|
/* Static rather than a stack local: the default main task stack
|
||||||
|
* (CONFIG_ESP_MAIN_TASK_STACK_SIZE, 3584 bytes) is smaller than this
|
||||||
|
* 4KB chunk buffer alone, let alone with the rest of the call chain --
|
||||||
|
* confirmed on hardware as a stack protection fault/crash. Not
|
||||||
|
* reentrant, but this driver only ever runs from one task at a time. */
|
||||||
|
static uint8_t chunk[EPD_SPI_CHUNK_SIZE];
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
size_t n;
|
size_t n;
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
|
|||||||
@@ -7,3 +7,10 @@ CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
|||||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||||
|
|
||||||
|
# The whole boot flow (provisioning, QR rendering, and eventually the HTTP
|
||||||
|
# fetch cycle) runs in the single default "main" task. The 3584-byte
|
||||||
|
# default already crashed with a stack protection fault once (a 4KB SPI
|
||||||
|
# chunk buffer that's since been moved off the stack) -- bumping this gives
|
||||||
|
# headroom for the rest of that call chain plus the upcoming HTTP client.
|
||||||
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||||
|
|||||||
Reference in New Issue
Block a user