Fix touch screen not responding, but screen still not refreshed.
This commit is contained in:
@@ -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.");
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user