修复了SW2 双击后抖动,
icon 文件改为每帧只加载一次,整帧绘制期间复用,不再每 8 行重复读取。
This commit is contained in:
@@ -44,6 +44,10 @@
|
|||||||
#define CONFIG_TQ_KEY_DOUBLE_CLICK_MS 300
|
#define CONFIG_TQ_KEY_DOUBLE_CLICK_MS 300
|
||||||
#endif
|
#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
|
#ifndef CONFIG_TQ_KEY_INPUT_POLL_MS
|
||||||
#define CONFIG_TQ_KEY_INPUT_POLL_MS 20
|
#define CONFIG_TQ_KEY_INPUT_POLL_MS 20
|
||||||
#endif
|
#endif
|
||||||
@@ -105,6 +109,7 @@ typedef struct {
|
|||||||
int64_t inactive_since_ms;
|
int64_t inactive_since_ms;
|
||||||
int64_t press_start_ms;
|
int64_t press_start_ms;
|
||||||
int64_t click_deadline_ms;
|
int64_t click_deadline_ms;
|
||||||
|
int64_t suppress_until_ms;
|
||||||
} control_plane_key_state_t;
|
} control_plane_key_state_t;
|
||||||
|
|
||||||
static const control_plane_key_config_t s_key_configs[] = {
|
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) {
|
if (config->enable_double_click && state->click_pending && now_ms <= state->click_deadline_ms) {
|
||||||
state->click_pending = false;
|
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_emit_key_event(config,
|
||||||
CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK,
|
CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK,
|
||||||
level,
|
level,
|
||||||
@@ -344,6 +352,11 @@ static void control_plane_process_key_timers(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->suppress_until_ms > 0 && now_ms < state->suppress_until_ms) {
|
||||||
|
state->debounce_pending = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (state->wait_inactive_before_press) {
|
if (state->wait_inactive_before_press) {
|
||||||
const int level = gpio_get_level(config->gpio_num);
|
const int level = gpio_get_level(config->gpio_num);
|
||||||
const bool pressed = control_plane_key_level_is_pressed(config, level);
|
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];
|
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->raw_level = irq_event.level;
|
||||||
state->debounce_pending = true;
|
state->debounce_pending = true;
|
||||||
state->debounce_deadline_ms = irq_event.timestamp_ms + config->debounce_ms;
|
state->debounce_deadline_ms = irq_event.timestamp_ms + config->debounce_ms;
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ typedef struct {
|
|||||||
size_t step_count;
|
size_t step_count;
|
||||||
} lcd_direct_request_t;
|
} 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 {
|
typedef struct {
|
||||||
uint8_t cmd;
|
uint8_t cmd;
|
||||||
uint8_t data[16];
|
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;
|
return runs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lcd_direct_overlay_icons(uint16_t *pixel_buffer,
|
static size_t lcd_direct_prepare_icons(const lcd_direct_icon_draw_t *icons,
|
||||||
uint16_t row_start,
|
size_t icon_count,
|
||||||
uint16_t row_count,
|
lcd_direct_prepared_icon_t *prepared,
|
||||||
const lcd_direct_icon_draw_t *icons,
|
size_t prepared_capacity)
|
||||||
size_t icon_count)
|
|
||||||
{
|
{
|
||||||
if (pixel_buffer == NULL || icons == NULL || icon_count == 0U) {
|
if (icons == NULL || icon_count == 0U || prepared == NULL || prepared_capacity == 0U) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint16_t row_end = row_start + row_count;
|
size_t prepared_count = 0;
|
||||||
for (size_t icon_index = 0; icon_index < icon_count; ++icon_index) {
|
for (size_t i = 0; i < icon_count && prepared_count < prepared_capacity; ++i) {
|
||||||
const lcd_direct_icon_draw_t *draw = &icons[icon_index];
|
const lcd_direct_icon_draw_t *draw = &icons[i];
|
||||||
const lcd_direct_icon_t *icon = lcd_direct_icon(draw->icon_id);
|
const lcd_direct_icon_t *icon = lcd_direct_icon(draw->icon_id);
|
||||||
if (icon == NULL || icon->width == 0U || icon->height == 0U) {
|
if (icon == NULL || icon->width == 0U || icon->height == 0U) {
|
||||||
continue;
|
continue;
|
||||||
@@ -337,11 +342,58 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
lcd_direct_rle_run_t *loaded_runs = NULL;
|
lcd_direct_rle_run_t *loaded_runs = NULL;
|
||||||
const lcd_direct_rle_run_t *runs = icon->runs;
|
if (icon->runs == NULL) {
|
||||||
if (runs == NULL) {
|
|
||||||
loaded_runs = lcd_direct_load_icon_runs(icon);
|
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) {
|
if (runs == NULL) {
|
||||||
continue;
|
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_y0 = draw->y;
|
||||||
const uint16_t overlay_y1 = draw->y + draw_height;
|
const uint16_t overlay_y1 = draw->y + draw_height;
|
||||||
if (overlay_y1 <= row_start || overlay_y0 >= row_end) {
|
if (overlay_y1 <= row_start || overlay_y0 >= row_end) {
|
||||||
free(loaded_runs);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,13 +438,11 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer,
|
|||||||
icon_pixel_pos = run_end;
|
icon_pixel_pos = run_end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(loaded_runs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_draw_rle_memory_image(const lcd_direct_image_t *image,
|
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)
|
size_t icon_count)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(image != NULL && image->runs != NULL,
|
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,
|
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)
|
size_t icon_count)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(image != NULL && image->path != NULL,
|
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,
|
ESP_ERR_INVALID_ARG,
|
||||||
TAG,
|
TAG,
|
||||||
"invalid image");
|
"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) {
|
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)
|
static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t *request)
|
||||||
|
|||||||
@@ -270,6 +270,14 @@ menu "Example Configuration"
|
|||||||
help
|
help
|
||||||
Maximum gap between short clicks before emitting a double-click event.
|
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
|
endmenu
|
||||||
|
|
||||||
menu "TQ Screen"
|
menu "TQ Screen"
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ CONFIG_TQ_KEY_INPUT_POLL_MS=20
|
|||||||
CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS=20
|
CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS=20
|
||||||
CONFIG_TQ_KEY_LONG_PRESS_MS=800
|
CONFIG_TQ_KEY_LONG_PRESS_MS=800
|
||||||
CONFIG_TQ_KEY_DOUBLE_CLICK_MS=450
|
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
|
# 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.
|
# bounce buffers, LCD DMA, and worker task stacks all need internal-only memory.
|
||||||
|
|||||||
Reference in New Issue
Block a user