feat(power): switch to physical power-key shutdown control
This commit is contained in:
@@ -6,11 +6,23 @@
|
|||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "domain.h"
|
#include "domain.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
static const char *TAG = "control_plane_lifecycle";
|
static const char *TAG = "control_plane_lifecycle";
|
||||||
static SemaphoreHandle_t s_lock;
|
static SemaphoreHandle_t s_lock;
|
||||||
|
static QueueHandle_t s_power_key_queue;
|
||||||
|
static TaskHandle_t s_power_key_task;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
POWER_KEY_CMD_SHUTDOWN_REQUEST = 0,
|
||||||
|
} power_key_cmd_t;
|
||||||
|
|
||||||
|
static const uint16_t POWER_KEY_QUEUE_LEN = 4;
|
||||||
|
static const uint16_t POWER_KEY_TASK_STACK = 4096;
|
||||||
|
static const uint16_t POWER_KEY_STOP_RETRY_COUNT = 30;
|
||||||
|
static const uint16_t POWER_KEY_STOP_RETRY_INTERVAL_MS = 100;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
control_plane_lifecycle_state_t state;
|
control_plane_lifecycle_state_t state;
|
||||||
esp_err_t last_error;
|
esp_err_t last_error;
|
||||||
@@ -96,6 +108,89 @@ static esp_err_t lifecycle_stop_system_runtime(void) {
|
|||||||
return system_runtime_shutdown();
|
return system_runtime_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lifecycle_power_key_event_cb(domain_power_event_t event, void *user_data) {
|
||||||
|
(void)user_data;
|
||||||
|
if (event != DOMAIN_POWER_EVENT_SHUTDOWN_REQUEST || s_power_key_queue == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
power_key_cmd_t cmd = POWER_KEY_CMD_SHUTDOWN_REQUEST;
|
||||||
|
BaseType_t queued = xQueueSend(s_power_key_queue, &cmd, 0);
|
||||||
|
if (queued == pdTRUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
power_key_cmd_t dropped = POWER_KEY_CMD_SHUTDOWN_REQUEST;
|
||||||
|
(void)xQueueReceive(s_power_key_queue, &dropped, 0);
|
||||||
|
(void)xQueueSend(s_power_key_queue, &cmd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lifecycle_power_key_task_main(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
power_key_cmd_t cmd = POWER_KEY_CMD_SHUTDOWN_REQUEST;
|
||||||
|
if (xQueueReceive(s_power_key_queue, &cmd, portMAX_DELAY) != pdTRUE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd != POWER_KEY_CMD_SHUTDOWN_REQUEST) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "POWER_KEY long press detected: stopping services and powering off");
|
||||||
|
|
||||||
|
esp_err_t stop_rc = ESP_ERR_INVALID_STATE;
|
||||||
|
for (uint16_t attempt = 0; attempt < POWER_KEY_STOP_RETRY_COUNT; ++attempt) {
|
||||||
|
stop_rc = control_plane_lifecycle_stop();
|
||||||
|
if (stop_rc != ESP_ERR_INVALID_STATE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(POWER_KEY_STOP_RETRY_INTERVAL_MS));
|
||||||
|
}
|
||||||
|
if (stop_rc != ESP_OK && stop_rc != ESP_ERR_INVALID_STATE) {
|
||||||
|
ESP_LOGW(TAG, "lifecycle stop during power-off failed: %s", esp_err_to_name(stop_rc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep a tiny gap so last logs/stop actions can flush before hold release.
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
|
||||||
|
esp_err_t power_rc = system_runtime_power_off();
|
||||||
|
if (power_rc != ESP_OK && power_rc != ESP_ERR_NOT_SUPPORTED) {
|
||||||
|
ESP_LOGE(TAG, "system runtime power-off failed: %s", esp_err_to_name(power_rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lifecycle_power_key_worker_init(void) {
|
||||||
|
if (s_power_key_queue == NULL) {
|
||||||
|
s_power_key_queue = xQueueCreate(POWER_KEY_QUEUE_LEN, sizeof(power_key_cmd_t));
|
||||||
|
if (s_power_key_queue == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_power_key_task == NULL) {
|
||||||
|
BaseType_t ok = xTaskCreate(lifecycle_power_key_task_main,
|
||||||
|
"power_key_ctrl",
|
||||||
|
POWER_KEY_TASK_STACK,
|
||||||
|
NULL,
|
||||||
|
5,
|
||||||
|
&s_power_key_task);
|
||||||
|
if (ok != pdPASS) {
|
||||||
|
vQueueDelete(s_power_key_queue);
|
||||||
|
s_power_key_queue = NULL;
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return system_runtime_set_power_event_callback(lifecycle_power_key_event_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lifecycle_power_key_worker_unbind(void) {
|
||||||
|
(void)system_runtime_set_power_event_callback(NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
typedef esp_err_t (*lifecycle_step_fn_t)(void);
|
typedef esp_err_t (*lifecycle_step_fn_t)(void);
|
||||||
|
|
||||||
static esp_err_t lifecycle_run_step_with_retry(const char *stage, lifecycle_step_fn_t fn) {
|
static esp_err_t lifecycle_run_step_with_retry(const char *stage, lifecycle_step_fn_t fn) {
|
||||||
@@ -162,6 +257,12 @@ esp_err_t control_plane_lifecycle_start(void) {
|
|||||||
}
|
}
|
||||||
system_started = true;
|
system_started = true;
|
||||||
|
|
||||||
|
failed_stage = "power_key_binding";
|
||||||
|
rc = lifecycle_power_key_worker_init();
|
||||||
|
if (rc != ESP_OK) {
|
||||||
|
goto start_failed;
|
||||||
|
}
|
||||||
|
|
||||||
failed_stage = "printer_protocol";
|
failed_stage = "printer_protocol";
|
||||||
rc = lifecycle_run_step_with_retry("printer_protocol", lifecycle_start_printer_protocol);
|
rc = lifecycle_run_step_with_retry("printer_protocol", lifecycle_start_printer_protocol);
|
||||||
if (rc != ESP_OK) {
|
if (rc != ESP_OK) {
|
||||||
@@ -194,6 +295,7 @@ start_failed:
|
|||||||
(void)lifecycle_stop_printer_protocol();
|
(void)lifecycle_stop_printer_protocol();
|
||||||
}
|
}
|
||||||
if (system_started) {
|
if (system_started) {
|
||||||
|
lifecycle_power_key_worker_unbind();
|
||||||
(void)lifecycle_stop_system_runtime();
|
(void)lifecycle_stop_system_runtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +330,8 @@ esp_err_t control_plane_lifecycle_stop(void) {
|
|||||||
lifecycle_set_state_locked(CONTROL_PLANE_LIFECYCLE_STATE_STOPPING, ESP_OK, "stopping");
|
lifecycle_set_state_locked(CONTROL_PLANE_LIFECYCLE_STATE_STOPPING, ESP_OK, "stopping");
|
||||||
xSemaphoreGive(s_lock);
|
xSemaphoreGive(s_lock);
|
||||||
|
|
||||||
|
lifecycle_power_key_worker_unbind();
|
||||||
|
|
||||||
esp_err_t first_err = ESP_OK;
|
esp_err_t first_err = ESP_OK;
|
||||||
|
|
||||||
esp_err_t rc = lifecycle_stop_voice();
|
esp_err_t rc = lifecycle_stop_voice();
|
||||||
|
|||||||
@@ -15,6 +15,15 @@ esp_err_t system_runtime_start(void);
|
|||||||
esp_err_t system_runtime_shutdown(void);
|
esp_err_t system_runtime_shutdown(void);
|
||||||
esp_err_t system_runtime_bootstrap(void);
|
esp_err_t system_runtime_bootstrap(void);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DOMAIN_POWER_EVENT_SHUTDOWN_REQUEST = 0,
|
||||||
|
} domain_power_event_t;
|
||||||
|
|
||||||
|
typedef void (*domain_power_event_cb_t)(domain_power_event_t event, void *user_data);
|
||||||
|
|
||||||
|
esp_err_t system_runtime_set_power_event_callback(domain_power_event_cb_t cb, void *user_data);
|
||||||
|
esp_err_t system_runtime_power_off(void);
|
||||||
|
|
||||||
bool system_runtime_wifi_ready(void);
|
bool system_runtime_wifi_ready(void);
|
||||||
void system_runtime_get_ip(char *buf, size_t buf_len);
|
void system_runtime_get_ip(char *buf, size_t buf_len);
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,60 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
static const char *TAG = "system_runtime";
|
static const char *TAG = "system_runtime";
|
||||||
|
|
||||||
#define SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS 220
|
#define SYSTEM_RUNTIME_WIFI_HINT_TIMEOUT_MS 220
|
||||||
|
|
||||||
static bool s_wifi_hint_shown;
|
static bool s_wifi_hint_shown;
|
||||||
|
static bool s_power_key_ready;
|
||||||
|
static domain_power_event_cb_t s_power_event_cb;
|
||||||
|
static void *s_power_event_user_data;
|
||||||
|
static portMUX_TYPE s_power_event_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
static void system_runtime_dispatch_power_event(domain_power_event_t event) {
|
||||||
|
domain_power_event_cb_t cb = NULL;
|
||||||
|
void *user_data = NULL;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_power_event_lock);
|
||||||
|
cb = s_power_event_cb;
|
||||||
|
user_data = s_power_event_user_data;
|
||||||
|
portEXIT_CRITICAL(&s_power_event_lock);
|
||||||
|
|
||||||
|
if (cb != NULL) {
|
||||||
|
cb(event, user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void system_runtime_power_key_platform_cb(platform_power_key_event_t event, void *user_data) {
|
||||||
|
(void)user_data;
|
||||||
|
if (event == PLATFORM_POWER_KEY_EVENT_LONG_PRESS) {
|
||||||
|
system_runtime_dispatch_power_event(DOMAIN_POWER_EVENT_SHUTDOWN_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t system_runtime_power_key_init(void) {
|
||||||
|
if (s_power_key_ready) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = platform_power_key_init();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "platform_power_key_init failed: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = platform_power_key_set_event_callback(system_runtime_power_key_platform_cb, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "platform_power_key_set_event_callback failed: %s", esp_err_to_name(err));
|
||||||
|
platform_power_key_deinit();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_power_key_ready = true;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static void system_runtime_show_wifi_hint(void) {
|
static void system_runtime_show_wifi_hint(void) {
|
||||||
if (s_wifi_hint_shown) {
|
if (s_wifi_hint_shown) {
|
||||||
@@ -60,6 +108,11 @@ esp_err_t system_runtime_start(void) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = system_runtime_power_key_init();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
s_wifi_hint_shown = false;
|
s_wifi_hint_shown = false;
|
||||||
(void)wifi_manager_set_event_callback(system_runtime_wifi_event_handler, NULL);
|
(void)wifi_manager_set_event_callback(system_runtime_wifi_event_handler, NULL);
|
||||||
system_runtime_try_show_wifi_hint_for_unprovisioned();
|
system_runtime_try_show_wifi_hint_for_unprovisioned();
|
||||||
@@ -75,10 +128,28 @@ esp_err_t system_runtime_start(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t system_runtime_shutdown(void) {
|
esp_err_t system_runtime_shutdown(void) {
|
||||||
|
if (s_power_key_ready) {
|
||||||
|
platform_power_key_deinit();
|
||||||
|
s_power_key_ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
(void)wifi_manager_set_event_callback(NULL, NULL);
|
(void)wifi_manager_set_event_callback(NULL, NULL);
|
||||||
return wifi_manager_stop();
|
return wifi_manager_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t system_runtime_set_power_event_callback(domain_power_event_cb_t cb, void *user_data) {
|
||||||
|
portENTER_CRITICAL(&s_power_event_lock);
|
||||||
|
s_power_event_cb = cb;
|
||||||
|
s_power_event_user_data = user_data;
|
||||||
|
portEXIT_CRITICAL(&s_power_event_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t system_runtime_power_off(void) {
|
||||||
|
ESP_LOGW(TAG, "system power-off requested: releasing POWER_HOLD");
|
||||||
|
return platform_power_hold_set(false);
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t system_runtime_bootstrap(void) {
|
esp_err_t system_runtime_bootstrap(void) {
|
||||||
return system_runtime_start();
|
return system_runtime_start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ idf_component_register(
|
|||||||
"src/display_lcd_decode_image.c"
|
"src/display_lcd_decode_image.c"
|
||||||
"src/display_st7789p3.c"
|
"src/display_st7789p3.c"
|
||||||
"src/platform_mic_key.c"
|
"src/platform_mic_key.c"
|
||||||
|
"src/platform_power_key.c"
|
||||||
"src/voice_audio.c"
|
"src/voice_audio.c"
|
||||||
"src/runtime_policy.c"
|
"src/runtime_policy.c"
|
||||||
INCLUDE_DIRS
|
INCLUDE_DIRS
|
||||||
|
|||||||
@@ -90,6 +90,21 @@ esp_err_t platform_mic_key_init(void);
|
|||||||
void platform_mic_key_deinit(void);
|
void platform_mic_key_deinit(void);
|
||||||
esp_err_t platform_mic_key_set_event_callback(platform_mic_key_event_cb_t cb, void *user_data);
|
esp_err_t platform_mic_key_set_event_callback(platform_mic_key_event_cb_t cb, void *user_data);
|
||||||
|
|
||||||
|
// ---------- power_key ----------
|
||||||
|
typedef enum {
|
||||||
|
PLATFORM_POWER_KEY_EVENT_LONG_PRESS = 0,
|
||||||
|
PLATFORM_POWER_KEY_EVENT_PRESS_UP,
|
||||||
|
} platform_power_key_event_t;
|
||||||
|
|
||||||
|
typedef void (*platform_power_key_event_cb_t)(platform_power_key_event_t event, void *user_data);
|
||||||
|
|
||||||
|
esp_err_t platform_power_key_init(void);
|
||||||
|
void platform_power_key_deinit(void);
|
||||||
|
esp_err_t platform_power_key_set_event_callback(platform_power_key_event_cb_t cb, void *user_data);
|
||||||
|
|
||||||
|
// Controls the board hold line: true keeps power on, false releases hold.
|
||||||
|
esp_err_t platform_power_hold_set(bool hold_on);
|
||||||
|
|
||||||
// ---------- voice_audio ----------
|
// ---------- voice_audio ----------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
|
|||||||
@@ -239,3 +239,23 @@ esp_err_t platform_bootstrap_init(void) {
|
|||||||
s_initialized = true;
|
s_initialized = true;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_power_hold_set(bool hold_on) {
|
||||||
|
if (CONFIG_TQ_POWER_HOLD_PIN < 0) {
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int level = hold_on ? 1 : 0;
|
||||||
|
if (!s_board_gpio_initialized) {
|
||||||
|
return board_gpio_config_output(CONFIG_TQ_POWER_HOLD_PIN, level, "power_hold");
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = gpio_set_level(CONFIG_TQ_POWER_HOLD_PIN, level);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "gpio_set_level failed: power_hold pin=%d level=%d err=%s",
|
||||||
|
CONFIG_TQ_POWER_HOLD_PIN,
|
||||||
|
level,
|
||||||
|
esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|||||||
125
components/platform/src/platform_power_key.c
Normal file
125
components/platform/src/platform_power_key.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "button_gpio.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "iot_button.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
static const char *TAG = "platform_power_key";
|
||||||
|
static const uint16_t POWER_KEY_SHORT_PRESS_MS = 40;
|
||||||
|
static const uint16_t POWER_KEY_LONG_PRESS_MS = 2000;
|
||||||
|
|
||||||
|
static button_handle_t s_power_key_button;
|
||||||
|
static platform_power_key_event_cb_t s_event_cb;
|
||||||
|
static void *s_event_cb_user_data;
|
||||||
|
static portMUX_TYPE s_event_cb_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
static void power_key_dispatch_event(platform_power_key_event_t event) {
|
||||||
|
platform_power_key_event_cb_t cb = NULL;
|
||||||
|
void *user_data = NULL;
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_event_cb_lock);
|
||||||
|
cb = s_event_cb;
|
||||||
|
user_data = s_event_cb_user_data;
|
||||||
|
portEXIT_CRITICAL(&s_event_cb_lock);
|
||||||
|
|
||||||
|
if (cb != NULL) {
|
||||||
|
cb(event, user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void power_key_long_press_cb(void *button_handle, void *usr_data) {
|
||||||
|
(void)button_handle;
|
||||||
|
(void)usr_data;
|
||||||
|
power_key_dispatch_event(PLATFORM_POWER_KEY_EVENT_LONG_PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void power_key_press_up_cb(void *button_handle, void *usr_data) {
|
||||||
|
(void)button_handle;
|
||||||
|
(void)usr_data;
|
||||||
|
power_key_dispatch_event(PLATFORM_POWER_KEY_EVENT_PRESS_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_power_key_init(void) {
|
||||||
|
if (CONFIG_TQ_POWER_KEY_PIN < 0) {
|
||||||
|
ESP_LOGW(TAG, "POWER_KEY disabled by config: pin=%d", CONFIG_TQ_POWER_KEY_PIN);
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_power_key_button != NULL) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_config_t button_cfg = {
|
||||||
|
.long_press_time = POWER_KEY_LONG_PRESS_MS,
|
||||||
|
.short_press_time = POWER_KEY_SHORT_PRESS_MS,
|
||||||
|
};
|
||||||
|
button_gpio_config_t gpio_cfg = {
|
||||||
|
.gpio_num = CONFIG_TQ_POWER_KEY_PIN,
|
||||||
|
.active_level = 0,
|
||||||
|
.enable_power_save = false,
|
||||||
|
.disable_pull = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t err = iot_button_new_gpio_device(&button_cfg, &gpio_cfg, &s_power_key_button);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"iot_button_new_gpio_device failed: pin=%d err=%s",
|
||||||
|
CONFIG_TQ_POWER_KEY_PIN,
|
||||||
|
esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iot_button_register_cb(s_power_key_button,
|
||||||
|
BUTTON_LONG_PRESS_START,
|
||||||
|
NULL,
|
||||||
|
power_key_long_press_cb,
|
||||||
|
NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "register BUTTON_LONG_PRESS_START failed: %s", esp_err_to_name(err));
|
||||||
|
(void)iot_button_delete(s_power_key_button);
|
||||||
|
s_power_key_button = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iot_button_register_cb(s_power_key_button, BUTTON_PRESS_UP, NULL, power_key_press_up_cb, NULL);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "register BUTTON_PRESS_UP failed: %s", esp_err_to_name(err));
|
||||||
|
(void)iot_button_delete(s_power_key_button);
|
||||||
|
s_power_key_button = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"POWER_KEY initialized: gpio=%d active_level=low long_press_ms=%u",
|
||||||
|
CONFIG_TQ_POWER_KEY_PIN,
|
||||||
|
(unsigned)POWER_KEY_LONG_PRESS_MS);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void platform_power_key_deinit(void) {
|
||||||
|
if (s_power_key_button != NULL) {
|
||||||
|
(void)iot_button_delete(s_power_key_button);
|
||||||
|
s_power_key_button = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_event_cb_lock);
|
||||||
|
s_event_cb = NULL;
|
||||||
|
s_event_cb_user_data = NULL;
|
||||||
|
portEXIT_CRITICAL(&s_event_cb_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_power_key_set_event_callback(platform_power_key_event_cb_t cb, void *user_data) {
|
||||||
|
if (s_power_key_button == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_event_cb_lock);
|
||||||
|
s_event_cb = cb;
|
||||||
|
s_event_cb_user_data = user_data;
|
||||||
|
portEXIT_CRITICAL(&s_event_cb_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# ESP32-S3 GPIO Mapping
|
# ESP32-S3 GPIO Mapping
|
||||||
|
|
||||||
本文件记录当前板级 GPIO 分配,来源于硬件对接表(2026-02-26)。
|
本文件按当前固件实现校对(代码 + 当前 `sdkconfig`),审计日期:2026-03-02。
|
||||||
|
|
||||||
## GPIO Table
|
## GPIO Table (Current Build)
|
||||||
|
|
||||||
| Board Pin | GPIO | Signal | Direction | Notes |
|
| Board Pin | GPIO | Signal | Direction (Current Build) | Notes |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| 1 | GND | GND | - | Ground |
|
| 1 | GND | GND | - | Ground |
|
||||||
| 2 | 3V3 | 3V3 | - | 3.3V Power |
|
| 2 | 3V3 | 3V3 | - | 3.3V Power |
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
| 7 | IO7 | ES8311_I2C_SCL | Output | Voice codec I2C SCL |
|
| 7 | IO7 | ES8311_I2C_SCL | Output | Voice codec I2C SCL |
|
||||||
| 8 | IO15 | BATTERY_ADC | Input | Battery voltage ADC detect |
|
| 8 | IO15 | BATTERY_ADC | Input | Battery voltage ADC detect |
|
||||||
| 9 | IO16 | PRINT_STB1_2 | Output | Printer strobe 1/2 |
|
| 9 | IO16 | PRINT_STB1_2 | Output | Printer strobe 1/2 |
|
||||||
| 10 | IO17 | PRINT_PAPER | Input | Paper detect |
|
| 10 | IO17 | PRINT_PAPER | Input | Paper-present level is controlled by `CONFIG_TQ_PRINTER_PAPER_PRESENT_LEVEL` (current: `0`, low means paper present) |
|
||||||
| 11 | IO18 | NTC_ADC | Input | NTC ADC |
|
| 11 | IO18 | NTC_ADC | Input | NTC ADC |
|
||||||
| 12 | IO8 | PRINT_OUTA_P | Output | Printer OUTA+ |
|
| 12 | IO8 | PRINT_OUTA_P | Output | Printer OUTA+ |
|
||||||
| 13 | IO19 | PRINT_OUTA_N | Output | Printer OUTA- |
|
| 13 | IO19 | PRINT_OUTA_N | Output | Printer OUTA- |
|
||||||
@@ -23,16 +23,16 @@
|
|||||||
| 15 | IO3 | PRINT_OUTB_P | Output | Printer OUTB+ |
|
| 15 | IO3 | PRINT_OUTB_P | Output | Printer OUTB+ |
|
||||||
| 16 | IO46 | PRINT_OUTB_N | Output | Printer OUTB- |
|
| 16 | IO46 | PRINT_OUTB_N | Output | Printer OUTB- |
|
||||||
| 17 | IO9 | SPI_SHARED_MOSI | Output | Printer/Screen shared MOSI |
|
| 17 | IO9 | SPI_SHARED_MOSI | Output | Printer/Screen shared MOSI |
|
||||||
| 18 | IO10 | PRINTER_MISO | Input | Printer MISO |
|
| 18 | IO10 | PRINTER_MISO | Input | Shared SPI MISO used by current screen driver config |
|
||||||
| 19 | IO11 | SPI_SHARED_CLK | Output | Printer/Screen shared CLK |
|
| 19 | IO11 | SPI_SHARED_CLK | Output | Printer/Screen shared CLK |
|
||||||
| 20 | IO12 | SCREEN_DC | Output | ST7789 DC |
|
| 20 | IO12 | SCREEN_DC | Output | ST7789 DC |
|
||||||
| 21 | IO13 | SCREEN_CS1 | Output | ST7789 CS |
|
| 21 | IO13 | SCREEN_CS (SCREEN_CS1) | Output | ST7789 CS |
|
||||||
| 22 | IO14 | MIC_KEY | Input | Mic key (can ADC-mux 2 keys if needed) |
|
| 22 | IO14 | MIC_KEY | Input | Mic key (supports ADC-mux extension path) |
|
||||||
| 23 | IO21 | LED_G | Output | Green LED |
|
| 23 | IO21 | LED_G | Output | Green LED |
|
||||||
| 24 | IO47 | POWER_KEY | Input | Hold low for 2s to request power-off logic |
|
| 24 | IO47 | POWER_KEY | Input | Active-low power key, long press 2s triggers lifecycle stop and power-off hold release |
|
||||||
| 25 | IO48 | SCREEN_BACKLIGHT | Output | High level turns backlight on |
|
| 25 | IO48 | SCREEN_BACKLIGHT | Output | Backlight active level by `CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH` (current: `y`, high = on) |
|
||||||
| 26 | IO45 | PRINT_LAT | Output | Printer LAT |
|
| 26 | IO45 | PRINT_LAT | Output | Printer LAT |
|
||||||
| 27 | IO0 | KEY_PRINT / BOOST_CTRL | Input/Output | Print confirm key or 7.2V boost control |
|
| 27 | IO0 | KEY_PRINT / BOOST_CTRL | Output (current) / Input (optional) | Current build uses boost mode at boot (`CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT=y`), so this pin is driven as boost control |
|
||||||
| 28 | IO35 | N/A | - | Not available |
|
| 28 | IO35 | N/A | - | Not available |
|
||||||
| 29 | IO36 | N/A | - | Not available |
|
| 29 | IO36 | N/A | - | Not available |
|
||||||
| 30 | IO37 | N/A | - | Not available |
|
| 30 | IO37 | N/A | - | Not available |
|
||||||
@@ -48,18 +48,46 @@
|
|||||||
| 40 | GND | GND | - | Ground |
|
| 40 | GND | GND | - | Ground |
|
||||||
| 41 | GND | GND | - | Ground |
|
| 41 | GND | GND | - | Ground |
|
||||||
|
|
||||||
## Firmware Mapping Notes
|
## Additional Runtime Pin Behavior
|
||||||
|
|
||||||
- `main/Kconfig.projbuild` contains board pin configs prefixed with `TQ_*`.
|
| Logical Signal | Config | Current Value | Behavior |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| SCREEN_RESET | `CONFIG_TQ_SCREEN_RESET_PIN` | `-1` | No dedicated reset pin. Runtime falls back to `CONFIG_TQ_SCREEN_BACKLIGHT_PIN` (`IO48`) as reset pin. |
|
||||||
|
|
||||||
|
## Firmware Mapping Notes (Current Implementation)
|
||||||
|
|
||||||
|
- Board pin mapping is configured through `main/Kconfig.projbuild` (`CONFIG_TQ_*`) and resolved from `sdkconfig` at build time.
|
||||||
|
- GPIO usage is config-driven. Current code path does not rely on `GPIO_NUM_xx` hardcoded pin numbers.
|
||||||
- `components/platform/src/platform_bootstrap.c` initializes critical GPIO on startup:
|
- `components/platform/src/platform_bootstrap.c` initializes critical GPIO on startup:
|
||||||
- `POWER_HOLD` asserted high early.
|
- `POWER_HOLD` is asserted high early.
|
||||||
- `SCREEN_DC/SCREEN_CS/SCREEN_BACKLIGHT` default states.
|
- `LED_R` and `LED_G` default low.
|
||||||
- `KEY_PRINT` defaults to boost-enable output mode (`CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT=y`).
|
- `SCREEN_CS/DC/BACKLIGHT` are initialized with configured defaults.
|
||||||
- key/sensor inputs (`POWER_KEY`, `MIC_KEY`, `PRINT_PAPER`, ADC inputs) default directions.
|
- `KEY_PRINT` is output boost control when `CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT=y`, otherwise input with pull-up.
|
||||||
- `components/platform/src/display_st7789.c` provides ST7789 driver integration:
|
- `POWER_KEY`, `MIC_KEY`, `PRINT_PAPER` default to input with pull-up.
|
||||||
- SPI bus (`MOSI=IO9`, `CLK=IO11`) + `esp_lcd_new_panel_st7789`.
|
- Physical power-key handling uses `managed_components/espressif__button`:
|
||||||
- Optional boot test pattern via `CONFIG_TQ_SCREEN_TEST_PATTERN_ON_BOOT`.
|
- Platform layer (`platform_power_key.c`) reports long-press events (`2s`, active-low).
|
||||||
- Voice codec and I2S pin defaults are aligned to this table:
|
- Control-plane consumes the shutdown request and performs graceful stop, then calls `platform_power_hold_set(false)` through `system_runtime_power_off()`.
|
||||||
- I2C: `SDA=6`, `SCL=7`
|
- Thermal printer runtime behavior (`components/platform/src/thermal_printer.c`):
|
||||||
- I2S: `MCLK=38`, `BCLK=39`, `WS=41`, `DIN=40`, `DOUT=42`
|
- `STB` lines are active-high.
|
||||||
- PA: `IO20`
|
- `LAT` idles high and is pulsed low-high.
|
||||||
|
- Shared `MOSI/CLK` are temporarily switched to GPIO bit-bang for printing.
|
||||||
|
- Paper detect compares GPIO level against `runtime_policy_printer_paper_present_level()`.
|
||||||
|
- Display stack is:
|
||||||
|
- `platform_display_*` (`display_st7789.c`) -> `lcd_module_*` (`display_lcd_module.c`) -> `st7789p3_*` (`display_st7789p3.c`).
|
||||||
|
- Current implementation no longer describes panel setup in `display_st7789.c` directly.
|
||||||
|
- LCD path includes shared-SPI recovery after printer takes over shared pins.
|
||||||
|
- Voice path (`components/platform/src/voice_audio.c`):
|
||||||
|
- I2C defaults to `SDA=6`, `SCL=7`, with internal pull-up enabled in both I2C code paths.
|
||||||
|
- I2S defaults to `MCLK=38`, `BCLK=39`, `WS=41`, `DOUT=42`, `DIN=40`.
|
||||||
|
- `PA` control defaults to `IO20` and polarity can be flipped by `CONFIG_TQ_VOICE_CODEC_PA_PIN_REVERSED`.
|
||||||
|
|
||||||
|
## Condition-Driven Behavior (Important)
|
||||||
|
|
||||||
|
| Config | Current | Impact |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT` | `y` | `KEY_PRINT/BOOST_CTRL` works as output boost-control at boot/runtime instead of key input |
|
||||||
|
| `CONFIG_TQ_KEY_PRINT_BOOST_ACTIVE_HIGH` | `y` | Boost enable level is high |
|
||||||
|
| `CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH` | `y` | Backlight ON level is high |
|
||||||
|
| `CONFIG_TQ_SCREEN_RESET_PIN` | `-1` | Screen reset falls back to backlight pin |
|
||||||
|
| `CONFIG_TQ_SCREEN_ENABLE` | `y` | Screen driver and its GPIO paths are compiled in |
|
||||||
|
| `CONFIG_TQ_PRINTER_PAPER_PRESENT_LEVEL` | `0` | Paper detect treats GPIO low as paper present |
|
||||||
|
|||||||
Reference in New Issue
Block a user