Fix touch screen not responding, but screen still not refreshed.

This commit is contained in:
GW_MC
2026-01-25 15:51:49 +08:00
parent 57f698425b
commit 259660a0bc
3 changed files with 117 additions and 44 deletions

View File

@@ -3,6 +3,9 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_lcd_touch_gt911.h" #include "esp_lcd_touch_gt911.h"
#define BUSY_ACTIVE_LEVEL 0 // BUSY pin is active low
#define BUSY_INACTIVE_LEVEL 1
DisplayHandler::~DisplayHandler() { DisplayHandler::~DisplayHandler() {
if (_spi_mutex != nullptr) { if (_spi_mutex != nullptr) {
vSemaphoreDelete(_spi_mutex); vSemaphoreDelete(_spi_mutex);
@@ -32,7 +35,10 @@ void DisplayHandler::init_devices(bool set_display_ready /*= true*/) {
void DisplayHandler::epd_write_cmd(uint8_t cmd) { void DisplayHandler::epd_write_cmd(uint8_t cmd) {
ESP_LOGI("DisplayHandler", "epd_write_cmd: waiting to send 0x%02X", 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); _dangerous_epd_write_cmd_without_lock(cmd);
xSemaphoreGive(_spi_mutex); xSemaphoreGive(_spi_mutex);
ESP_LOGI("DisplayHandler", "epd_write_cmd: 0x%02X done", cmd); 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) { void DisplayHandler::epd_write_data(uint8_t data) {
ESP_LOGI("DisplayHandler", "epd_write_data: waiting to send 0x%02X", 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); _dangerous_epd_write_data_without_lock(data);
xSemaphoreGive(_spi_mutex); xSemaphoreGive(_spi_mutex);
ESP_LOGI("DisplayHandler", "epd_write_data: 0x%02X done", data); 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) { 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); 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); _dangerous_epd_write_cmd_without_lock(cmd);
for (size_t i = 0; i < data_len; ++i) { for (size_t i = 0; i < data_len; ++i) {
_dangerous_epd_write_data_without_lock(data[i]); _dangerous_epd_write_data_without_lock(data[i]);
@@ -108,7 +120,7 @@ void DisplayHandler::_epd_init(void) {
// Check BUSY pin // Check BUSY pin
ESP_LOGI("DisplayHandler", "Waiting for EPD to be ready..."); 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)); vTaskDelay(pdMS_TO_TICKS(10));
} }
ESP_LOGI("DisplayHandler", "EPD is ready."); ESP_LOGI("DisplayHandler", "EPD is ready.");

View File

@@ -75,7 +75,7 @@ void EInkDisplayHandler::init() {
// Add SPI device // Add SPI device
spi_device_interface_config_t devcfg = {}; 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.mode = 0; // SPI mode 0
devcfg.spics_io_num = PIN_CS; devcfg.spics_io_num = PIN_CS;
devcfg.queue_size = 7; // Queue size for non-blocking transactions 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 // Step 1: Write old data (0x10) - typically all zeros for full refresh
epd_write_cmd(0x10); 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 gpio_set_level(PIN_DC, 1); // Data mode
// Use queued transactions to allow task switching ESP_LOGI(TAG, "Starting SPI data transmission for old data (0x10)...");
static uint8_t zero_byte = 0x00; // Static to persist across queue operations
// 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++) { for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) {
spi_transaction_t t = {}; spi_transaction_t t = {};
t.length = 8; t.length = 8;
t.tx_buffer = &zero_byte; 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) { 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; break;
} }
// Retrieve result every 100 bytes to prevent queue overflow and allow yielding // Yield every 1000 bytes to prevent watchdog timeout
if (i % 100 == 99) { if (i % 1000 == 999) {
for (int j = 0; j < 100; j++) { xSemaphoreGive(_spi_mutex);
spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); 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);
} }
} }
} ESP_LOGI(TAG, "Completed SPI data transmission for old data");
// Get remaining results
for (size_t i = 0; i < (DISPLAY_BUFFER_SIZE % 100); i++) {
spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY);
}
xSemaphoreGive(_spi_mutex); xSemaphoreGive(_spi_mutex);
// Step 2: Write new data (0x13) // Step 2: Write new data (0x13)
epd_write_cmd(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 gpio_set_level(PIN_DC, 1); // Data mode
// Use queued transactions with framebuffer data ESP_LOGI(TAG, "Starting SPI data transmission for new data (0x13)...");
static uint8_t tx_buffer[100]; // Buffer for batch of bytes
// Use polling transmission for simplicity and reliability
for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) { for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) {
size_t buf_idx = i % 100; uint8_t data_byte = framebuffer[i]; // Write data directly (no inversion)
tx_buffer[buf_idx] = framebuffer[i]; // Write data directly (no inversion)
spi_transaction_t t = {}; spi_transaction_t t = {};
t.length = 8; 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) { 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; break;
} }
// Retrieve result every 100 bytes to prevent queue overflow and allow yielding // Yield every 1000 bytes to prevent watchdog timeout
if (buf_idx == 99) { if (i % 1000 == 999) {
for (int j = 0; j < 100; j++) { xSemaphoreGive(_spi_mutex);
spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY); 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 ESP_LOGI(TAG, "Completed SPI data transmission for new data");
for (size_t i = 0; i < (DISPLAY_BUFFER_SIZE % 100); i++) {
spi_device_get_trans_result(_spi, &rtrans, portMAX_DELAY);
}
xSemaphoreGive(_spi_mutex); xSemaphoreGive(_spi_mutex);
// Step 3: Trigger display refresh (DRF) // 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) // Step 4: Write new data (0x13 command)
epd_write_cmd(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 partial refresh");
return;
}
gpio_set_level(PIN_DC, 1); // Data mode 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 // Send data in chunks with task yields to prevent blocking
static uint8_t tx_byte; static uint8_t tx_byte;
const size_t CHUNK_SIZE = 1000; // Send 1000 bytes between yields 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 // Yield to other tasks every CHUNK_SIZE bytes
if (i % CHUNK_SIZE == (CHUNK_SIZE - 1)) { if (i % CHUNK_SIZE == (CHUNK_SIZE - 1)) {
xSemaphoreGive(_spi_mutex); xSemaphoreGive(_spi_mutex);
vTaskDelay(1); // Allow other tasks to run vTaskDelay(pdMS_TO_TICKS(1)); // Allow other tasks to run
xSemaphoreTake(_spi_mutex, portMAX_DELAY); 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 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); xSemaphoreGive(_spi_mutex);
// Step 5: Trigger partial display refresh (DRF) // 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() { void EInkDisplayHandler::_wait_for_busy() {
ESP_LOGI(TAG, "Waiting for display ready (BUSY pin)..."); 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; int timeout = 0;
while (gpio_get_level(PIN_BUSY) == BUSY_ACTIVE_LEVEL) { // 0=BUSY, 1=FREE while (gpio_get_level(PIN_BUSY) == BUSY_ACTIVE_LEVEL) { // 0=BUSY, 1=FREE
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
timeout++; timeout++;
if (timeout > 100) { // 10 second 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; break;
} }
} }
ESP_LOGI(TAG, "Display ready");
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 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) { void EInkDisplayHandler::_convert_buffer_to_epaper(const uint8_t* lvgl_buf, uint8_t* epd_buf, size_t size) {

View File

@@ -87,7 +87,9 @@ void app_main(void) {
// Initialize display and touch // Initialize display and touch
display_handler->init(); display_handler->init();
ESP_LOGV(TAG, "Starting touch task...\n");
display_handler->start_touch_task(); display_handler->start_touch_task();
ESP_LOGV(TAG, "Touch task started.\n");
// //
// LVGL tick timer // LVGL tick timer
auto lvgl_tick_timer_callback = [](TimerHandle_t xTimer) { auto lvgl_tick_timer_callback = [](TimerHandle_t xTimer) {
@@ -97,6 +99,7 @@ void app_main(void) {
if (lvgl_tick_period == 0) { if (lvgl_tick_period == 0) {
lvgl_tick_period = 1; // ensure at least 1 tick to avoid FreeRTOS assert 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( TimerHandle_t lvgl_tick_timer = xTimerCreate(
"lvgl_tick_timer", "lvgl_tick_timer",
lvgl_tick_period, lvgl_tick_period,
@@ -109,6 +112,7 @@ void app_main(void) {
vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS);
return esp_restart(); return esp_restart();
} }
ESP_LOGV(TAG, "Starting LVGL tick timer...\n");
xTimerStart(lvgl_tick_timer, 0); xTimerStart(lvgl_tick_timer, 0);
// //