From 259660a0bc6bed6e30ccb9cd4b957c4dbb950aa4 Mon Sep 17 00:00:00 2001 From: GW_MC <72297530+GWMCwing@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:51:49 +0800 Subject: [PATCH] Fix touch screen not responding, but screen still not refreshed. --- main/display/display.cpp | 20 +++- main/display/eink_display_handler.cpp | 137 ++++++++++++++++++-------- main/main.cpp | 4 + 3 files changed, 117 insertions(+), 44 deletions(-) diff --git a/main/display/display.cpp b/main/display/display.cpp index 4ee2e0b..943522d 100644 --- a/main/display/display.cpp +++ b/main/display/display.cpp @@ -3,6 +3,9 @@ #include "esp_log.h" #include "esp_lcd_touch_gt911.h" +#define BUSY_ACTIVE_LEVEL 0 // BUSY pin is active low +#define BUSY_INACTIVE_LEVEL 1 + DisplayHandler::~DisplayHandler() { if (_spi_mutex != nullptr) { vSemaphoreDelete(_spi_mutex); @@ -32,7 +35,10 @@ void DisplayHandler::init_devices(bool set_display_ready /*= true*/) { void DisplayHandler::epd_write_cmd(uint8_t cmd) { ESP_LOGI("DisplayHandler", "epd_write_cmd: waiting to send 0x%02X", cmd); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE("DisplayHandler", "SPI mutex timeout for cmd 0x%02X", cmd); + return; + } _dangerous_epd_write_cmd_without_lock(cmd); xSemaphoreGive(_spi_mutex); ESP_LOGI("DisplayHandler", "epd_write_cmd: 0x%02X done", cmd); @@ -40,7 +46,10 @@ void DisplayHandler::epd_write_cmd(uint8_t cmd) { void DisplayHandler::epd_write_data(uint8_t data) { ESP_LOGI("DisplayHandler", "epd_write_data: waiting to send 0x%02X", data); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE("DisplayHandler", "SPI mutex timeout for data 0x%02X", data); + return; + } _dangerous_epd_write_data_without_lock(data); xSemaphoreGive(_spi_mutex); ESP_LOGI("DisplayHandler", "epd_write_data: 0x%02X done", data); @@ -48,7 +57,10 @@ void DisplayHandler::epd_write_data(uint8_t data) { void DisplayHandler::epd_write_cmd_with_data(uint8_t cmd, const uint8_t* data, size_t data_len) { ESP_LOGI("DisplayHandler", "epd_write_cmd_with_data: waiting to send cmd 0x%02X with %u bytes of data", cmd, (unsigned)data_len); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE("DisplayHandler", "SPI mutex timeout for cmd with data 0x%02X", cmd); + return; + } _dangerous_epd_write_cmd_without_lock(cmd); for (size_t i = 0; i < data_len; ++i) { _dangerous_epd_write_data_without_lock(data[i]); @@ -108,7 +120,7 @@ void DisplayHandler::_epd_init(void) { // Check BUSY pin ESP_LOGI("DisplayHandler", "Waiting for EPD to be ready..."); - while (gpio_get_level(PIN_BUSY) == 0) { // 0=BUSY, 1=FREE + while (gpio_get_level(PIN_BUSY) == BUSY_ACTIVE_LEVEL) { // BUSY is active LOW vTaskDelay(pdMS_TO_TICKS(10)); } ESP_LOGI("DisplayHandler", "EPD is ready."); diff --git a/main/display/eink_display_handler.cpp b/main/display/eink_display_handler.cpp index 24cc767..a3f97cb 100644 --- a/main/display/eink_display_handler.cpp +++ b/main/display/eink_display_handler.cpp @@ -75,7 +75,7 @@ void EInkDisplayHandler::init() { // Add SPI device spi_device_interface_config_t devcfg = {}; - devcfg.clock_speed_hz = 10 * 1000 * 1000; // 10 MHz (max for GDEY075T7) + devcfg.clock_speed_hz = 6 * 1000 * 1000; // 6 MHz (reduced for reliability) devcfg.mode = 0; // SPI mode 0 devcfg.spics_io_num = PIN_CS; devcfg.queue_size = 7; // Queue size for non-blocking transactions @@ -316,72 +316,84 @@ void EInkDisplayHandler::_perform_full_refresh(const uint8_t* framebuffer) { // Step 1: Write old data (0x10) - typically all zeros for full refresh epd_write_cmd(0x10); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout in full refresh step 1"); + return; + } gpio_set_level(PIN_DC, 1); // Data mode - // Use queued transactions to allow task switching - static uint8_t zero_byte = 0x00; // Static to persist across queue operations + ESP_LOGI(TAG, "Starting SPI data transmission for old data (0x10)..."); + + // Use simpler polling transmission instead of queued to avoid complexity + static uint8_t zero_byte = 0x00; // Static to persist + esp_err_t ret; for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) { spi_transaction_t t = {}; t.length = 8; t.tx_buffer = &zero_byte; - esp_err_t ret = spi_device_queue_trans(_spi, &t, portMAX_DELAY); + ret = spi_device_polling_transmit(_spi, &t); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to queue SPI transaction: %s", esp_err_to_name(ret)); + ESP_LOGE(TAG, "Failed to send SPI byte %zu: %s", i, esp_err_to_name(ret)); break; } - // Retrieve result every 100 bytes to prevent queue overflow and allow yielding - if (i % 100 == 99) { - for (int j = 0; j < 100; j++) { - spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); + // Yield every 1000 bytes to prevent watchdog timeout + if (i % 1000 == 999) { + xSemaphoreGive(_spi_mutex); + vTaskDelay(pdMS_TO_TICKS(1)); // Small delay + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout during yield at byte %zu", i); + return; } + gpio_set_level(PIN_DC, 1); // Re-set data mode after yield + ESP_LOGI(TAG, "Transmitted %zu/%zu bytes (%.1f%%)", i+1, DISPLAY_BUFFER_SIZE, + (float)(i+1) * 100.0f / DISPLAY_BUFFER_SIZE); } } - - // Get remaining results - for (size_t i = 0; i < (DISPLAY_BUFFER_SIZE % 100); i++) { - spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); - } - + ESP_LOGI(TAG, "Completed SPI data transmission for old data"); xSemaphoreGive(_spi_mutex); // Step 2: Write new data (0x13) epd_write_cmd(0x13); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout in full refresh step 2"); + return; + } gpio_set_level(PIN_DC, 1); // Data mode - // Use queued transactions with framebuffer data - static uint8_t tx_buffer[100]; // Buffer for batch of bytes + ESP_LOGI(TAG, "Starting SPI data transmission for new data (0x13)..."); + + // Use polling transmission for simplicity and reliability for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) { - size_t buf_idx = i % 100; - tx_buffer[buf_idx] = framebuffer[i]; // Write data directly (no inversion) - + uint8_t data_byte = framebuffer[i]; // Write data directly (no inversion) + spi_transaction_t t = {}; t.length = 8; - t.tx_buffer = &tx_buffer[buf_idx]; + t.tx_buffer = &data_byte; - esp_err_t ret = spi_device_queue_trans(_spi, &t, portMAX_DELAY); + esp_err_t ret = spi_device_polling_transmit(_spi, &t); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to queue SPI transaction: %s", esp_err_to_name(ret)); + ESP_LOGE(TAG, "Failed to send SPI byte %zu: %s", i, esp_err_to_name(ret)); break; } - // Retrieve result every 100 bytes to prevent queue overflow and allow yielding - if (buf_idx == 99) { - for (int j = 0; j < 100; j++) { - spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); + // Yield every 1000 bytes to prevent watchdog timeout + if (i % 1000 == 999) { + xSemaphoreGive(_spi_mutex); + vTaskDelay(pdMS_TO_TICKS(1)); // Small delay + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout during yield at byte %zu", i); + return; } + gpio_set_level(PIN_DC, 1); // Re-set data mode after yield + ESP_LOGI(TAG, "Transmitted %zu/%zu bytes (%.1f%%)", i+1, DISPLAY_BUFFER_SIZE, + (float)(i+1) * 100.0f / DISPLAY_BUFFER_SIZE); } } - - // Get remaining results - for (size_t i = 0; i < (DISPLAY_BUFFER_SIZE % 100); i++) { - spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); - } - + + ESP_LOGI(TAG, "Completed SPI data transmission for new data"); xSemaphoreGive(_spi_mutex); // Step 3: Trigger display refresh (DRF) @@ -421,9 +433,14 @@ void EInkDisplayHandler::_perform_partial_refresh(const uint8_t* framebuffer) { // Step 4: Write new data (0x13 command) epd_write_cmd(0x13); - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout in partial refresh"); + return; + } gpio_set_level(PIN_DC, 1); // Data mode + ESP_LOGI(TAG, "Starting SPI data transmission for partial refresh..."); + // Send data in chunks with task yields to prevent blocking static uint8_t tx_byte; const size_t CHUNK_SIZE = 1000; // Send 1000 bytes between yields @@ -437,11 +454,17 @@ void EInkDisplayHandler::_perform_partial_refresh(const uint8_t* framebuffer) { // Yield to other tasks every CHUNK_SIZE bytes if (i % CHUNK_SIZE == (CHUNK_SIZE - 1)) { xSemaphoreGive(_spi_mutex); - vTaskDelay(1); // Allow other tasks to run - xSemaphoreTake(_spi_mutex, portMAX_DELAY); + vTaskDelay(pdMS_TO_TICKS(1)); // Allow other tasks to run + if (xSemaphoreTake(_spi_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) { + ESP_LOGE(TAG, "SPI mutex timeout during partial refresh yield"); + return; + } gpio_set_level(PIN_DC, 1); // Re-set data mode after yield + ESP_LOGI(TAG, "Partial refresh progress: %zu/%zu bytes (%.1f%%)", i+1, DISPLAY_BUFFER_SIZE, + (float)(i+1) * 100.0f / DISPLAY_BUFFER_SIZE); } } + ESP_LOGI(TAG, "Completed SPI data transmission for partial refresh"); xSemaphoreGive(_spi_mutex); // Step 5: Trigger partial display refresh (DRF) @@ -459,16 +482,50 @@ void EInkDisplayHandler::_perform_partial_refresh(const uint8_t* framebuffer) { void EInkDisplayHandler::_wait_for_busy() { ESP_LOGI(TAG, "Waiting for display ready (BUSY pin)..."); + ESP_LOGI(TAG, "Initial BUSY pin level: %d", gpio_get_level(PIN_BUSY)); + int timeout = 0; while (gpio_get_level(PIN_BUSY) == BUSY_ACTIVE_LEVEL) { // 0=BUSY, 1=FREE vTaskDelay(pdMS_TO_TICKS(100)); timeout++; if (timeout > 100) { // 10 second timeout - ESP_LOGW(TAG, "Display BUSY timeout!"); + ESP_LOGE(TAG, "Display BUSY timeout! Pin level: %d", gpio_get_level(PIN_BUSY)); + ESP_LOGW(TAG, "Attempting hardware reset..."); + + // Hardware reset sequence + gpio_set_level(PIN_RST, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level(PIN_RST, 1); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Re-initialize display + ESP_LOGI(TAG, "Re-initializing display after reset..."); + _epd_init(); + + // Check if reset worked + int reset_timeout = 0; + while (gpio_get_level(PIN_BUSY) == BUSY_ACTIVE_LEVEL) { + vTaskDelay(pdMS_TO_TICKS(100)); + reset_timeout++; + if (reset_timeout > 50) { // 5 second timeout after reset + ESP_LOGE(TAG, "Display reset failed! Still busy after reset."); + break; + } + } + + if (gpio_get_level(PIN_BUSY) != BUSY_ACTIVE_LEVEL) { + ESP_LOGI(TAG, "Display reset successful after %d tenths of a second", reset_timeout); + } break; } + + // Log every 2 seconds to track progress + if (timeout % 20 == 0) { + ESP_LOGW(TAG, "Still waiting for BUSY pin, timeout: %d/100, level: %d", + timeout, gpio_get_level(PIN_BUSY)); + } } - ESP_LOGI(TAG, "Display ready"); + ESP_LOGI(TAG, "Display ready after %d tenths of a second", timeout); } void EInkDisplayHandler::_convert_buffer_to_epaper(const uint8_t* lvgl_buf, uint8_t* epd_buf, size_t size) { diff --git a/main/main.cpp b/main/main.cpp index 6c6858e..c790b20 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -87,7 +87,9 @@ void app_main(void) { // Initialize display and touch display_handler->init(); + ESP_LOGV(TAG, "Starting touch task...\n"); display_handler->start_touch_task(); + ESP_LOGV(TAG, "Touch task started.\n"); // // LVGL tick timer auto lvgl_tick_timer_callback = [](TimerHandle_t xTimer) { @@ -97,6 +99,7 @@ void app_main(void) { if (lvgl_tick_period == 0) { lvgl_tick_period = 1; // ensure at least 1 tick to avoid FreeRTOS assert } + ESP_LOGV(TAG, "Creating LVGL tick timer with period %u ticks...\n", (unsigned)lvgl_tick_period); TimerHandle_t lvgl_tick_timer = xTimerCreate( "lvgl_tick_timer", lvgl_tick_period, @@ -109,6 +112,7 @@ void app_main(void) { vTaskDelay(5000 / portTICK_PERIOD_MS); return esp_restart(); } + ESP_LOGV(TAG, "Starting LVGL tick timer...\n"); xTimerStart(lvgl_tick_timer, 0); //