Merge branch 'setup' into feature/mtr-app

This commit is contained in:
GW_MC
2026-01-24 18:25:11 +08:00
3 changed files with 104 additions and 53 deletions

View File

@@ -22,7 +22,12 @@ EInkDisplayHandler::~EInkDisplayHandler() {
vTaskDelete(_touch_task_handle);
}
if (_lvgl_display != nullptr) {
lvgl_port_remove_disp(_lvgl_display);
lv_display_delete(_lvgl_display);
_lvgl_display = nullptr;
if (_lvgl_draw_buf != nullptr) {
lv_draw_buf_destroy(_lvgl_draw_buf);
_lvgl_draw_buf = nullptr;
}
}
if (_lvgl_touch_indev != nullptr) {
lvgl_port_remove_touch(_lvgl_touch_indev);
@@ -73,7 +78,7 @@ void EInkDisplayHandler::init() {
devcfg.clock_speed_hz = 10 * 1000 * 1000; // 10 MHz (max for GDEY075T7)
devcfg.mode = 0; // SPI mode 0
devcfg.spics_io_num = PIN_CS;
devcfg.queue_size = 1;
devcfg.queue_size = 7; // Queue size for non-blocking transactions
devcfg.pre_cb = nullptr;
ret = spi_bus_add_device(SPI2_HOST, &devcfg, &_spi);
@@ -102,43 +107,43 @@ void EInkDisplayHandler::init() {
}
memset(_framebuffer, 0xFF, DISPLAY_BUFFER_SIZE); // Initialize to white
// Create LVGL display driver
lvgl_port_display_cfg_t disp_cfg = {};
disp_cfg.io_handle = nullptr;
disp_cfg.panel_handle = nullptr;
disp_cfg.buffer_size = DISPLAY_WIDTH * 40; // 40 lines buffer
disp_cfg.double_buffer = false;
disp_cfg.hres = DISPLAY_WIDTH;
disp_cfg.vres = DISPLAY_HEIGHT;
disp_cfg.monochrome = true;
disp_cfg.rotation.swap_xy = false;
disp_cfg.rotation.mirror_x = false;
disp_cfg.rotation.mirror_y = false;
disp_cfg.flags.buff_dma = _framebuffer_in_psram ? false : true;
disp_cfg.flags.buff_spiram = _framebuffer_in_psram;
disp_cfg.flags.swap_bytes = false;
disp_cfg.flags.full_refresh = false;
disp_cfg.flags.direct_mode = false;
_lvgl_display = lvgl_port_add_disp(&disp_cfg);
if (_lvgl_display == nullptr) {
// Create LVGL display manually (no esp_lcd panel for e-paper)
lv_display_t* disp = lv_display_create(DISPLAY_WIDTH, DISPLAY_HEIGHT);
if (disp == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL display");
return;
}
// Set custom flush callback
lv_display_set_flush_cb(_lvgl_display, _lvgl_flush_cb);
lv_display_set_user_data(_lvgl_display, this);
/* 1-bit e-paper display */
lv_display_set_color_format(disp, LV_COLOR_FORMAT_I1);
/* Create a draw buffer covering ~40 lines */
_lvgl_draw_buf = lv_draw_buf_create(DISPLAY_WIDTH, DISPLAY_HEIGHT, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
if (_lvgl_draw_buf == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL draw buffer");
lv_display_delete(disp);
return;
}
lv_display_set_draw_buffers(disp, _lvgl_draw_buf, NULL);
lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_DIRECT);
// Set custom flush callback and user data
lv_display_set_flush_cb(disp, _lvgl_flush_cb);
lv_display_set_user_data(disp, this);
_lvgl_display = disp;
ESP_LOGI(TAG, "LVGL display registered");
// Register GT911 touch input with LVGL
// Register GT911 touch input with LVGL, only if touch handle is valid
esp_lcd_touch_handle_t tp_handle = get_touch_handle();
if (tp_handle == nullptr) {
ESP_LOGE(TAG, "Touch handle is NULL — touch initialization failed; skipping LVGL touch registration");
} else {
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = _lvgl_display,
.handle = get_touch_handle(),
.handle = tp_handle,
.scale = {}, // Default scaling
};
@@ -153,6 +158,7 @@ void EInkDisplayHandler::init() {
lv_indev_set_user_data(_lvgl_touch_indev, this);
ESP_LOGI(TAG, "LVGL touch input registered");
}
// Perform initial full refresh to clear display
ESP_LOGI(TAG, "Performing initial display clear...");
@@ -302,19 +308,40 @@ void EInkDisplayHandler::_perform_full_refresh(const uint8_t* framebuffer) {
_wait_for_busy();
spi_transaction_t* rtrans; // Declare once for entire function
// Step 1: Write old data (0x10) - typically all zeros for full refresh
epd_write_cmd(0x10);
xSemaphoreTake(_spi_mutex, portMAX_DELAY);
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
for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) {
spi_transaction_t t = {};
t.length = 8;
uint8_t byte = 0x00; // Old data (cleared screen)
t.tx_buffer = &byte;
spi_device_polling_transmit(_spi, &t);
t.tx_buffer = &zero_byte;
esp_err_t ret = spi_device_queue_trans(_spi, &t, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to queue SPI transaction: %s", 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);
}
}
}
// 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);
// Step 2: Write new data (0x13) with data inversion
@@ -323,13 +350,35 @@ void EInkDisplayHandler::_perform_full_refresh(const uint8_t* framebuffer) {
xSemaphoreTake(_spi_mutex, portMAX_DELAY);
gpio_set_level(PIN_DC, 1); // Data mode
// Use queued transactions with inverted framebuffer data
static uint8_t tx_buffer[100]; // Buffer for batch of inverted bytes
for (size_t i = 0; i < DISPLAY_BUFFER_SIZE; i++) {
size_t buf_idx = i % 100;
tx_buffer[buf_idx] = ~framebuffer[i]; // Invert data per manufacturer spec
spi_transaction_t t = {};
t.length = 8;
uint8_t byte = ~framebuffer[i]; // Invert data per manufacturer spec
t.tx_buffer = &byte;
spi_device_polling_transmit(_spi, &t);
t.tx_buffer = &tx_buffer[buf_idx];
esp_err_t ret = spi_device_queue_trans(_spi, &t, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to queue SPI transaction: %s", 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);
}
}
}
// 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);
// Step 3: Trigger display refresh (DRF)

View File

@@ -28,6 +28,7 @@ private:
// LVGL display and input device handles
lv_display_t* _lvgl_display = nullptr;
lv_indev_t* _lvgl_touch_indev = nullptr;
lv_draw_buf_t* _lvgl_draw_buf = nullptr;
// Framebuffer
uint8_t* _framebuffer = nullptr;

View File

@@ -47,7 +47,7 @@ void NVSStorageHandler::put(const std::string& key, const std::string& value) {
ESP_LOGE(TAG, "Error (%s) setting key-value pair in NVS!", esp_err_to_name(err));
} else {
nvs_commit(this->nvsHandle);
ESP_LOGI(TAG, "Key-value pair (%s, %s) stored in NVS.", key.c_str(), value.c_str());
// ESP_LOGI(TAG, "Key-value pair (%s, %s) stored in NVS.", key.c_str(), value.c_str());
}
}
@@ -67,8 +67,9 @@ std::string NVSStorageHandler::get(const std::string& key) const {
return "";
}
std::string value;
err = nvs_get_str(this->nvsHandle, key.c_str(), value.data(), &required_size);
// Allocate string buffer with correct size (includes null terminator)
std::string value(required_size - 1, '\0');
err = nvs_get_str(this->nvsHandle, key.c_str(), &value[0], &required_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) getting value for key %s from NVS!", esp_err_to_name(err), key.c_str());
return "";