fix(rlcd): fix ST7305 DMA fragmentation and lockup bug

- Moved temporary draw buffer allocation to the initialization phase and made it persistent in the driver struct.
- Resolved severe heap fragmentation caused by allocating and freeing 15KB per UI frame.
- Fixed a use-after-free corruption where the SPI DMA would read from a freed temporary buffer.
- Fixed WDT timeout caused by OOM failure in esp_lcd_panel_draw_bitmap that left LVGL hanging in wait_for_flushing.
- Added explicit RAM clear command to boot sequence to wipe lingering hardware freeze artifacts.
This commit is contained in:
Adolfo Reyna
2026-07-11 22:40:55 -04:00
parent 6f095081d2
commit 89c9f317c1
+26 -12
View File
@@ -40,6 +40,7 @@ typedef struct {
uint8_t madctl_val;
const st7305_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
uint8_t *draw_buffer;
} st7305_panel_t;
static const st7305_lcd_init_cmd_t st7305_init_cmds[] = {
@@ -103,6 +104,10 @@ esp_err_t esp_lcd_new_panel_st7305(const esp_lcd_panel_io_handle_t io, const esp
st7305->init_cmds = st7305_init_cmds;
st7305->init_cmds_size = sizeof(st7305_init_cmds) / sizeof(st7305_lcd_init_cmd_t);
}
st7305->draw_buffer = heap_caps_malloc(15000, MALLOC_CAP_DMA);
ESP_GOTO_ON_FALSE(st7305->draw_buffer, ESP_ERR_NO_MEM, err, TAG, "no mem for st7305 draw buffer");
memset(st7305->draw_buffer, 0, 15000);
st7305->base.del = panel_st7305_del;
st7305->base.reset = panel_st7305_reset;
st7305->base.init = panel_st7305_init;
@@ -126,6 +131,9 @@ err:
if (panel_dev_config->reset_gpio_num >= 0) {
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
if (st7305->draw_buffer) {
free(st7305->draw_buffer);
}
free(st7305);
}
return ret;
@@ -137,6 +145,9 @@ static esp_err_t panel_st7305_del(esp_lcd_panel_t *panel)
if (st7305->reset_gpio_num >= 0) {
gpio_reset_pin(st7305->reset_gpio_num);
}
if (st7305->draw_buffer) {
free(st7305->draw_buffer);
}
free(st7305);
return ESP_OK;
}
@@ -172,6 +183,18 @@ static esp_err_t panel_st7305_init(esp_lcd_panel_t *panel)
vTaskDelay(pdMS_TO_TICKS(st7305->init_cmds[i].delay_ms));
}
}
// Explicitly clear display RAM to white
if (st7305->draw_buffer) {
memset(st7305->draw_buffer, 0xFF, 15000); // 0xFF is White
uint8_t caset[] = {0x12, 0x2A};
uint8_t raset[] = {0x00, 0xC7};
esp_lcd_panel_io_tx_param(io, ST7305_CMD_CASET, caset, sizeof(caset));
esp_lcd_panel_io_tx_param(io, ST7305_CMD_RASET, raset, sizeof(raset));
esp_lcd_panel_io_tx_color(io, ST7305_CMD_RAMWR, st7305->draw_buffer, 15000);
vTaskDelay(pdMS_TO_TICKS(50));
}
return ESP_OK;
}
@@ -180,14 +203,6 @@ static esp_err_t panel_st7305_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i
st7305_panel_t *st7305 = __containerof(panel, st7305_panel_t, base);
esp_lcd_panel_io_handle_t io = st7305->io;
// Output buffer size is 15000 bytes (400 * 300 / 8)
size_t lcd_buf_size = 15000;
uint8_t *temp_buffer = heap_caps_malloc(lcd_buf_size, MALLOC_CAP_DMA);
if (!temp_buffer) {
return ESP_ERR_NO_MEM;
}
memset(temp_buffer, 0, lcd_buf_size);
const uint8_t *src = (const uint8_t *)color_data;
// Convert from vertical-page format (SSD1306-style) to ST7305 landscape 2x4 block format
@@ -218,9 +233,9 @@ static esp_err_t panel_st7305_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i
// In ST7305 display RAM, White/Light is 1, Black/Dark is 0.
// So we write: 1 (White) if is_pixel_set is false (light), and 0 (Black) if is_pixel_set is true (dark).
if (!is_pixel_set) {
temp_buffer[dest_byte_idx] |= (1 << dest_bit_pos);
st7305->draw_buffer[dest_byte_idx] |= (1 << dest_bit_pos);
} else {
temp_buffer[dest_byte_idx] &= ~(1 << dest_bit_pos);
st7305->draw_buffer[dest_byte_idx] &= ~(1 << dest_bit_pos);
}
}
}
@@ -230,9 +245,8 @@ static esp_err_t panel_st7305_draw_bitmap(esp_lcd_panel_t *panel, int x_start, i
esp_lcd_panel_io_tx_param(io, ST7305_CMD_CASET, caset, sizeof(caset));
esp_lcd_panel_io_tx_param(io, ST7305_CMD_RASET, raset, sizeof(raset));
esp_lcd_panel_io_tx_color(io, ST7305_CMD_RAMWR, temp_buffer, lcd_buf_size);
esp_lcd_panel_io_tx_color(io, ST7305_CMD_RAMWR, st7305->draw_buffer, 15000);
free(temp_buffer);
return ESP_OK;
}