按键系统待测
This commit is contained in:
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "control_plane_internal.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "control_plane_events.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_INPUT_ENABLE
|
||||
#define CONFIG_TQ_KEY_INPUT_ENABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_INPUT_TASK_STACK
|
||||
#define CONFIG_TQ_KEY_INPUT_TASK_STACK 4096
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_IRQ_QUEUE_LEN
|
||||
#define CONFIG_TQ_KEY_IRQ_QUEUE_LEN 16
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS
|
||||
#define CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS 20
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_LONG_PRESS_MS
|
||||
#define CONFIG_TQ_KEY_LONG_PRESS_MS 800
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_DOUBLE_CLICK_MS
|
||||
#define CONFIG_TQ_KEY_DOUBLE_CLICK_MS 300
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_KEY_INPUT_POLL_MS
|
||||
#define CONFIG_TQ_KEY_INPUT_POLL_MS 20
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_MULTIMODAL_PTT_BUTTON_PULLDOWN
|
||||
#define CONFIG_MULTIMODAL_PTT_BUTTON_PULLDOWN 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN
|
||||
#define CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SLOT_SWITCH_PULLDOWN
|
||||
#define CONFIG_TQ_SLOT_SWITCH_PULLDOWN 0
|
||||
#endif
|
||||
|
||||
static const char *TAG = "cp_key_input";
|
||||
|
||||
typedef struct {
|
||||
gpio_num_t gpio_num;
|
||||
int level;
|
||||
int64_t timestamp_ms;
|
||||
} control_plane_gpio_irq_event_t;
|
||||
|
||||
typedef struct {
|
||||
control_plane_key_id_t key_id;
|
||||
gpio_num_t gpio_num;
|
||||
int active_level;
|
||||
bool pullup_enabled;
|
||||
bool pulldown_enabled;
|
||||
uint32_t debounce_ms;
|
||||
uint32_t long_press_ms;
|
||||
uint32_t double_click_ms;
|
||||
bool emit_press_release;
|
||||
bool enable_short_press;
|
||||
bool enable_long_press;
|
||||
bool enable_double_click;
|
||||
} control_plane_key_config_t;
|
||||
|
||||
typedef struct {
|
||||
bool initialized;
|
||||
int raw_level;
|
||||
bool debounced_pressed;
|
||||
bool debounce_pending;
|
||||
bool long_press_sent;
|
||||
bool click_pending;
|
||||
int64_t debounce_deadline_ms;
|
||||
int64_t press_start_ms;
|
||||
int64_t click_deadline_ms;
|
||||
} control_plane_key_state_t;
|
||||
|
||||
static const control_plane_key_config_t s_key_configs[] = {
|
||||
{
|
||||
.key_id = CONTROL_PLANE_KEY_ID_PTT,
|
||||
.gpio_num = CONFIG_MULTIMODAL_PTT_BUTTON_GPIO,
|
||||
.active_level = CONFIG_MULTIMODAL_PTT_BUTTON_ACTIVE_LEVEL,
|
||||
.pullup_enabled = CONFIG_MULTIMODAL_PTT_BUTTON_PULLUP,
|
||||
.pulldown_enabled = CONFIG_MULTIMODAL_PTT_BUTTON_PULLDOWN,
|
||||
.debounce_ms = CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS,
|
||||
.long_press_ms = CONFIG_TQ_KEY_LONG_PRESS_MS,
|
||||
.double_click_ms = CONFIG_TQ_KEY_DOUBLE_CLICK_MS,
|
||||
.emit_press_release = true,
|
||||
.enable_short_press = false,
|
||||
.enable_long_press = false,
|
||||
.enable_double_click = false,
|
||||
},
|
||||
{
|
||||
.key_id = CONTROL_PLANE_KEY_ID_SW1,
|
||||
.gpio_num = CONFIG_TQ_CALIBRATION_SWITCH_GPIO,
|
||||
.active_level = CONFIG_TQ_CALIBRATION_SWITCH_ACTIVE_LEVEL,
|
||||
.pullup_enabled = CONFIG_TQ_CALIBRATION_SWITCH_PULLUP,
|
||||
.pulldown_enabled = CONFIG_TQ_CALIBRATION_SWITCH_PULLDOWN,
|
||||
.debounce_ms = CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS,
|
||||
.long_press_ms = CONFIG_TQ_KEY_LONG_PRESS_MS,
|
||||
.double_click_ms = CONFIG_TQ_KEY_DOUBLE_CLICK_MS,
|
||||
.emit_press_release = true,
|
||||
.enable_short_press = true,
|
||||
.enable_long_press = true,
|
||||
.enable_double_click = true,
|
||||
},
|
||||
{
|
||||
.key_id = CONTROL_PLANE_KEY_ID_SW2,
|
||||
.gpio_num = CONFIG_TQ_SLOT_SWITCH_GPIO,
|
||||
.active_level = CONFIG_TQ_SLOT_SWITCH_ACTIVE_LEVEL,
|
||||
.pullup_enabled = CONFIG_TQ_SLOT_SWITCH_PULLUP,
|
||||
.pulldown_enabled = CONFIG_TQ_SLOT_SWITCH_PULLDOWN,
|
||||
.debounce_ms = CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS,
|
||||
.long_press_ms = CONFIG_TQ_KEY_LONG_PRESS_MS,
|
||||
.double_click_ms = CONFIG_TQ_KEY_DOUBLE_CLICK_MS,
|
||||
.emit_press_release = true,
|
||||
.enable_short_press = true,
|
||||
.enable_long_press = true,
|
||||
.enable_double_click = true,
|
||||
},
|
||||
};
|
||||
|
||||
static QueueHandle_t s_gpio_irq_queue;
|
||||
static TaskHandle_t s_key_input_task;
|
||||
static volatile bool s_stop_requested;
|
||||
static control_plane_key_state_t s_key_states[sizeof(s_key_configs) / sizeof(s_key_configs[0])];
|
||||
|
||||
static const control_plane_key_config_t *control_plane_find_key_config_by_gpio(gpio_num_t gpio_num,
|
||||
size_t *out_index)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) {
|
||||
if (s_key_configs[i].gpio_num == gpio_num) {
|
||||
if (out_index != NULL) {
|
||||
*out_index = i;
|
||||
}
|
||||
return &s_key_configs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool control_plane_key_level_is_pressed(const control_plane_key_config_t *config, int level)
|
||||
{
|
||||
return config != NULL && level == config->active_level;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR control_plane_key_gpio_isr_handler(void *arg)
|
||||
{
|
||||
const gpio_num_t gpio_num = (gpio_num_t)(intptr_t)arg;
|
||||
QueueHandle_t queue = s_gpio_irq_queue;
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
control_plane_gpio_irq_event_t event = {
|
||||
.gpio_num = gpio_num,
|
||||
.level = gpio_get_level(gpio_num),
|
||||
.timestamp_ms = (int64_t)pdTICKS_TO_MS(xTaskGetTickCountFromISR()),
|
||||
};
|
||||
BaseType_t higher_priority_task_woken = pdFALSE;
|
||||
(void)xQueueSendFromISR(queue, &event, &higher_priority_task_woken);
|
||||
if (higher_priority_task_woken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t control_plane_configure_key_gpio(const control_plane_key_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
gpio_config_t io_config = {
|
||||
.pin_bit_mask = 1ULL << config->gpio_num,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = config->pullup_enabled ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = config->pulldown_enabled ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_ANYEDGE,
|
||||
};
|
||||
|
||||
esp_err_t err = gpio_config(&io_config);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = gpio_isr_handler_add(config->gpio_num,
|
||||
control_plane_key_gpio_isr_handler,
|
||||
(void *)(intptr_t)config->gpio_num);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
(void)gpio_isr_handler_remove(config->gpio_num);
|
||||
err = gpio_isr_handler_add(config->gpio_num,
|
||||
control_plane_key_gpio_isr_handler,
|
||||
(void *)(intptr_t)config->gpio_num);
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return gpio_intr_enable(config->gpio_num);
|
||||
}
|
||||
|
||||
static void control_plane_emit_key_event(const control_plane_key_config_t *config,
|
||||
control_plane_key_event_type_t type,
|
||||
int level,
|
||||
uint32_t duration_ms,
|
||||
uint32_t click_count)
|
||||
{
|
||||
control_plane_system_event_t event = {
|
||||
.family = CONTROL_PLANE_EVENT_FAMILY_KEY,
|
||||
.timestamp_ms = control_plane_event_timestamp_ms(),
|
||||
.data.key = {
|
||||
.key_id = config->key_id,
|
||||
.type = type,
|
||||
.gpio_num = config->gpio_num,
|
||||
.level = level,
|
||||
.timestamp_ms = control_plane_event_timestamp_ms(),
|
||||
.press_duration_ms = duration_ms,
|
||||
.click_count = click_count,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"emit key=%s type=%s gpio=%d level=%d duration=%" PRIu32 "ms clicks=%" PRIu32,
|
||||
control_plane_key_id_name(config->key_id),
|
||||
control_plane_key_event_type_name(type),
|
||||
(int)config->gpio_num,
|
||||
level,
|
||||
duration_ms,
|
||||
click_count);
|
||||
|
||||
esp_err_t err = control_plane_post_system_event(&event, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "failed to post key event: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_handle_debounced_transition(const control_plane_key_config_t *config,
|
||||
control_plane_key_state_t *state,
|
||||
bool pressed,
|
||||
int level,
|
||||
int64_t now_ms)
|
||||
{
|
||||
if (pressed == state->debounced_pressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->debounced_pressed = pressed;
|
||||
if (pressed) {
|
||||
state->press_start_ms = now_ms;
|
||||
state->long_press_sent = false;
|
||||
if (config->emit_press_release) {
|
||||
control_plane_emit_key_event(config, CONTROL_PLANE_KEY_EVENT_PRESSED, level, 0, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t duration_ms = 0;
|
||||
if (state->press_start_ms > 0 && now_ms >= state->press_start_ms) {
|
||||
duration_ms = (uint32_t)(now_ms - state->press_start_ms);
|
||||
}
|
||||
|
||||
if (config->emit_press_release) {
|
||||
control_plane_emit_key_event(config, CONTROL_PLANE_KEY_EVENT_RELEASED, level, duration_ms, 0);
|
||||
}
|
||||
|
||||
if (state->long_press_sent) {
|
||||
if (config->enable_long_press) {
|
||||
control_plane_emit_key_event(config,
|
||||
CONTROL_PLANE_KEY_EVENT_LONG_PRESS_RELEASE,
|
||||
level,
|
||||
duration_ms,
|
||||
0);
|
||||
}
|
||||
state->click_pending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config->enable_short_press) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config->enable_double_click && state->click_pending && now_ms <= state->click_deadline_ms) {
|
||||
state->click_pending = false;
|
||||
control_plane_emit_key_event(config,
|
||||
CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK,
|
||||
level,
|
||||
duration_ms,
|
||||
2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (config->enable_double_click) {
|
||||
state->click_pending = true;
|
||||
state->click_deadline_ms = now_ms + config->double_click_ms;
|
||||
} else {
|
||||
control_plane_emit_key_event(config,
|
||||
CONTROL_PLANE_KEY_EVENT_SHORT_PRESS,
|
||||
level,
|
||||
duration_ms,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_process_key_timers(void)
|
||||
{
|
||||
const int64_t now_ms = control_plane_event_timestamp_ms();
|
||||
|
||||
for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) {
|
||||
const control_plane_key_config_t *config = &s_key_configs[i];
|
||||
control_plane_key_state_t *state = &s_key_states[i];
|
||||
|
||||
if (!state->initialized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state->debounce_pending && now_ms >= state->debounce_deadline_ms) {
|
||||
state->debounce_pending = false;
|
||||
const int level = gpio_get_level(config->gpio_num);
|
||||
const bool pressed = control_plane_key_level_is_pressed(config, level);
|
||||
control_plane_handle_debounced_transition(config, state, pressed, level, now_ms);
|
||||
}
|
||||
|
||||
if (config->enable_long_press &&
|
||||
state->debounced_pressed &&
|
||||
!state->long_press_sent &&
|
||||
state->press_start_ms > 0 &&
|
||||
now_ms - state->press_start_ms >= config->long_press_ms) {
|
||||
state->long_press_sent = true;
|
||||
control_plane_emit_key_event(config,
|
||||
CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START,
|
||||
state->raw_level,
|
||||
(uint32_t)(now_ms - state->press_start_ms),
|
||||
0);
|
||||
}
|
||||
|
||||
if (state->click_pending && now_ms >= state->click_deadline_ms) {
|
||||
state->click_pending = false;
|
||||
control_plane_emit_key_event(config,
|
||||
CONTROL_PLANE_KEY_EVENT_SHORT_PRESS,
|
||||
state->raw_level,
|
||||
0,
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_key_input_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
ESP_LOGI(TAG,
|
||||
"Key input task started irq_queue_len=%d debounce=%dms long=%dms double=%dms",
|
||||
CONFIG_TQ_KEY_IRQ_QUEUE_LEN,
|
||||
CONFIG_TQ_KEY_DEBOUNCE_INTERVAL_MS,
|
||||
CONFIG_TQ_KEY_LONG_PRESS_MS,
|
||||
CONFIG_TQ_KEY_DOUBLE_CLICK_MS);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
esp_err_t setup_err = ESP_OK;
|
||||
memset(s_key_states, 0, sizeof(s_key_states));
|
||||
for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) {
|
||||
const control_plane_key_config_t *config = &s_key_configs[i];
|
||||
s_key_states[i].initialized = true;
|
||||
s_key_states[i].raw_level = gpio_get_level(config->gpio_num);
|
||||
s_key_states[i].debounced_pressed =
|
||||
control_plane_key_level_is_pressed(config, s_key_states[i].raw_level);
|
||||
|
||||
setup_err = control_plane_configure_key_gpio(config);
|
||||
if (setup_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"failed to configure key=%s gpio=%d: %s",
|
||||
control_plane_key_id_name(config->key_id),
|
||||
(int)config->gpio_num,
|
||||
esp_err_to_name(setup_err));
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"key config key=%s gpio=%d active_level=%d pullup=%d pulldown=%d initial_level=%d",
|
||||
control_plane_key_id_name(config->key_id),
|
||||
(int)config->gpio_num,
|
||||
config->active_level,
|
||||
config->pullup_enabled,
|
||||
config->pulldown_enabled,
|
||||
s_key_states[i].raw_level);
|
||||
}
|
||||
|
||||
if (setup_err != ESP_OK) {
|
||||
s_key_input_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
while (!s_stop_requested) {
|
||||
control_plane_gpio_irq_event_t irq_event = {0};
|
||||
if (xQueueReceive(s_gpio_irq_queue,
|
||||
&irq_event,
|
||||
pdMS_TO_TICKS(CONFIG_TQ_KEY_INPUT_POLL_MS)) == pdTRUE) {
|
||||
size_t index = 0;
|
||||
const control_plane_key_config_t *config =
|
||||
control_plane_find_key_config_by_gpio(irq_event.gpio_num, &index);
|
||||
if (config == NULL) {
|
||||
ESP_LOGW(TAG, "ignored gpio change gpio=%d level=%d", (int)irq_event.gpio_num, irq_event.level);
|
||||
continue;
|
||||
}
|
||||
|
||||
control_plane_key_state_t *state = &s_key_states[index];
|
||||
state->raw_level = irq_event.level;
|
||||
state->debounce_pending = true;
|
||||
state->debounce_deadline_ms = irq_event.timestamp_ms + config->debounce_ms;
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"gpio change key=%s gpio=%d level=%d",
|
||||
control_plane_key_id_name(config->key_id),
|
||||
(int)irq_event.gpio_num,
|
||||
irq_event.level);
|
||||
}
|
||||
|
||||
control_plane_process_key_timers();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Key input task stopped");
|
||||
s_key_input_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t control_plane_start_key_input_task(void)
|
||||
{
|
||||
#if CONFIG_TQ_KEY_INPUT_ENABLE
|
||||
if (s_key_input_task != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = gpio_install_isr_service(0);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s_gpio_irq_queue == NULL) {
|
||||
s_gpio_irq_queue = xQueueCreate(CONFIG_TQ_KEY_IRQ_QUEUE_LEN,
|
||||
sizeof(control_plane_gpio_irq_event_t));
|
||||
if (s_gpio_irq_queue == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
s_stop_requested = false;
|
||||
BaseType_t created = xTaskCreate(control_plane_key_input_task,
|
||||
"cp_key_input",
|
||||
CONFIG_TQ_KEY_INPUT_TASK_STACK,
|
||||
NULL,
|
||||
5,
|
||||
&s_key_input_task);
|
||||
if (created != pdPASS) {
|
||||
s_key_input_task = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
#else
|
||||
ESP_LOGI(TAG, "Key input task disabled by configuration");
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void control_plane_stop_key_input_task(void)
|
||||
{
|
||||
#if CONFIG_TQ_KEY_INPUT_ENABLE
|
||||
s_stop_requested = true;
|
||||
|
||||
for (int i = 0; i < 20 && s_key_input_task != NULL; ++i) {
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
if (s_key_input_task != NULL) {
|
||||
vTaskDelete(s_key_input_task);
|
||||
s_key_input_task = NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(s_key_configs) / sizeof(s_key_configs[0]); ++i) {
|
||||
(void)gpio_intr_disable(s_key_configs[i].gpio_num);
|
||||
(void)gpio_isr_handler_remove(s_key_configs[i].gpio_num);
|
||||
}
|
||||
|
||||
if (s_gpio_irq_queue != NULL) {
|
||||
vQueueDelete(s_gpio_irq_queue);
|
||||
s_gpio_irq_queue = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1675,6 +1675,12 @@ esp_err_t control_plane_start_runtime_workers(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t system_event_err = control_plane_start_system_event_task();
|
||||
if (system_event_err != ESP_OK) {
|
||||
control_plane_stop_runtime_workers();
|
||||
return system_event_err;
|
||||
}
|
||||
|
||||
if (control_plane_get_task_handle(&s_ptt_task) == NULL) {
|
||||
esp_err_t task_err = control_plane_create_task(control_plane_ptt_task,
|
||||
"cp_ptt",
|
||||
@@ -1703,6 +1709,12 @@ esp_err_t control_plane_start_runtime_workers(void)
|
||||
ESP_LOGI(TAG, "Slot switch worker disabled by configuration");
|
||||
#endif
|
||||
|
||||
esp_err_t key_input_err = control_plane_start_key_input_task();
|
||||
if (key_input_err != ESP_OK) {
|
||||
control_plane_stop_runtime_workers();
|
||||
return key_input_err;
|
||||
}
|
||||
|
||||
#if CONFIG_FORTUNE_ENABLE
|
||||
if (control_plane_get_task_handle(&s_daily_fortune_task) == NULL) {
|
||||
control_plane_request_fortune_refresh();
|
||||
@@ -1721,6 +1733,34 @@ esp_err_t control_plane_start_runtime_workers(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t control_plane_start_event_observer_runtime(void)
|
||||
{
|
||||
control_plane_set_runtime_stop_requested(false);
|
||||
|
||||
esp_err_t system_event_err = control_plane_start_system_event_task();
|
||||
if (system_event_err != ESP_OK) {
|
||||
control_plane_stop_event_observer_runtime();
|
||||
return system_event_err;
|
||||
}
|
||||
|
||||
esp_err_t key_input_err = control_plane_start_key_input_task();
|
||||
if (key_input_err != ESP_OK) {
|
||||
control_plane_stop_event_observer_runtime();
|
||||
return key_input_err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Event observer runtime started");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void control_plane_stop_event_observer_runtime(void)
|
||||
{
|
||||
control_plane_set_runtime_stop_requested(true);
|
||||
control_plane_stop_key_input_task();
|
||||
control_plane_stop_system_event_task();
|
||||
control_plane_set_runtime_stop_requested(false);
|
||||
}
|
||||
|
||||
void control_plane_stop_runtime_workers(void)
|
||||
{
|
||||
control_plane_set_runtime_stop_requested(true);
|
||||
@@ -1741,6 +1781,9 @@ void control_plane_stop_runtime_workers(void)
|
||||
(void)xQueueSend(queue, &stop_cmd, 0);
|
||||
}
|
||||
|
||||
control_plane_stop_key_input_task();
|
||||
control_plane_stop_system_event_task();
|
||||
|
||||
esp_err_t lcd_stop_err = control_plane_stop_lcd_render_loop(CONTROL_PLANE_TASK_STOP_WAIT_MS);
|
||||
if (lcd_stop_err == ESP_ERR_TIMEOUT) {
|
||||
control_plane_set_task_handle(&s_lcd_render_loop_task, NULL);
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "control_plane_internal.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "control_plane_events.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_TQ_SYSTEM_EVENT_ENABLE
|
||||
#define CONFIG_TQ_SYSTEM_EVENT_ENABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN
|
||||
#define CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN 32
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SYSTEM_EVENT_TASK_STACK
|
||||
#define CONFIG_TQ_SYSTEM_EVENT_TASK_STACK 4096
|
||||
#endif
|
||||
|
||||
static const char *TAG = "cp_system_event";
|
||||
|
||||
static QueueHandle_t s_system_event_queue;
|
||||
static TaskHandle_t s_system_event_task;
|
||||
static volatile bool s_stop_requested;
|
||||
|
||||
static const char *control_plane_event_family_name(control_plane_event_family_t family)
|
||||
{
|
||||
switch (family) {
|
||||
case CONTROL_PLANE_EVENT_FAMILY_KEY:
|
||||
return "KEY";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_WIFI:
|
||||
return "WIFI";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_MULTIMODAL:
|
||||
return "MULTIMODAL";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_LCD:
|
||||
return "LCD";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_DIRECTION:
|
||||
return "DIRECTION";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_CALIBRATION:
|
||||
return "CALIBRATION";
|
||||
case CONTROL_PLANE_EVENT_FAMILY_RUNTIME:
|
||||
return "RUNTIME";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *control_plane_key_id_name(control_plane_key_id_t key_id)
|
||||
{
|
||||
switch (key_id) {
|
||||
case CONTROL_PLANE_KEY_ID_PTT:
|
||||
return "PTT";
|
||||
case CONTROL_PLANE_KEY_ID_SW1:
|
||||
return "SW1";
|
||||
case CONTROL_PLANE_KEY_ID_SW2:
|
||||
return "SW2";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *control_plane_key_event_type_name(control_plane_key_event_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case CONTROL_PLANE_KEY_EVENT_PRESSED:
|
||||
return "PRESSED";
|
||||
case CONTROL_PLANE_KEY_EVENT_RELEASED:
|
||||
return "RELEASED";
|
||||
case CONTROL_PLANE_KEY_EVENT_SHORT_PRESS:
|
||||
return "SHORT_PRESS";
|
||||
case CONTROL_PLANE_KEY_EVENT_LONG_PRESS_START:
|
||||
return "LONG_PRESS_START";
|
||||
case CONTROL_PLANE_KEY_EVENT_LONG_PRESS_RELEASE:
|
||||
return "LONG_PRESS_RELEASE";
|
||||
case CONTROL_PLANE_KEY_EVENT_DOUBLE_CLICK:
|
||||
return "DOUBLE_CLICK";
|
||||
case CONTROL_PLANE_KEY_EVENT_REPEAT:
|
||||
return "REPEAT";
|
||||
case CONTROL_PLANE_KEY_EVENT_CANCELLED:
|
||||
return "CANCELLED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_dispatch_key_event(const control_plane_key_event_t *event)
|
||||
{
|
||||
if (event == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"key event key=%s type=%s gpio=%d level=%d duration=%" PRIu32 "ms clicks=%" PRIu32,
|
||||
control_plane_key_id_name(event->key_id),
|
||||
control_plane_key_event_type_name(event->type),
|
||||
(int)event->gpio_num,
|
||||
event->level,
|
||||
event->press_duration_ms,
|
||||
event->click_count);
|
||||
}
|
||||
|
||||
static void control_plane_dispatch_system_event(const control_plane_system_event_t *event)
|
||||
{
|
||||
if (event == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "dispatch family=%s", control_plane_event_family_name(event->family));
|
||||
|
||||
switch (event->family) {
|
||||
case CONTROL_PLANE_EVENT_FAMILY_KEY:
|
||||
control_plane_dispatch_key_event(&event->data.key);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "unhandled event family=%s", control_plane_event_family_name(event->family));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void control_plane_system_event_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
ESP_LOGI(TAG,
|
||||
"System event task started queue_len=%d",
|
||||
CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN);
|
||||
|
||||
while (!s_stop_requested) {
|
||||
control_plane_system_event_t event = {0};
|
||||
if (xQueueReceive(s_system_event_queue, &event, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
control_plane_dispatch_system_event(&event);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "System event task stopped");
|
||||
s_system_event_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t control_plane_post_system_event(const control_plane_system_event_t *event, uint32_t timeout_ms)
|
||||
{
|
||||
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
|
||||
if (event == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_system_event_queue == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
|
||||
if (xQueueSend(s_system_event_queue, event, timeout_ticks) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
(void)event;
|
||||
(void)timeout_ms;
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t control_plane_event_timestamp_ms(void)
|
||||
{
|
||||
return esp_timer_get_time() / 1000;
|
||||
}
|
||||
|
||||
esp_err_t control_plane_start_system_event_task(void)
|
||||
{
|
||||
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
|
||||
if (s_system_event_task != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (s_system_event_queue == NULL) {
|
||||
s_system_event_queue = xQueueCreate(CONFIG_TQ_SYSTEM_EVENT_QUEUE_LEN,
|
||||
sizeof(control_plane_system_event_t));
|
||||
if (s_system_event_queue == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
s_stop_requested = false;
|
||||
BaseType_t created = xTaskCreate(control_plane_system_event_task,
|
||||
"cp_system_event",
|
||||
CONFIG_TQ_SYSTEM_EVENT_TASK_STACK,
|
||||
NULL,
|
||||
4,
|
||||
&s_system_event_task);
|
||||
if (created != pdPASS) {
|
||||
s_system_event_task = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
#else
|
||||
ESP_LOGI(TAG, "System event task disabled by configuration");
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void control_plane_stop_system_event_task(void)
|
||||
{
|
||||
#if CONFIG_TQ_SYSTEM_EVENT_ENABLE
|
||||
s_stop_requested = true;
|
||||
|
||||
for (int i = 0; i < 20 && s_system_event_task != NULL; ++i) {
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
if (s_system_event_task != NULL) {
|
||||
vTaskDelete(s_system_event_task);
|
||||
s_system_event_task = NULL;
|
||||
}
|
||||
|
||||
if (s_system_event_queue != NULL) {
|
||||
vQueueDelete(s_system_event_queue);
|
||||
s_system_event_queue = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user