From 21e5d43766c0e73e136e9b623c0a5461b1728b86 Mon Sep 17 00:00:00 2001 From: "CSBOOK\\44744" <447449914@qq.com> Date: Sun, 28 Jun 2026 19:16:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86SW2=20=E5=8F=8C?= =?UTF-8?q?=E5=87=BB=E5=90=8E=E6=8A=96=E5=8A=A8=EF=BC=8C=20icon=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=94=B9=E4=B8=BA=E6=AF=8F=E5=B8=A7=E5=8F=AA?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E4=B8=80=E6=AC=A1=EF=BC=8C=E6=95=B4=E5=B8=A7?= =?UTF-8?q?=E7=BB=98=E5=88=B6=E6=9C=9F=E9=97=B4=E5=A4=8D=E7=94=A8=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E6=AF=8F=208=20=E8=A1=8C=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/runtime/control_plane_key_input.c | 28 ++++++ components/platform/src/lcd_direct.c | 98 +++++++++++++++---- main/Kconfig.projbuild | 8 ++ sdkconfig.defaults | 1 + 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/components/control_plane/src/runtime/control_plane_key_input.c b/components/control_plane/src/runtime/control_plane_key_input.c index 887c0f1..09e3f83 100644 --- a/components/control_plane/src/runtime/control_plane_key_input.c +++ b/components/control_plane/src/runtime/control_plane_key_input.c @@ -44,6 +44,10 @@ #define CONFIG_TQ_KEY_DOUBLE_CLICK_MS 300 #endif +#ifndef CONFIG_TQ_KEY_POST_DOUBLE_CLICK_SUPPRESS_MS +#define CONFIG_TQ_KEY_POST_DOUBLE_CLICK_SUPPRESS_MS 450 +#endif + #ifndef CONFIG_TQ_KEY_INPUT_POLL_MS #define CONFIG_TQ_KEY_INPUT_POLL_MS 20 #endif @@ -105,6 +109,7 @@ typedef struct { int64_t inactive_since_ms; int64_t press_start_ms; int64_t click_deadline_ms; + int64_t suppress_until_ms; } control_plane_key_state_t; static const control_plane_key_config_t s_key_configs[] = { @@ -312,6 +317,9 @@ static void control_plane_handle_debounced_transition(const control_plane_key_co if (config->enable_double_click && state->click_pending && now_ms <= state->click_deadline_ms) { state->click_pending = false; + state->debounce_pending = false; + state->press_start_ms = 0; + state->suppress_until_ms = now_ms + CONFIG_TQ_KEY_POST_DOUBLE_CLICK_SUPPRESS_MS; control_plane_emit_key_event(config, CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK, level, @@ -344,6 +352,11 @@ static void control_plane_process_key_timers(void) continue; } + if (state->suppress_until_ms > 0 && now_ms < state->suppress_until_ms) { + state->debounce_pending = false; + continue; + } + if (state->wait_inactive_before_press) { const int level = gpio_get_level(config->gpio_num); const bool pressed = control_plane_key_level_is_pressed(config, level); @@ -469,6 +482,21 @@ static void control_plane_key_input_task(void *arg) } control_plane_key_state_t *state = &s_key_states[index]; + if (state->suppress_until_ms > 0 && irq_event.timestamp_ms < state->suppress_until_ms) { + const bool pressed = control_plane_key_level_is_pressed(config, irq_event.level); + state->raw_level = irq_event.level; + state->debounce_pending = false; + if (!pressed) { + state->debounced_pressed = false; + } + ESP_LOGD(TAG, + "suppressed gpio change key=%s gpio=%d level=%d", + control_plane_key_id_name(config->key_id), + (int)irq_event.gpio_num, + irq_event.level); + continue; + } + state->raw_level = irq_event.level; state->debounce_pending = true; state->debounce_deadline_ms = irq_event.timestamp_ms + config->debounce_ms; diff --git a/components/platform/src/lcd_direct.c b/components/platform/src/lcd_direct.c index 6ef9622..e498d78 100644 --- a/components/platform/src/lcd_direct.c +++ b/components/platform/src/lcd_direct.c @@ -55,6 +55,12 @@ typedef struct { size_t step_count; } lcd_direct_request_t; +typedef struct { + lcd_direct_icon_draw_t draw; + const lcd_direct_icon_t *icon; + lcd_direct_rle_run_t *loaded_runs; +} lcd_direct_prepared_icon_t; + typedef struct { uint8_t cmd; uint8_t data[16]; @@ -315,19 +321,18 @@ static lcd_direct_rle_run_t *lcd_direct_load_icon_runs(const lcd_direct_icon_t * return runs; } -static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, - uint16_t row_start, - uint16_t row_count, - const lcd_direct_icon_draw_t *icons, - size_t icon_count) +static size_t lcd_direct_prepare_icons(const lcd_direct_icon_draw_t *icons, + size_t icon_count, + lcd_direct_prepared_icon_t *prepared, + size_t prepared_capacity) { - if (pixel_buffer == NULL || icons == NULL || icon_count == 0U) { - return; + if (icons == NULL || icon_count == 0U || prepared == NULL || prepared_capacity == 0U) { + return 0; } - const uint16_t row_end = row_start + row_count; - for (size_t icon_index = 0; icon_index < icon_count; ++icon_index) { - const lcd_direct_icon_draw_t *draw = &icons[icon_index]; + size_t prepared_count = 0; + for (size_t i = 0; i < icon_count && prepared_count < prepared_capacity; ++i) { + const lcd_direct_icon_draw_t *draw = &icons[i]; const lcd_direct_icon_t *icon = lcd_direct_icon(draw->icon_id); if (icon == NULL || icon->width == 0U || icon->height == 0U) { continue; @@ -337,11 +342,58 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, } lcd_direct_rle_run_t *loaded_runs = NULL; - const lcd_direct_rle_run_t *runs = icon->runs; - if (runs == NULL) { + if (icon->runs == NULL) { loaded_runs = lcd_direct_load_icon_runs(icon); - runs = loaded_runs; + if (loaded_runs == NULL) { + continue; + } } + + prepared[prepared_count] = (lcd_direct_prepared_icon_t) { + .draw = *draw, + .icon = icon, + .loaded_runs = loaded_runs, + }; + prepared_count++; + } + + return prepared_count; +} + +static void lcd_direct_release_prepared_icons(lcd_direct_prepared_icon_t *prepared, size_t prepared_count) +{ + if (prepared == NULL) { + return; + } + + for (size_t i = 0; i < prepared_count; ++i) { + free(prepared[i].loaded_runs); + prepared[i].loaded_runs = NULL; + } +} + +static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, + uint16_t row_start, + uint16_t row_count, + const lcd_direct_prepared_icon_t *icons, + size_t icon_count) +{ + if (pixel_buffer == NULL || icons == NULL || icon_count == 0U) { + return; + } + + const uint16_t row_end = row_start + row_count; + for (size_t icon_index = 0; icon_index < icon_count; ++icon_index) { + const lcd_direct_icon_draw_t *draw = &icons[icon_index].draw; + const lcd_direct_icon_t *icon = icons[icon_index].icon; + if (icon == NULL || icon->width == 0U || icon->height == 0U) { + continue; + } + if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) { + continue; + } + + const lcd_direct_rle_run_t *runs = icon->runs != NULL ? icon->runs : icons[icon_index].loaded_runs; if (runs == NULL) { continue; } @@ -358,7 +410,6 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, const uint16_t overlay_y0 = draw->y; const uint16_t overlay_y1 = draw->y + draw_height; if (overlay_y1 <= row_start || overlay_y0 >= row_end) { - free(loaded_runs); continue; } @@ -387,13 +438,11 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, icon_pixel_pos = run_end; } } - - free(loaded_runs); } } static esp_err_t lcd_direct_draw_rle_memory_image(const lcd_direct_image_t *image, - const lcd_direct_icon_draw_t *icons, + const lcd_direct_prepared_icon_t *icons, size_t icon_count) { ESP_RETURN_ON_FALSE(image != NULL && image->runs != NULL, @@ -448,7 +497,7 @@ static esp_err_t lcd_direct_draw_rle_memory_image(const lcd_direct_image_t *imag } static esp_err_t lcd_direct_draw_rle_file_image(const lcd_direct_image_t *image, - const lcd_direct_icon_draw_t *icons, + const lcd_direct_prepared_icon_t *icons, size_t icon_count) { ESP_RETURN_ON_FALSE(image != NULL && image->path != NULL, @@ -547,10 +596,19 @@ static esp_err_t lcd_direct_draw_rle_image(const lcd_direct_image_t *image, ESP_ERR_INVALID_ARG, TAG, "invalid image"); + lcd_direct_prepared_icon_t prepared_icons[LCD_DIRECT_DEFAULT_ICON_COUNT]; + size_t prepared_icon_count = lcd_direct_prepare_icons(icons, + icon_count, + prepared_icons, + sizeof(prepared_icons) / sizeof(prepared_icons[0])); + esp_err_t err = ESP_OK; if (image->runs != NULL) { - return lcd_direct_draw_rle_memory_image(image, icons, icon_count); + err = lcd_direct_draw_rle_memory_image(image, prepared_icons, prepared_icon_count); + } else { + err = lcd_direct_draw_rle_file_image(image, prepared_icons, prepared_icon_count); } - return lcd_direct_draw_rle_file_image(image, icons, icon_count); + lcd_direct_release_prepared_icons(prepared_icons, prepared_icon_count); + return err; } static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t *request) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index b046a20..fe20833 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -270,6 +270,14 @@ menu "Example Configuration" help Maximum gap between short clicks before emitting a double-click event. + config TQ_KEY_POST_DOUBLE_CLICK_SUPPRESS_MS + int "Key post-double-click suppress interval (ms)" + default 450 + range 0 1000 + depends on TQ_KEY_INPUT_ENABLE + help + Ignore raw GPIO chatter briefly after a double-click has been emitted. + endmenu menu "TQ Screen" diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 3216907..2eab505 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -55,6 +55,7 @@ CONFIG_TQ_KEY_INPUT_POLL_MS=20 CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS=20 CONFIG_TQ_KEY_LONG_PRESS_MS=800 CONFIG_TQ_KEY_DOUBLE_CLICK_MS=450 +CONFIG_TQ_KEY_POST_DOUBLE_CLICK_SUPPRESS_MS=450 # Internal DMA-capable SRAM budget. TLS/auth/fortune flows, hardware AES DMA # bounce buffers, LCD DMA, and worker task stacks all need internal-only memory.