From e7aa0e75bffae9c3df772bef9075f5e66beec0a8 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 26 Feb 2026 16:06:28 +0800 Subject: [PATCH] feat(platform): integrate st7789 boot test display and board gpio mapping --- README.md | 18 ++ components/platform/CMakeLists.txt | 2 + components/platform/include/platform.h | 5 + components/platform/src/display_st7789.c | 317 +++++++++++++++++++ components/platform/src/platform_bootstrap.c | 190 +++++++++++ docs/gpio-map.md | 65 ++++ main/Kconfig.projbuild | 227 ++++++++++++- sdkconfig.defaults | 49 ++- 8 files changed, 860 insertions(+), 13 deletions(-) create mode 100644 components/platform/src/display_st7789.c create mode 100644 docs/gpio-map.md diff --git a/README.md b/README.md index edcf645..1a4b0b7 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,24 @@ In `menuconfig -> TQ Controller Config`: - `TQ_VOICE_OPUS_BITRATE_KBPS` - `TQ_VOICE_I2C_*` / `TQ_VOICE_I2S_*` - `TQ_VOICE_CODEC_*` +- Board GPIO map fields: + - `TQ_POWER_*`, `TQ_LED_*`, `TQ_SCREEN_*`, `TQ_PRINT_*`, `TQ_SPI_*` + - `TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT` / `TQ_KEY_PRINT_BOOST_ACTIVE_HIGH` +- ST7789 screen fields: + - `TQ_SCREEN_ENABLE` + - `TQ_SCREEN_TEST_PATTERN_ON_BOOT` + - `TQ_SCREEN_PIXEL_CLOCK_HZ` + - `TQ_SCREEN_SPI_MODE` + - `TQ_SCREEN_H_RES` / `TQ_SCREEN_V_RES` + - `TQ_SCREEN_DRAW_LINES` + - `TQ_SCREEN_COLOR_ORDER_BGR` + - `TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH` + - `TQ_SCREEN_MIRROR_*` / `TQ_SCREEN_SWAP_XY` + - `TQ_SCREEN_X_GAP` / `TQ_SCREEN_Y_GAP` + - `TQ_SCREEN_RESET_PIN` + +Board pin assignment reference: +- [`docs/gpio-map.md`](docs/gpio-map.md) When `TQ_API_KEY` is not empty, each request must include: diff --git a/components/platform/CMakeLists.txt b/components/platform/CMakeLists.txt index 27f8c5e..ba0704c 100644 --- a/components/platform/CMakeLists.txt +++ b/components/platform/CMakeLists.txt @@ -3,6 +3,7 @@ idf_component_register( "src/platform_bootstrap.c" "src/wifi_manager.c" "src/ble_printer_client.c" + "src/display_st7789.c" "src/voice_audio.c" "src/runtime_policy.c" "src/runtime_diagnostics.c" @@ -18,6 +19,7 @@ idf_component_register( esp_event nvs_flash mbedtls + esp_lcd esp_codec_dev esp_audio_codec driver diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index 898eb90..2b0d049 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -37,6 +37,11 @@ bool ble_printer_client_is_connected(void); void ble_printer_client_get_link_state(ble_link_state_t *out_state); esp_err_t ble_printer_client_write(const uint8_t *data, size_t len); +// ---------- display_st7789 ---------- +esp_err_t platform_display_init(void); +esp_err_t platform_display_show_test_pattern(void); +void platform_display_deinit(void); + // ---------- voice_audio ---------- typedef struct { uint32_t sample_rate; diff --git a/components/platform/src/display_st7789.c b/components/platform/src/display_st7789.c new file mode 100644 index 0000000..6bf2f46 --- /dev/null +++ b/components/platform/src/display_st7789.c @@ -0,0 +1,317 @@ +#include "platform.h" + +#include +#include +#include + +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "esp_check.h" +#include "esp_heap_caps.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_log.h" +#include "sdkconfig.h" + +static const char *TAG = "display_st7789"; + +#define ST7789_SPI_HOST SPI2_HOST +#define ST7789_COLOR_BYTES_PER_PIXEL 2 + +#ifndef CONFIG_TQ_SCREEN_SPI_MODE +#define CONFIG_TQ_SCREEN_SPI_MODE 0 +#endif + +#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH +#define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1 +#endif + +#if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH +#define ST7789_BACKLIGHT_ON_LEVEL 1 +#define ST7789_BACKLIGHT_OFF_LEVEL 0 +#else +#define ST7789_BACKLIGHT_ON_LEVEL 0 +#define ST7789_BACKLIGHT_OFF_LEVEL 1 +#endif + +typedef struct { + esp_lcd_panel_io_handle_t io_handle; + esp_lcd_panel_handle_t panel_handle; + bool bus_owned; + bool initialized; +} st7789_display_state_t; + +static st7789_display_state_t s_display; + +#if CONFIG_TQ_SCREEN_COLOR_ORDER_BGR +#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_BGR +#else +#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_RGB +#endif + +#if CONFIG_TQ_SCREEN_SWAP_XY +#define ST7789_SWAP_XY true +#else +#define ST7789_SWAP_XY false +#endif + +#if CONFIG_TQ_SCREEN_MIRROR_X +#define ST7789_MIRROR_X true +#else +#define ST7789_MIRROR_X false +#endif + +#if CONFIG_TQ_SCREEN_MIRROR_Y +#define ST7789_MIRROR_Y true +#else +#define ST7789_MIRROR_Y false +#endif + +static uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) { + return (uint16_t)(((uint16_t)(r & 0xF8) << 8) | + ((uint16_t)(g & 0xFC) << 3) | + ((uint16_t)(b & 0xF8) >> 3)); +} + +static uint16_t test_pattern_color(int x, int y, int h_res, int v_res) { + int top = v_res / 3; + int middle = (v_res * 2) / 3; + + if (y < top) { + uint8_t r = (uint8_t)((x * 255) / (h_res - 1)); + uint8_t g = (uint8_t)(255 - r); + return rgb565(r, g, 48); + } + + if (y < middle) { + uint8_t gray = (uint8_t)(((y - top) * 255) / ((middle - top) > 1 ? (middle - top - 1) : 1)); + return rgb565(gray, gray, gray); + } + + bool checker = (((x / 16) + (y / 16)) & 1) != 0; + return checker ? rgb565(250, 250, 250) : rgb565(8, 8, 8); +} + +static void fill_test_pattern_chunk(uint16_t *buf, + int y_start, + int lines, + int h_res, + int v_res) { + for (int row = 0; row < lines; ++row) { + int y = y_start + row; + for (int x = 0; x < h_res; ++x) { + uint16_t color = test_pattern_color(x, y, h_res, v_res); + + if (x == 0 || y == 0 || x == (h_res - 1) || y == (v_res - 1)) { + color = rgb565(255, 0, 0); + } else if (x == (h_res / 2) || y == (v_res / 2)) { + color = rgb565(255, 255, 255); + } + + buf[row * h_res + x] = color; + } + } +} + +void platform_display_deinit(void) { +#if CONFIG_TQ_SCREEN_ENABLE + if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) { + (void)gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL); + } + + if (s_display.panel_handle != NULL) { + (void)esp_lcd_panel_del(s_display.panel_handle); + s_display.panel_handle = NULL; + } + if (s_display.io_handle != NULL) { + (void)esp_lcd_panel_io_del(s_display.io_handle); + s_display.io_handle = NULL; + } + if (s_display.bus_owned) { + (void)spi_bus_free(ST7789_SPI_HOST); + s_display.bus_owned = false; + } + s_display.initialized = false; +#endif +} + +esp_err_t platform_display_init(void) { +#if !CONFIG_TQ_SCREEN_ENABLE + return ESP_ERR_NOT_SUPPORTED; +#else + if (s_display.initialized) { + return ESP_OK; + } + + if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) { + ESP_RETURN_ON_ERROR(gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL), + TAG, + "set backlight off failed"); + } + + const size_t max_transfer_size = (size_t)CONFIG_TQ_SCREEN_H_RES * + (size_t)CONFIG_TQ_SCREEN_DRAW_LINES * + ST7789_COLOR_BYTES_PER_PIXEL + 8; + + spi_bus_config_t bus_cfg = { + .sclk_io_num = CONFIG_TQ_SPI_SHARED_CLK_PIN, + .mosi_io_num = CONFIG_TQ_SPI_SHARED_MOSI_PIN, + .miso_io_num = -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = (int)max_transfer_size, + }; + + esp_err_t err = spi_bus_initialize(ST7789_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO); + if (err == ESP_OK) { + s_display.bus_owned = true; + } else if (err == ESP_ERR_INVALID_STATE) { + s_display.bus_owned = false; + ESP_LOGW(TAG, "SPI host already initialized, reuse host=%d", ST7789_SPI_HOST); + } else { + return err; + } + + esp_lcd_panel_io_spi_config_t io_cfg = { + .cs_gpio_num = CONFIG_TQ_SCREEN_CS_PIN, + .dc_gpio_num = CONFIG_TQ_SCREEN_DC_PIN, + .spi_mode = CONFIG_TQ_SCREEN_SPI_MODE, + .pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ, + .trans_queue_depth = 1, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ST7789_SPI_HOST, &io_cfg, &s_display.io_handle); + if (err != ESP_OK) { + platform_display_deinit(); + return err; + } + + esp_lcd_panel_dev_config_t panel_cfg = { + .reset_gpio_num = CONFIG_TQ_SCREEN_RESET_PIN, + .rgb_ele_order = ST7789_COLOR_ORDER, + .bits_per_pixel = 16, + }; + err = esp_lcd_new_panel_st7789(s_display.io_handle, &panel_cfg, &s_display.panel_handle); + if (err != ESP_OK) { + platform_display_deinit(); + return err; + } + + err = esp_lcd_panel_reset(s_display.panel_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "panel reset failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_init(s_display.panel_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "panel init failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_swap_xy(s_display.panel_handle, ST7789_SWAP_XY); + if (err != ESP_OK) { + ESP_LOGE(TAG, "swap xy failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_mirror(s_display.panel_handle, ST7789_MIRROR_X, ST7789_MIRROR_Y); + if (err != ESP_OK) { + ESP_LOGE(TAG, "mirror failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_set_gap(s_display.panel_handle, CONFIG_TQ_SCREEN_X_GAP, CONFIG_TQ_SCREEN_Y_GAP); + if (err != ESP_OK) { + ESP_LOGE(TAG, "set gap failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_invert_color(s_display.panel_handle, false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "invert control failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + err = esp_lcd_panel_disp_on_off(s_display.panel_handle, true); + if (err != ESP_OK) { + ESP_LOGE(TAG, "display on failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + + if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) { + err = gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_ON_LEVEL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "set backlight on failed: %s", esp_err_to_name(err)); + platform_display_deinit(); + return err; + } + } + + s_display.initialized = true; + ESP_LOGI(TAG, + "st7789 ready: spi_host=%d sclk=%d mosi=%d dc=%d cs=%d rst=%d bk=%d bk_on=%d spi_mode=%d size=%dx%d pclk=%u", + ST7789_SPI_HOST, + CONFIG_TQ_SPI_SHARED_CLK_PIN, + CONFIG_TQ_SPI_SHARED_MOSI_PIN, + CONFIG_TQ_SCREEN_DC_PIN, + CONFIG_TQ_SCREEN_CS_PIN, + CONFIG_TQ_SCREEN_RESET_PIN, + CONFIG_TQ_SCREEN_BACKLIGHT_PIN, + ST7789_BACKLIGHT_ON_LEVEL, + CONFIG_TQ_SCREEN_SPI_MODE, + CONFIG_TQ_SCREEN_H_RES, + CONFIG_TQ_SCREEN_V_RES, + (unsigned)CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ); + return ESP_OK; +#endif +} + +esp_err_t platform_display_show_test_pattern(void) { +#if !CONFIG_TQ_SCREEN_ENABLE + return ESP_ERR_NOT_SUPPORTED; +#else + ESP_RETURN_ON_ERROR(platform_display_init(), TAG, "display init failed"); + + const int h_res = CONFIG_TQ_SCREEN_H_RES; + const int v_res = CONFIG_TQ_SCREEN_V_RES; + const int draw_lines = CONFIG_TQ_SCREEN_DRAW_LINES > 0 ? CONFIG_TQ_SCREEN_DRAW_LINES : 1; + const size_t buf_pixels = (size_t)h_res * (size_t)draw_lines; + const size_t buf_size = buf_pixels * ST7789_COLOR_BYTES_PER_PIXEL; + + uint16_t *draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + if (draw_buf == NULL) { + draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_8BIT); + } + ESP_RETURN_ON_FALSE(draw_buf != NULL, ESP_ERR_NO_MEM, TAG, "alloc draw buffer failed"); + + ESP_LOGI(TAG, "draw test pattern start"); + + esp_err_t err = ESP_OK; + for (int y = 0; y < v_res && err == ESP_OK; y += draw_lines) { + int chunk_lines = draw_lines; + if (y + chunk_lines > v_res) { + chunk_lines = v_res - y; + } + + fill_test_pattern_chunk(draw_buf, y, chunk_lines, h_res, v_res); + err = esp_lcd_panel_draw_bitmap(s_display.panel_handle, 0, y, h_res, y + chunk_lines, draw_buf); + if (err != ESP_OK) { + break; + } + + // Wait for queued color transfer to finish before reusing draw_buf. + err = esp_lcd_panel_io_tx_param(s_display.io_handle, -1, NULL, 0); + } + + heap_caps_free(draw_buf); + + if (err == ESP_OK) { + ESP_LOGI(TAG, "draw test pattern done"); + } + return err; +#endif +} diff --git a/components/platform/src/platform_bootstrap.c b/components/platform/src/platform_bootstrap.c index 98e6698..4421198 100644 --- a/components/platform/src/platform_bootstrap.c +++ b/components/platform/src/platform_bootstrap.c @@ -2,14 +2,43 @@ #include +#include "driver/gpio.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "mbedtls/platform.h" #include "nvs_flash.h" +#include "sdkconfig.h" static bool s_initialized; +static bool s_board_gpio_initialized; static const char *TAG = "platform_bootstrap"; +#ifndef CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT +#define CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT 1 +#endif + +#ifndef CONFIG_TQ_KEY_PRINT_BOOST_ACTIVE_HIGH +#define CONFIG_TQ_KEY_PRINT_BOOST_ACTIVE_HIGH 1 +#endif + +#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH +#define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1 +#endif + +#if CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT +#if CONFIG_TQ_KEY_PRINT_BOOST_ACTIVE_HIGH +#define KEY_PRINT_BOOST_LEVEL 1 +#else +#define KEY_PRINT_BOOST_LEVEL 0 +#endif +#endif + +#if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH +#define BOOT_SCREEN_BACKLIGHT_ON_LEVEL 1 +#else +#define BOOT_SCREEN_BACKLIGHT_ON_LEVEL 0 +#endif + static void *tls_calloc_prefer_psram(size_t n, size_t size) { void *ptr = heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (ptr == NULL) { @@ -22,11 +51,157 @@ static void tls_free(void *ptr) { heap_caps_free(ptr); } +static esp_err_t board_gpio_config_output(int gpio_num, int level, const char *name) { + if (gpio_num < 0) { + return ESP_OK; + } + + gpio_config_t cfg = { + .pin_bit_mask = 1ULL << gpio_num, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + esp_err_t err = gpio_config(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_config output failed: %s pin=%d err=%s", name, gpio_num, esp_err_to_name(err)); + return err; + } + err = gpio_set_level(gpio_num, level); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_set_level failed: %s pin=%d level=%d err=%s", name, gpio_num, level, esp_err_to_name(err)); + return err; + } + return ESP_OK; +} + +static esp_err_t board_gpio_config_input(int gpio_num, bool pull_up, const char *name) { + if (gpio_num < 0) { + return ESP_OK; + } + + gpio_config_t cfg = { + .pin_bit_mask = 1ULL << gpio_num, + .mode = GPIO_MODE_INPUT, + .pull_up_en = pull_up ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + esp_err_t err = gpio_config(&cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "gpio_config input failed: %s pin=%d err=%s", name, gpio_num, esp_err_to_name(err)); + return err; + } + return ESP_OK; +} + +static esp_err_t platform_board_gpio_init(void) { + if (s_board_gpio_initialized) { + return ESP_OK; + } + + // Must be asserted early to keep the board powered. + esp_err_t err = board_gpio_config_output(CONFIG_TQ_POWER_HOLD_PIN, 1, "power_hold"); + if (err != ESP_OK) { + return err; + } + + // Default UI GPIO states. + err = board_gpio_config_output(CONFIG_TQ_LED_R_PIN, 0, "led_r"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_output(CONFIG_TQ_LED_G_PIN, 0, "led_g"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_output(CONFIG_TQ_SCREEN_CS_PIN, 1, "screen_cs"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_output(CONFIG_TQ_SCREEN_DC_PIN, 0, "screen_dc"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_output(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, + BOOT_SCREEN_BACKLIGHT_ON_LEVEL, + "screen_backlight"); + if (err != ESP_OK) { + return err; + } + + // Key/power/sensor GPIO input defaults. + err = board_gpio_config_input(CONFIG_TQ_POWER_KEY_PIN, true, "power_key"); + if (err != ESP_OK) { + return err; + } +#if CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT + err = board_gpio_config_output(CONFIG_TQ_KEY_PRINT_PIN, KEY_PRINT_BOOST_LEVEL, "key_print_boost"); + if (err != ESP_OK) { + return err; + } +#else + err = board_gpio_config_input(CONFIG_TQ_KEY_PRINT_PIN, true, "key_print"); + if (err != ESP_OK) { + return err; + } +#endif + err = board_gpio_config_input(CONFIG_TQ_MIC_KEY_PIN, true, "mic_key"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_input(CONFIG_TQ_PRINT_PAPER_PIN, true, "print_paper"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_input(CONFIG_TQ_BATTERY_ADC_PIN, false, "battery_adc"); + if (err != ESP_OK) { + return err; + } + err = board_gpio_config_input(CONFIG_TQ_NTC_ADC_PIN, false, "ntc_adc"); + if (err != ESP_OK) { + return err; + } + + ESP_LOGI(TAG, + "board gpio mapped: hold=%d pwr_key=%d key_print=%d boost_mode=%d led_r=%d led_g=%d spi(mosi=%d,miso=%d,clk=%d) screen(dc=%d,cs=%d,bk=%d) i2c(sda=%d,scl=%d) i2s(mclk=%d,bclk=%d,ws=%d,di=%d,do=%d) pa=%d", + CONFIG_TQ_POWER_HOLD_PIN, + CONFIG_TQ_POWER_KEY_PIN, + CONFIG_TQ_KEY_PRINT_PIN, +#if CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT + 1, +#else + 0, +#endif + CONFIG_TQ_LED_R_PIN, + CONFIG_TQ_LED_G_PIN, + CONFIG_TQ_SPI_SHARED_MOSI_PIN, + CONFIG_TQ_PRINTER_MISO_PIN, + CONFIG_TQ_SPI_SHARED_CLK_PIN, + CONFIG_TQ_SCREEN_DC_PIN, + CONFIG_TQ_SCREEN_CS_PIN, + CONFIG_TQ_SCREEN_BACKLIGHT_PIN, + CONFIG_TQ_VOICE_I2C_SDA_PIN, + CONFIG_TQ_VOICE_I2C_SCL_PIN, + CONFIG_TQ_VOICE_I2S_MCLK_PIN, + CONFIG_TQ_VOICE_I2S_BCLK_PIN, + CONFIG_TQ_VOICE_I2S_WS_PIN, + CONFIG_TQ_VOICE_I2S_DIN_PIN, + CONFIG_TQ_VOICE_I2S_DOUT_PIN, + CONFIG_TQ_VOICE_CODEC_PA_PIN); + + s_board_gpio_initialized = true; + return ESP_OK; +} + esp_err_t platform_bootstrap_init(void) { if (s_initialized) { return ESP_OK; } + ESP_ERROR_CHECK(platform_board_gpio_init()); + esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); @@ -49,6 +224,21 @@ esp_err_t platform_bootstrap_init(void) { } #endif +#if CONFIG_TQ_SCREEN_ENABLE + err = platform_display_init(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "st7789 init failed: %s", esp_err_to_name(err)); + } +#if CONFIG_TQ_SCREEN_TEST_PATTERN_ON_BOOT + else { + err = platform_display_show_test_pattern(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "st7789 test pattern failed: %s", esp_err_to_name(err)); + } + } +#endif +#endif + s_initialized = true; return ESP_OK; } diff --git a/docs/gpio-map.md b/docs/gpio-map.md new file mode 100644 index 0000000..d36bbe3 --- /dev/null +++ b/docs/gpio-map.md @@ -0,0 +1,65 @@ +# ESP32-S3 GPIO Mapping + +本文件记录当前板级 GPIO 分配,来源于硬件对接表(2026-02-26)。 + +## GPIO Table + +| Board Pin | GPIO | Signal | Direction | Notes | +| --- | --- | --- | --- | --- | +| 1 | GND | GND | - | Ground | +| 2 | 3V3 | 3V3 | - | 3.3V Power | +| 3 | EN/RST | EN | Input | Chip enable/reset | +| 4 | IO4 | PRINT_STB5_6 | Output | Printer strobe 5/6 | +| 5 | IO5 | PRINT_STB3_4 | Output | Printer strobe 3/4 | +| 6 | IO6 | ES8311_I2C_SDA | Bidirectional | Voice codec I2C SDA | +| 7 | IO7 | ES8311_I2C_SCL | Output | Voice codec I2C SCL | +| 8 | IO15 | BATTERY_ADC | Input | Battery voltage ADC detect | +| 9 | IO16 | PRINT_STB1_2 | Output | Printer strobe 1/2 | +| 10 | IO17 | PRINT_PAPER | Input | Paper detect | +| 11 | IO18 | NTC_ADC | Input | NTC ADC | +| 12 | IO8 | PRINT_OUTA_P | Output | Printer OUTA+ | +| 13 | IO19 | PRINT_OUTA_N | Output | Printer OUTA- | +| 14 | IO20 | ES8311_PA | Output | PA control | +| 15 | IO3 | PRINT_OUTB_P | Output | Printer OUTB+ | +| 16 | IO46 | PRINT_OUTB_N | Output | Printer OUTB- | +| 17 | IO9 | SPI_SHARED_MOSI | Output | Printer/Screen shared MOSI | +| 18 | IO10 | PRINTER_MISO | Input | Printer MISO | +| 19 | IO11 | SPI_SHARED_CLK | Output | Printer/Screen shared CLK | +| 20 | IO12 | SCREEN_DC | Output | ST7789 DC | +| 21 | IO13 | SCREEN_CS1 | Output | ST7789 CS | +| 22 | IO14 | MIC_KEY | Input | Mic key (can ADC-mux 2 keys if needed) | +| 23 | IO21 | LED_G | Output | Green LED | +| 24 | IO47 | POWER_KEY | Input | Hold low for 2s to request power-off logic | +| 25 | IO48 | SCREEN_BACKLIGHT | Output | High level turns backlight on | +| 26 | IO45 | PRINT_LAT | Output | Printer LAT | +| 27 | IO0 | KEY_PRINT / BOOST_CTRL | Input/Output | Print confirm key or 7.2V boost control | +| 28 | IO35 | N/A | - | Not available | +| 29 | IO36 | N/A | - | Not available | +| 30 | IO37 | N/A | - | Not available | +| 31 | IO38 | I2S_MCLK | Output | Voice I2S MCLK | +| 32 | IO39 | I2S_BCLK | Output | Voice I2S BCLK | +| 33 | IO40 | I2S_DI | Input | Voice I2S data in | +| 34 | IO41 | I2S_WS | Output | Voice I2S WS | +| 35 | IO42 | I2S_DO | Output | Voice I2S data out | +| 36 | TXD0 | UART0_TX | Output | Serial log/program | +| 37 | RXD0 | UART0_RX | Input | Serial log/program | +| 38 | IO2 | LED_R | Output | Red LED | +| 39 | IO1 | POWER_HOLD | Output | Must drive high at boot to keep board powered | +| 40 | GND | GND | - | Ground | +| 41 | GND | GND | - | Ground | + +## Firmware Mapping Notes + +- `main/Kconfig.projbuild` contains board pin configs prefixed with `TQ_*`. +- `components/platform/src/platform_bootstrap.c` initializes critical GPIO on startup: + - `POWER_HOLD` asserted high early. + - `SCREEN_DC/SCREEN_CS/SCREEN_BACKLIGHT` default states. + - `KEY_PRINT` defaults to boost-enable output mode (`CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT=y`). + - key/sensor inputs (`POWER_KEY`, `MIC_KEY`, `PRINT_PAPER`, ADC inputs) default directions. +- `components/platform/src/display_st7789.c` provides ST7789 driver integration: + - SPI bus (`MOSI=IO9`, `CLK=IO11`) + `esp_lcd_new_panel_st7789`. + - Optional boot test pattern via `CONFIG_TQ_SCREEN_TEST_PATTERN_ON_BOOT`. +- Voice codec and I2S pin defaults are aligned to this table: + - I2C: `SDA=6`, `SCL=7` + - I2S: `MCLK=38`, `BCLK=39`, `WS=41`, `DIN=40`, `DOUT=42` + - PA: `IO20` diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index ad64962..6bc4386 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -181,12 +181,12 @@ config TQ_VOICE_I2C_PORT config TQ_VOICE_I2C_SDA_PIN int "Voice I2C SDA GPIO" range 0 48 - default 4 + default 6 config TQ_VOICE_I2C_SCL_PIN int "Voice I2C SCL GPIO" range 0 48 - default 5 + default 7 config TQ_VOICE_CODEC_ADDR int "ES8311 I2C address" @@ -201,12 +201,12 @@ config TQ_VOICE_I2S_PORT config TQ_VOICE_I2S_MCLK_PIN int "Voice I2S MCLK GPIO" range -1 48 - default 39 + default 38 config TQ_VOICE_I2S_BCLK_PIN int "Voice I2S BCLK GPIO" range 0 48 - default 40 + default 39 config TQ_VOICE_I2S_WS_PIN int "Voice I2S WS GPIO" @@ -216,18 +216,233 @@ config TQ_VOICE_I2S_WS_PIN config TQ_VOICE_I2S_DOUT_PIN int "Voice I2S DOUT GPIO" range 0 48 - default 38 + default 42 config TQ_VOICE_I2S_DIN_PIN int "Voice I2S DIN GPIO" range 0 48 - default 42 + default 40 config TQ_VOICE_CODEC_PA_PIN int "ES8311 PA control GPIO" range -1 48 + default 20 + +menu "Board GPIO Mapping (ESP32-S3)" + +config TQ_POWER_HOLD_PIN + int "Power hold GPIO (drive high to keep power)" + range -1 48 + default 1 + +config TQ_POWER_KEY_PIN + int "Power key GPIO" + range -1 48 + default 47 + +config TQ_LED_R_PIN + int "LED_R GPIO" + range -1 48 + default 2 + +config TQ_LED_G_PIN + int "LED_G GPIO" + range -1 48 + default 21 + +config TQ_KEY_PRINT_PIN + int "Print confirm key / 7.2V boost control GPIO" + range -1 48 + default 0 + +config TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT + bool "Use KEY_PRINT pin as 7.2V boost enable on boot" + default y + +config TQ_KEY_PRINT_BOOST_ACTIVE_HIGH + bool "KEY_PRINT boost enable level is high" + default y + depends on TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT + +config TQ_MIC_KEY_PIN + int "Mic key GPIO" + range -1 48 + default 14 + +config TQ_BATTERY_ADC_PIN + int "Battery ADC detect GPIO" + range -1 48 + default 15 + +config TQ_NTC_ADC_PIN + int "NTC ADC GPIO" + range -1 48 + default 18 + +config TQ_PRINT_STB_56_PIN + int "Printer STB5_6 GPIO" + range -1 48 + default 4 + +config TQ_PRINT_STB_34_PIN + int "Printer STB3_4 GPIO" + range -1 48 + default 5 + +config TQ_PRINT_STB_12_PIN + int "Printer STB1_2 GPIO" + range -1 48 + default 16 + +config TQ_PRINT_PAPER_PIN + int "Printer paper detect GPIO" + range -1 48 + default 17 + +config TQ_PRINT_OUTA_P_PIN + int "Printer OUTA+ GPIO" + range -1 48 + default 8 + +config TQ_PRINT_OUTA_N_PIN + int "Printer OUTA- GPIO" + range -1 48 + default 19 + +config TQ_PRINT_OUTB_P_PIN + int "Printer OUTB+ GPIO" + range -1 48 + default 3 + +config TQ_PRINT_OUTB_N_PIN + int "Printer OUTB- GPIO" + range -1 48 + default 46 + +config TQ_PRINT_LAT_PIN + int "Printer LAT GPIO" + range -1 48 + default 45 + +config TQ_SPI_SHARED_MOSI_PIN + int "Printer/Screen shared MOSI GPIO" + range -1 48 + default 9 + +config TQ_PRINTER_MISO_PIN + int "Printer MISO GPIO" + range -1 48 + default 10 + +config TQ_SPI_SHARED_CLK_PIN + int "Printer/Screen shared CLK GPIO" + range -1 48 + default 11 + +config TQ_SCREEN_DC_PIN + int "Screen DC GPIO" + range -1 48 + default 12 + +config TQ_SCREEN_CS_PIN + int "Screen CS GPIO" + range -1 48 + default 13 + +config TQ_SCREEN_BACKLIGHT_PIN + int "Screen backlight GPIO (active high)" + range -1 48 default 48 +endmenu + +menu "ST7789 Screen" + +config TQ_SCREEN_ENABLE + bool "Enable ST7789 screen driver" + default y + depends on SOC_GPSPI_SUPPORTED + +config TQ_SCREEN_TEST_PATTERN_ON_BOOT + bool "Show ST7789 test pattern at boot" + default y + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_PIXEL_CLOCK_HZ + int "ST7789 SPI pixel clock (Hz)" + range 1000000 80000000 + default 10000000 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_H_RES + int "ST7789 horizontal resolution" + range 64 480 + default 240 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_V_RES + int "ST7789 vertical resolution" + range 64 480 + default 240 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_DRAW_LINES + int "ST7789 draw chunk lines" + range 1 120 + default 20 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_SPI_MODE + int "ST7789 SPI mode (0-3)" + range 0 3 + default 0 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_COLOR_ORDER_BGR + bool "ST7789 color order is BGR" + default y + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH + bool "ST7789 backlight active-high" + default y + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_MIRROR_X + bool "ST7789 mirror X axis" + default n + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_MIRROR_Y + bool "ST7789 mirror Y axis" + default n + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_SWAP_XY + bool "ST7789 swap X/Y axis" + default n + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_X_GAP + int "ST7789 X gap" + range 0 480 + default 0 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_Y_GAP + int "ST7789 Y gap" + range 0 480 + default 0 + depends on TQ_SCREEN_ENABLE + +config TQ_SCREEN_RESET_PIN + int "ST7789 reset GPIO (-1 if not connected)" + range -1 48 + default -1 + depends on TQ_SCREEN_ENABLE + +endmenu + config TQ_VOICE_CODEC_PA_PIN_REVERSED bool "ES8311 PA enable level is active-low" default n diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 9bb93d3..40c0650 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -52,15 +52,50 @@ CONFIG_TQ_VOICE_OPUS_FRAME_MS=100 CONFIG_TQ_VOICE_OPUS_BITRATE_KBPS=32 CONFIG_TQ_VOICE_HEARTBEAT_SEC=50 CONFIG_TQ_VOICE_I2C_PORT=1 -CONFIG_TQ_VOICE_I2C_SDA_PIN=4 -CONFIG_TQ_VOICE_I2C_SCL_PIN=5 +CONFIG_TQ_VOICE_I2C_SDA_PIN=6 +CONFIG_TQ_VOICE_I2C_SCL_PIN=7 CONFIG_TQ_VOICE_CODEC_ADDR=48 CONFIG_TQ_VOICE_I2S_PORT=0 -CONFIG_TQ_VOICE_I2S_MCLK_PIN=39 -CONFIG_TQ_VOICE_I2S_BCLK_PIN=40 +CONFIG_TQ_VOICE_I2S_MCLK_PIN=38 +CONFIG_TQ_VOICE_I2S_BCLK_PIN=39 CONFIG_TQ_VOICE_I2S_WS_PIN=41 -CONFIG_TQ_VOICE_I2S_DOUT_PIN=38 -CONFIG_TQ_VOICE_I2S_DIN_PIN=42 -CONFIG_TQ_VOICE_CODEC_PA_PIN=48 +CONFIG_TQ_VOICE_I2S_DOUT_PIN=42 +CONFIG_TQ_VOICE_I2S_DIN_PIN=40 +CONFIG_TQ_VOICE_CODEC_PA_PIN=20 CONFIG_TQ_VOICE_CODEC_OUT_VOL=70 CONFIG_TQ_VOICE_CODEC_MIC_GAIN_DB=24 +CONFIG_TQ_POWER_HOLD_PIN=1 +CONFIG_TQ_POWER_KEY_PIN=47 +CONFIG_TQ_LED_R_PIN=2 +CONFIG_TQ_LED_G_PIN=21 +CONFIG_TQ_KEY_PRINT_PIN=0 +CONFIG_TQ_KEY_PRINT_BOOST_ENABLE_ON_BOOT=y +CONFIG_TQ_KEY_PRINT_BOOST_ACTIVE_HIGH=y +CONFIG_TQ_MIC_KEY_PIN=14 +CONFIG_TQ_BATTERY_ADC_PIN=15 +CONFIG_TQ_NTC_ADC_PIN=18 +CONFIG_TQ_PRINT_STB_56_PIN=4 +CONFIG_TQ_PRINT_STB_34_PIN=5 +CONFIG_TQ_PRINT_STB_12_PIN=16 +CONFIG_TQ_PRINT_PAPER_PIN=17 +CONFIG_TQ_PRINT_OUTA_P_PIN=8 +CONFIG_TQ_PRINT_OUTA_N_PIN=19 +CONFIG_TQ_PRINT_OUTB_P_PIN=3 +CONFIG_TQ_PRINT_OUTB_N_PIN=46 +CONFIG_TQ_PRINT_LAT_PIN=45 +CONFIG_TQ_SPI_SHARED_MOSI_PIN=9 +CONFIG_TQ_PRINTER_MISO_PIN=10 +CONFIG_TQ_SPI_SHARED_CLK_PIN=11 +CONFIG_TQ_SCREEN_DC_PIN=12 +CONFIG_TQ_SCREEN_CS_PIN=13 +CONFIG_TQ_SCREEN_BACKLIGHT_PIN=48 +CONFIG_TQ_SCREEN_ENABLE=y +CONFIG_TQ_SCREEN_TEST_PATTERN_ON_BOOT=y +CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ=10000000 +CONFIG_TQ_SCREEN_H_RES=240 +CONFIG_TQ_SCREEN_V_RES=240 +CONFIG_TQ_SCREEN_DRAW_LINES=20 +CONFIG_TQ_SCREEN_SPI_MODE=0 +CONFIG_TQ_SCREEN_COLOR_ORDER_BGR=y +CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH=y +CONFIG_TQ_SCREEN_RESET_PIN=-1