Files
trail-mate/platform/esp/idf_components/m5stack_tab5/m5stack_tab5.c
T
vicliuandGitHub 54cf0301e2 Gat562 mesh evb pro (#13)
* feat(gat562): wire nrf52 board runtime and bluetooth stack

* refactor(chat): align shared BLE cores and nrf meshtastic routing

- extract Meshtastic and MeshCore phone/business flows into shared core_chat BLE cores with owner hook seams for ESP and nRF backends

- replace nrf-specific lite adapters with board/platform adapters that consume shared cores and shared storage contracts

- extend nrf Meshtastic radio path with packet history dedupe, flooding rebroadcast, route discovery/traceroute responses, routing ACK/error handling, observed relay learning, retransmit queueing, and next-hop persistence

- persist nrf device identity and node metadata more explicitly, including next-hop state and node-id initialization support

- move nrf chat/contact/blob storage further toward InternalFS-backed persistence and update contact/node store behavior accordingly

- wire gat562 protocol/app facade pieces to the concrete nrf stores/adapters and add supporting app time-sync hooks used by platform runtimes

- add owner-hook split files and planning/status docs to document the ongoing alignment with the reference meshtastic-firmware multi-platform structure

* Add NRF Fusion Pixel font and fix Meshtastic RX

Integrate Fusion Pixel 8px as the NRF mono UI font stack and switch the mono runtime to a dedicated MonoFont/TextRenderer path that can render ASCII, CJK, and symbols from one asset set.

Add the generated NRF font assets and generator script, including the compact font format with per-glyph advance support, and fix the BDF bitmap parsing bug that previously produced blank ASCII glyphs on device.

Move the LVGL Noto CJK font asset out of shared modules into the ESP-specific platform tree and update the related build/config references so the ESP-only font boundary is explicit and NRF does not compile the LVGL asset by accident.

Update the mono UI runtime to use the new renderer, normalize major menu and title labels to uppercase, and wire the GAT562 UI runtime to the Fusion Pixel font.

Fix the core mesh adapter router so Meshtastic and MeshCore backends can coexist without overwriting each other, and ensure active protocol selection is applied before mesh config changes are pushed into the runtime.

Improve GAT562 runtime diagnostics by mirroring debug output to both Serial and Serial2, logging startup radio configuration, and logging raw RX packets with RSSI/SNR so radio-path issues are visible during bring-up.

Relax SX1262 receive polling so it no longer hard-gates RX handling on DIO1 state alone, avoiding a failure mode where the board appeared alive but never delivered packets into the receive path.

Align the NRF Meshtastic adapter with the working ESP behavior for channel handling: use the default expanded public PSK for an unconfigured primary channel, compute channel hashes from the active preset name or Custom label instead of a hard-coded Primary string, and apply the same logic to self-announcement/NodeInfo packets.

Add protocol-layer Meshtastic RX diagnostics for parse, decrypt, decode, text queueing, app-data queueing, unknown-channel, and dedup events to make packet flow visible while validating interoperability.

Fix early duplicate handling for relayed packets so the first valid packet is still delivered into text processing while later relays are correctly suppressed, which restores Broadcast conversation creation and message visibility in the CHATS page.

* feat(gat562): align nrf ui and meshtastic behavior

* Refine mono UI GPS pages and board diagnostics

* refactor: align board runtimes, GAT562 app shell, and BLE integrations

- migrate ESP board-specific implementations and runtime adapters into boards/*
- continue GAT562/Tab5/UI runtime alignment across app and board layers
- sync BLE and runtime integrations, and apply repository clang-format rules

* Refine gat562 mono UI and persist UI settings

* Fix settings persistence and GNSS UI behavior

* Improve mono UI paging and Meshtastic RX fallback

* Align nRF52 Meshtastic BLE and PKI behavior

* Refine mono UI node and chat interactions

* Fix deferred config persistence on gat562 BLE

* Refine mono node compass and GPS layouts

* Include node position in Meshtastic phone info

* Refactor shared board contracts out of ESP platform layer

* Persist node positions across restarts

* Ignore local compile commands database

* Sync README acknowledgements updates

* Apply CI formatting

* Fix board ownership drift and restore pager walkie support

* Add APRS asset, expand System controls, and harden GAT562 self-position persistence

Bundle the current workspace changes into a single checkpoint covering the in-progress shared UI, settings, and GAT562 runtime work.

What this includes:

- add the APRS asset source and image resource currently used by the shared UI asset set

- extend the shared System/settings flow so vibration and screen brightness can be surfaced through the device runtime and settings UI plumbing

- update the mono 128x64 runtime and the GAT562 platform bindings so the new settings controls are available in the GAT562 experience

- wire the ESP runtime/startup helpers needed to support the same device-runtime settings path on the ESP side

- persist the GAT562 self GPS position into the node store during core service updates so a device that already obtained a fix can retain its own last known position across reboot

- add an explicit node-store flush interface and implement it in both the nRF52 and ESP Meshtastic node-store backends so critical self-position updates can be forced to persistent storage immediately instead of waiting for the normal debounce window

- keep the existing debounced persistence behavior for ordinary node-store churn while allowing targeted immediate flushes for data that would otherwise be easy to lose during restart or power loss

* chore: checkpoint current chat and contacts changes

* perf: reduce chat list and message handling latency

* Align Meshtastic phone sync and chat UI refresh

* Remove key verification UI and hide MC position exchange

* Add T-Deck Pro board bring-up and sync pending UI fixes

* Refactor GAT562 runtime and stabilize Meshtastic BLE

Refactor the GAT562 board runtime by extracting dedicated GPS and input runtime components and routing config application through a focused runtime apply service.

Narrow the nRF52 BLE manager dependencies, rework the Meshtastic BLE transport to better match the Android client handshake, and harden the FromRadio/FromNum flow with improved staging, read pacing, diagnostics, and stack-safe config snapshot generation.

Also repair repository text encoding issues in docs and UI comments, and add Meshtastic BLE timing documentation to capture the expected Android/firmware interaction model for future debugging.

* fix: gat562 settings

* Define LoRa tx power caps per board

* Refine mono conversation bubble focus

* Polish mono conversation and message info layout

* Harden Meshtastic BLE flow and cross-target builds

Adjust the Meshtastic BLE transport timing on nRF52, retain pending FROMNUM notifications, and defer local admin/self replies to reduce request/response races during Android settings flows.

Also make the shared chat/UI code compile across non-nRF targets by gating RTOS and Arduino-only helpers and by updating the team page layout to the current page profile fields.

Note: this does not resolve the gat562 Meshtastic Android settings progress bar issue yet; the BLE/admin response path is improved and instrumented, but the root cause of the stuck loading state remains unresolved.

* Fix GAT562 mono channel slot UI and screen timeout

* Revert Meshtastic BLE hardening changes

Revert b9e20b2662 because it causes app-side configuration reads to hang on GAT562.

This restores the previous Meshtastic BLE timing/notification behavior and also drops the cross-target compatibility adjustments that came with that commit.

* Refactor nrf52 fs helpers and show flash free space

* fix sync node info

* Harden Meshtastic BLE receive path

* Fix Meshtastic nodeinfo persistence

* Persist NodeInfo position updates

* Format files to match CI style

* Prepare 0.1.14-alpha docs and CI

* Fix watch chat build and CI formatting

* Format sources for CI clang-format-14
2026-04-13 17:33:21 +08:00

2193 lines
71 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_private/esp_gpio_reserve.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C"
{
#endif
void trail_mate_idf_note_user_activity(void);
#ifdef __cplusplus
}
#endif
#include "bsp/display.h"
#include "bsp/m5stack_tab5.h"
#include "bsp/touch.h"
#include "bsp_err_check.h"
#include "esp_codec_dev_defaults.h"
#include "esp_lcd_ili9881c.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_st7123.h"
#include "esp_lcd_st7703.h"
#include "esp_lcd_touch_gt911.h"
#include "esp_lcd_touch_st7123.h"
#include "esp_ldo_regulator.h"
#include "esp_spiffs.h"
#include "esp_timer.h"
#include "esp_vfs_fat.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "sd_pwr_ctrl_by_on_chip_ldo.h"
#include "sdmmc_cmd.h"
#include "usb/usb_host.h"
#include <inttypes.h>
static const char* TAG = "M5STACK_TAB5";
static uint32_t sys_i2c_generation = 0;
static bool sys_i2c_reinit_in_progress = false;
static SemaphoreHandle_t sys_i2c_reinit_mutex = NULL;
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
static lv_indev_t* disp_indev = NULL;
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
// Global uSD card handler
sdmmc_card_t* bsp_sdcard = NULL;
// USB Host Library task
static TaskHandle_t usb_host_task;
// sys i2c
static bool i2c_initialized = false;
static i2c_master_bus_handle_t i2c_handle = NULL;
// SYS I2C bus mutex to protect against concurrent access from multiple devices
// This is critical because ST7123 touch screen, Module GNSS sensors, RTC, built-in IMU, etc.
// all share the same SYS I2C bus (GPIO 31/32)
static SemaphoreHandle_t sys_i2c_mutex = NULL;
// ext i2c
static bool ext_i2c_initialized = false;
static i2c_master_bus_handle_t ext_i2c_bus_handle = NULL;
// grove i2c
static bool grove_i2c_initialized = false;
static i2c_master_bus_handle_t grove_i2c_bus_handle = NULL;
// i2s
static i2s_chan_handle_t i2s_tx_chan = NULL;
static i2s_chan_handle_t i2s_rx_chan = NULL;
static const audio_codec_data_if_t* i2s_data_if = NULL; /* Codec data interface */
static bool s_lcd_backlight_initialized = false;
//==================================================================================
// camera 设置输出时钟
//==================================================================================
esp_err_t bsp_cam_osc_init(void)
{
ledc_timer_config_t timer_conf = {0};
timer_conf.duty_resolution = LEDC_TIMER_1_BIT;
timer_conf.freq_hz = 24000000; // <<<< change this to the frequency you want
timer_conf.speed_mode = LEDC_LOW_SPEED_MODE;
timer_conf.deconfigure = false;
timer_conf.clk_cfg = LEDC_AUTO_CLK;
timer_conf.timer_num = LEDC_TIMER_0;
esp_err_t err = ledc_timer_config(&timer_conf);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "ledc_timer_config failed for freq %d, rc=%x", 24000000, err);
}
ledc_channel_config_t ch_conf = {0};
ch_conf.gpio_num = 36; // æ‘„åƒå¤´æ—¶é’Ÿè¾“å…?
ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
ch_conf.channel = LEDC_CHANNEL_0;
ch_conf.intr_type = LEDC_INTR_DISABLE;
ch_conf.timer_sel = LEDC_TIMER_0;
ch_conf.duty = 1;
ch_conf.hpoint = 0;
err = ledc_channel_config(&ch_conf);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err);
}
return ESP_OK;
}
//==================================================================================
// i2c
//==================================================================================
esp_err_t bsp_i2c_init(void)
{
/* I2C was initialized before */
if (i2c_initialized)
{
return ESP_OK;
}
if (sys_i2c_reinit_mutex == NULL)
{
sys_i2c_reinit_mutex = xSemaphoreCreateMutex();
if (sys_i2c_reinit_mutex == NULL)
{
ESP_LOGE(TAG, "Failed to create SYS I2C reinit mutex");
return ESP_ERR_NO_MEM;
}
}
if (xSemaphoreTake(sys_i2c_reinit_mutex, pdMS_TO_TICKS(2000)) != pdTRUE)
{
return ESP_ERR_TIMEOUT;
}
sys_i2c_reinit_in_progress = true;
// Create mutex for SYS I2C bus protection
// This mutex protects against concurrent access from multiple devices sharing the same I2C bus
// (ST7123 touch screen, Module GNSS sensors, RTC, built-in IMU, etc.)
if (sys_i2c_mutex == NULL)
{
sys_i2c_mutex = xSemaphoreCreateMutex();
if (sys_i2c_mutex == NULL)
{
ESP_LOGE(TAG, "Failed to create SYS I2C mutex");
return ESP_ERR_NO_MEM;
}
}
i2c_master_bus_config_t i2c_bus_conf = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.sda_io_num = BSP_I2C_SDA,
.scl_io_num = BSP_I2C_SCL,
.i2c_port = BSP_I2C_NUM,
.flags.enable_internal_pullup = false, // Disable internal pullup - hardware has external pullup resistors
};
BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle));
sys_i2c_generation++;
// Suppress I2C driver error logs for normal operation
// I2C transaction failures can occur during normal operation (e.g., when touch screen has no data)
// Set log level to NONE to suppress ALL I2C logs including NACK/timeout errors
// These errors are expected during normal operation when devices are busy or have no data
// Touch panel I2C errors are handled by the touch driver with rate-limiting
esp_log_level_set("i2c.master", ESP_LOG_NONE);
esp_log_level_set("lcd_panel.io.i2c", ESP_LOG_NONE);
i2c_initialized = true;
sys_i2c_reinit_in_progress = false;
xSemaphoreGive(sys_i2c_reinit_mutex);
return ESP_OK;
}
esp_err_t bsp_i2c_deinit(void)
{
if (sys_i2c_reinit_mutex != NULL)
{
if (xSemaphoreTake(sys_i2c_reinit_mutex, pdMS_TO_TICKS(2000)) != pdTRUE)
{
return ESP_ERR_TIMEOUT;
}
sys_i2c_reinit_in_progress = true;
}
BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle));
i2c_initialized = false;
sys_i2c_generation++;
sys_i2c_reinit_in_progress = false;
if (sys_i2c_reinit_mutex != NULL)
{
xSemaphoreGive(sys_i2c_reinit_mutex);
}
return ESP_OK;
}
i2c_master_bus_handle_t bsp_i2c_get_handle(void)
{
// CRITICAL: Ensure I2C is initialized before returning handle
// This prevents crashes when handle is NULL
if (!i2c_initialized || i2c_handle == NULL)
{
ESP_LOGE(TAG, "I2C bus not initialized, initializing now");
if (bsp_i2c_init() != ESP_OK)
{
ESP_LOGE(TAG, "Failed to initialize I2C bus");
return NULL;
}
}
static uint32_t last_log_ms = 0;
uint32_t now_ms = esp_timer_get_time() / 1000;
if (now_ms - last_log_ms >= 5000)
{
ESP_LOGI(TAG, "bsp_i2c_get_handle: handle=%p gen=%" PRIu32 " reinit=%d",
(void*)i2c_handle, sys_i2c_generation, sys_i2c_reinit_in_progress);
last_log_ms = now_ms;
}
return i2c_handle;
}
uint32_t bsp_i2c_get_generation(void)
{
return sys_i2c_generation;
}
bool bsp_i2c_is_reinit_in_progress(void)
{
return sys_i2c_reinit_in_progress;
}
bool bsp_i2c_lock(uint32_t timeout_ms)
{
if (sys_i2c_mutex == NULL)
{
// Mutex not initialized yet, try to initialize I2C first
if (bsp_i2c_init() != ESP_OK)
{
return false;
}
}
TickType_t timeout_ticks = (timeout_ms == 0) ? 0 : pdMS_TO_TICKS(timeout_ms);
if (timeout_ms == UINT32_MAX)
{
timeout_ticks = portMAX_DELAY;
}
return xSemaphoreTake(sys_i2c_mutex, timeout_ticks) == pdTRUE;
}
void bsp_i2c_unlock(void)
{
if (sys_i2c_mutex != NULL)
{
xSemaphoreGive(sys_i2c_mutex);
}
}
esp_err_t bsp_ext_i2c_init(void)
{
if (ext_i2c_initialized)
{
return ESP_OK;
}
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = BSP_EXT_I2C_NUM,
.scl_io_num = BSP_EXT_I2C_SCL,
.sda_io_num = BSP_EXT_I2C_SDA,
.flags.enable_internal_pullup = false, // Disable internal pullup - hardware has external pullup resistors
};
i2c_new_master_bus(&i2c_mst_config, &ext_i2c_bus_handle);
ext_i2c_initialized = true;
return ESP_OK;
}
esp_err_t bsp_ext_i2c_deinit(void)
{
ext_i2c_initialized = false;
return i2c_del_master_bus(ext_i2c_bus_handle);
}
i2c_master_bus_handle_t bsp_ext_i2c_get_handle(void)
{
return ext_i2c_bus_handle;
}
esp_err_t bsp_grove_i2c_init(void)
{
if (grove_i2c_initialized)
{
return ESP_OK;
}
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = BSP_EXT_I2C_NUM,
.scl_io_num = 54, // BSP_EXT_I2C_SCL,
.sda_io_num = 53, // BSP_EXT_I2C_SDA,
.flags.enable_internal_pullup = false, // Disable internal pullup - hardware has external pullup resistors
};
i2c_new_master_bus(&i2c_mst_config, &grove_i2c_bus_handle);
grove_i2c_initialized = true;
return ESP_OK;
}
esp_err_t bsp_grove_i2c_deinit(void)
{
grove_i2c_initialized = false;
return i2c_del_master_bus(grove_i2c_bus_handle);
}
i2c_master_bus_handle_t bsp_grove_i2c_get_handle(void)
{
return grove_i2c_bus_handle;
}
esp_err_t bsp_i2c_scan()
{
esp_err_t ret;
uint8_t address;
// Lock SYS I2C bus before scanning to prevent conflicts
if (!bsp_i2c_lock(1000))
{ // 1 second timeout
ESP_LOGE(TAG, "Failed to lock I2C bus for scan");
return ESP_ERR_TIMEOUT;
}
printf("scan i2c device\n");
printf("\n 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16)
{
printf("%02x: ", i);
for (int j = 0; j < 16; j++)
{
fflush(stdout);
address = i + j;
ret = i2c_master_probe(i2c_handle, address, 50);
if (ret == ESP_OK)
{
printf("%02x ", address);
}
else if (ret == ESP_ERR_TIMEOUT)
{
printf("UU ");
}
else
{
printf("-- ");
}
}
printf("\r\n");
}
printf("\nscan i2c device finished\n");
bsp_i2c_unlock();
return ESP_OK;
}
//==================================================================================
// I/O Exapnder PI4IOE5V6416
//==================================================================================
#define I2C_DEV_ADDR_PI4IOE1 0x43 // addr pin low
#define I2C_DEV_ADDR_PI4IOE2 0x44 // addr pin high
#define I2C_MASTER_TIMEOUT_MS 50
static i2c_master_dev_handle_t i2c_dev_handle_pi4ioe1;
static i2c_master_dev_handle_t i2c_dev_handle_pi4ioe2;
// PI4IO registers
#define PI4IO_REG_CHIP_RESET 0x01
#define PI4IO_REG_IO_DIR 0x03
#define PI4IO_REG_OUT_SET 0x05
#define PI4IO_REG_OUT_H_IM 0x07
#define PI4IO_REG_IN_DEF_STA 0x09
#define PI4IO_REG_PULL_EN 0x0B
#define PI4IO_REG_PULL_SEL 0x0D
#define PI4IO_REG_IN_STA 0x0F
#define PI4IO_REG_INT_MASK 0x11
#define PI4IO_REG_IRQ_STA 0x13
#define setbit(x, y) x |= (0x01 << y)
#define clrbit(x, y) x &= ~(0x01 << y)
void bsp_io_expander_pi4ioe_init(i2c_master_bus_handle_t bus_handle)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
/* */
i2c_device_config_t dev_cfg1 = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = I2C_DEV_ADDR_PI4IOE1,
.scl_speed_hz = 400000,
};
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg1, &i2c_dev_handle_pi4ioe1));
// Lock SYS I2C bus for initialization
if (!bsp_i2c_lock(1000))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for PI4IOE1 init");
return;
}
write_buf[0] = PI4IO_REG_CHIP_RESET;
write_buf[1] = 0xFF;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_CHIP_RESET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe1, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_IO_DIR;
write_buf[1] = 0b01111111;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS); // 0: input 1: output
write_buf[0] = PI4IO_REG_OUT_H_IM;
write_buf[1] = 0b00000000;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // 使用到的引脚关闭 High-Impedance
write_buf[0] = PI4IO_REG_PULL_SEL;
write_buf[1] = 0b01111111;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // pull up/down select, 0 down, 1 up
write_buf[0] = PI4IO_REG_PULL_EN;
write_buf[1] = 0b01111111;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // P7 中断使能 0 enable, 1 disable
/* Output Port Register P1(SPK_EN), P2(EXT5V_EN), P4(LCD_RST), P5(TP_RST), P6(CAM)RST 输出高电�*/
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = 0b01110110;
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
/* */
i2c_device_config_t dev_cfg2 = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
.device_address = I2C_DEV_ADDR_PI4IOE2,
.scl_speed_hz = 400000,
};
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg2, &i2c_dev_handle_pi4ioe2));
write_buf[0] = PI4IO_REG_CHIP_RESET;
write_buf[1] = 0xFF;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_CHIP_RESET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_IO_DIR;
write_buf[1] = 0b10111001;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS); // 0: input 1: output
write_buf[0] = PI4IO_REG_OUT_H_IM;
write_buf[1] = 0b00000110;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // 使用到的引脚关闭 High-Impedance
write_buf[0] = PI4IO_REG_PULL_SEL;
write_buf[1] = 0b10111001;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // pull up/down select, 0 down, 1 up
write_buf[0] = PI4IO_REG_PULL_EN;
write_buf[1] = 0b11111001;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // pull up/down enable, 0 disable, 1 enable
write_buf[0] = PI4IO_REG_IN_DEF_STA;
write_buf[1] = 0b01000000;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS); // P6 默认高电�
write_buf[0] = PI4IO_REG_INT_MASK;
write_buf[1] = 0b10111111;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2,
I2C_MASTER_TIMEOUT_MS); // P6 中断使能 0 enable, 1 disable
/* Output Port Register P0(WLAN_PWR_EN), P3(USB5V_EN), P7(CHG_EN) 输出高电�*/
write_buf[0] = PI4IO_REG_OUT_SET;
// write_buf[1] = 0b10001001;
write_buf[1] = 0b00001001;
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_set_charge_qc_en(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_charge_qc_en");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
clrbit(write_buf[1], 5);
}
else
{
setbit(write_buf[1], 5);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_set_charge_en(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_charge_en");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
setbit(write_buf[1], 7);
}
else
{
clrbit(write_buf[1], 7);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_set_usb_5v_en(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_usb_5v_en");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
setbit(write_buf[1], 3);
}
else
{
clrbit(write_buf[1], 3);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_set_ext_5v_en(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE1
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_ext_5v_en");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe1, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
setbit(write_buf[1], 2);
}
else
{
clrbit(write_buf[1], 2);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_generate_poweroff_signal()
{
ESP_LOGW(TAG, "Generate poweroff signal!");
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(1000))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_generate_poweroff_signal");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[1] = read_buf[0];
// Try to generate poweroff signal 3 times to make sure it works :)
for (int i = 0; i < 3; i++)
{
setbit(write_buf[1], 4);
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
vTaskDelay(100 / portTICK_PERIOD_MS);
clrbit(write_buf[1], 4);
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
bsp_i2c_unlock();
}
bool bsp_headphone_detect()
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE1
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_headphone_detect");
return false;
}
write_buf[0] = PI4IO_REG_IN_STA;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe1, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
// printf("get %02x\n", read_buf[0]);
// Get bit 8
bool ret = false;
if (read_buf[0] & 0b10000000)
{
ret = true;
}
bsp_i2c_unlock();
return ret;
}
bool bsp_usb_c_detect()
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_usb_c_detect");
return false;
}
write_buf[0] = PI4IO_REG_IN_STA;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
// printf("get %02x\n", read_buf[0]);
// Get bit 6
bool ret = false;
if (read_buf[0] & 0b01000000)
{
ret = true;
}
bsp_i2c_unlock();
return ret;
}
void bsp_set_ext_antenna_enable(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE1
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_ext_antenna_enable");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe1, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
setbit(write_buf[1], 0);
}
else
{
clrbit(write_buf[1], 0);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
bsp_i2c_unlock();
}
void bsp_set_wifi_power_enable(bool en)
{
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
ESP_LOGI(TAG, "set_wifi_power_enable: %d", en);
// Lock SYS I2C bus before accessing PI4IOE2
if (!bsp_i2c_lock(100))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_set_wifi_power_enable");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
if (en)
{
setbit(write_buf[1], 0);
}
else
{
clrbit(write_buf[1], 0);
}
i2c_master_transmit(i2c_dev_handle_pi4ioe2, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe2, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
printf("0x%02X: %02x\n", PI4IO_REG_OUT_SET, read_buf[0]);
bsp_i2c_unlock();
}
void bsp_reset_tp()
{
ESP_LOGI(TAG, "reset tp");
ESP_LOGI(TAG, "reset gpio %d", GPIO_NUM_23);
gpio_reset_pin(GPIO_NUM_23);
uint8_t write_buf[2] = {0};
uint8_t read_buf[1] = {0};
// Lock SYS I2C bus before accessing PI4IOE1
if (!bsp_i2c_lock(1000))
{
ESP_LOGE(TAG, "Failed to lock I2C bus for bsp_reset_tp");
return;
}
write_buf[0] = PI4IO_REG_OUT_SET;
i2c_master_transmit_receive(i2c_dev_handle_pi4ioe1, write_buf, 1, read_buf, 1, I2C_MASTER_TIMEOUT_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
clrbit(write_buf[1], 4);
clrbit(write_buf[1], 5);
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
vTaskDelay(100 / portTICK_PERIOD_MS);
write_buf[0] = PI4IO_REG_OUT_SET;
write_buf[1] = read_buf[0];
setbit(write_buf[1], 4);
setbit(write_buf[1], 5);
i2c_master_transmit(i2c_dev_handle_pi4ioe1, write_buf, 2, I2C_MASTER_TIMEOUT_MS);
vTaskDelay(100 / portTICK_PERIOD_MS);
bsp_i2c_unlock();
}
//==================================================================================
// sd card
//==================================================================================
#define BSP_LDO_PROBE_SD_CHAN 4
#define BSP_LDO_PROBE_SD_VOLTAGE_MV 3300
#define SDMMC_BUS_WIDTH (4) // SDIO 4 线模�
#define GPIO_SDMMC_DET (GPIO_NUM_NC) // SDIO 塿£€æµ?
// M5Stack-Tab5-P4
#define GPIO_SDMMC_CLK (GPIO_NUM_43) // SDIO æ—¶é’Ÿ
#define GPIO_SDMMC_CMD (GPIO_NUM_44) // SDIO 命令
#define GPIO_SDMMC_D0 (GPIO_NUM_39) // SDIO æ•°æ® 0
#define GPIO_SDMMC_D1 (GPIO_NUM_40) // SDIO æ•°æ® 1
#define GPIO_SDMMC_D2 (GPIO_NUM_41) // SDIO æ•°æ® 2
#define GPIO_SDMMC_D3 (GPIO_NUM_42) // SDIO æ•°æ® 3
static sdmmc_card_t* card;
esp_err_t bsp_sdcard_init(char* mount_point, size_t max_files)
{
esp_err_t ret_val = ESP_OK;
if (NULL != card)
{
return ESP_ERR_INVALID_STATE;
}
/**
* @brief Use settings defined above to initialize SD card and mount FAT filesystem.
* Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
* Please check its source code and implement error recovery when developing
* production applications.
*
*/
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
host.slot = SDMMC_HOST_SLOT_0; //
// host.slot = SDMMC_HOST_SLOT_1; //
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
sd_pwr_ctrl_ldo_config_t ldo_config = {
.ldo_chan_id = BSP_LDO_PROBE_SD_CHAN, // `LDO_VO4` is used as the SDMMC IO power
};
static sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL;
if (pwr_ctrl_handle == NULL)
{
ret_val = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle);
if (ret_val != ESP_OK)
{
ESP_LOGE(TAG, "Failed to new an on-chip ldo power control driver");
return ret_val;
}
}
host.pwr_ctrl_handle = pwr_ctrl_handle;
/**
* @brief This initializes the slot without card detect (CD) and write protect (WP) signals.
* Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
*
*/
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.width = SDMMC_BUS_WIDTH;
slot_config.clk = GPIO_SDMMC_CLK;
slot_config.cmd = GPIO_SDMMC_CMD;
slot_config.d0 = GPIO_SDMMC_D0;
slot_config.d1 = GPIO_SDMMC_D1;
slot_config.d2 = GPIO_SDMMC_D2;
slot_config.d3 = GPIO_SDMMC_D3;
// slot_config.cd = GPIO_SDMMC_DET;
// slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
/**
* @brief Options for mounting the filesystem.
* If format_if_mount_failed is set to true, SD card will be partitioned and
* formatted in case when mounting fails.
*/
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false, .max_files = max_files, .allocation_unit_size = 16 * 1024};
ret_val = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
/* Check for SDMMC mount result. */
if (ret_val != ESP_OK)
{
if (ret_val == ESP_FAIL)
{
ESP_LOGE(TAG,
"Failed to mount filesystem. "
"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
}
else
{
ESP_LOGE(TAG,
"Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.",
esp_err_to_name(ret_val));
}
return ret_val;
}
/* Card has been initialized, print its properties. */
sdmmc_card_print_info(stdout, card);
return ret_val;
}
esp_err_t bsp_sdcard_deinit(char* mount_point)
{
if (mount_point == NULL)
{
return ESP_ERR_INVALID_STATE;
}
/* Unmount an SD card from the FAT filesystem and release resources acquired */
esp_err_t ret_val = esp_vfs_fat_sdcard_unmount(mount_point, card);
// ret_val = sd_pwr_ctrl_del_on_chip_ldo(card->host.pwr_ctrl_handle);
// if (ret_val != ESP_OK) {
// ESP_LOGE(TAG, "Failed to delete on-chip ldo power control driver");
// }
/* Make SD/MMC card information structure pointer NULL */
card = NULL;
return ret_val;
}
//==================================================================================
// spiffs
//==================================================================================
esp_err_t bsp_spiffs_mount(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT,
.partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL,
.max_files = CONFIG_BSP_SPIFFS_MAX_FILES,
#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL
.format_if_mount_failed = true,
#else
.format_if_mount_failed = false,
#endif
};
esp_err_t ret_val = esp_vfs_spiffs_register(&conf);
BSP_ERROR_CHECK_RETURN_ERR(ret_val);
size_t total = 0, used = 0;
ret_val = esp_spiffs_info(conf.partition_label, &total, &used);
if (ret_val != ESP_OK)
{
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val));
}
else
{
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
}
return ret_val;
}
esp_err_t bsp_spiffs_unmount(void)
{
return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL);
}
//==================================================================================
// audio es7210 + es8388
//==================================================================================
static esp_codec_dev_handle_t play_dev_handle;
static esp_codec_dev_handle_t record_dev_handle;
static bsp_codec_config_t g_codec_handle;
static int volume;
/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */
#define BSP_I2S_GPIO_CFG \
{ \
.mclk = BSP_I2S_MCLK, .bclk = BSP_I2S_SCLK, .ws = BSP_I2S_LCLK, .dout = BSP_I2S_DOUT, .din = BSP_I2S_DSIN, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
/* This configuration is used by default in `bsp_extra_audio_init()` */
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \
{ \
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
.gpio_cfg = BSP_I2S_GPIO_CFG, \
}
esp_err_t bsp_audio_init(const i2s_std_config_t* i2s_config)
{
if (i2s_tx_chan && i2s_rx_chan)
{
/* Audio was initialized before */
return ESP_OK;
}
/* Setup I2S peripheral */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan));
/* Setup I2S channels */
// const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(16000);
const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(48000);
const i2s_std_config_t* p_i2s_cfg = &std_cfg_default;
if (i2s_config != NULL)
{
p_i2s_cfg = i2s_config;
}
if (i2s_tx_chan != NULL)
{
ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan));
}
// if (i2s_rx_chan != NULL) {
// ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg));
// ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan));
// }
i2s_tdm_config_t tdm_cfg = {
.clk_cfg =
{
.sample_rate_hz = (uint32_t)48000,
.clk_src = I2S_CLK_SRC_DEFAULT,
.ext_clk_freq_hz = 0,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
.bclk_div = 8,
},
.slot_cfg = {.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
.slot_mode = I2S_SLOT_MODE_STEREO,
.slot_mask = (I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3),
.ws_width = I2S_TDM_AUTO_WS_WIDTH,
.ws_pol = false,
.bit_shift = true,
.left_align = false,
.big_endian = false,
.bit_order_lsb = false,
.skip_mask = false,
.total_slot = I2S_TDM_AUTO_SLOT_NUM},
.gpio_cfg = BSP_I2S_GPIO_CFG,
};
if (i2s_rx_chan != NULL)
{
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(i2s_rx_chan, &tdm_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan));
}
audio_codec_i2s_cfg_t i2s_cfg = {
.port = CONFIG_BSP_I2S_NUM,
.tx_handle = i2s_tx_chan,
.rx_handle = i2s_rx_chan,
};
i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
return ESP_OK;
}
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void)
{
static esp_codec_dev_handle_t codec = NULL;
if (codec)
{
return codec;
}
if (i2s_data_if == NULL)
{
/* Initilize I2C */
bsp_i2c_init();
/* Configure I2S peripheral and Power Amplifier */
bsp_audio_init(NULL);
}
assert(i2s_data_if);
// const audio_codec_gpio_if_t* gpio_if = audio_codec_new_gpio(); // Not used in current implementation
i2c_master_bus_handle_t i2c_bus_handle = bsp_i2c_get_handle();
audio_codec_i2c_cfg_t i2c_cfg = {
// .port = BSP_I2C_NUM, // Commented out to avoid legacy I2C driver conflict
.addr = ES8388_CODEC_DEFAULT_ADDR,
.bus_handle = i2c_bus_handle,
};
const audio_codec_ctrl_if_t* i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
BSP_NULL_CHECK(i2c_ctrl_if, NULL);
// esp_codec_dev_hw_gain_t gain = { // Not used in current implementation
// .pa_voltage = 5.0,
// .codec_dac_voltage = 3.3,
// };
es8388_codec_cfg_t es8388_cfg = {
.codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC,
.master_mode = false,
.ctrl_if = i2c_ctrl_if,
.pa_pin = -1, // PI4IOE1 P1 控制
};
const audio_codec_if_t* es8388_dev = es8388_codec_new(&es8388_cfg);
BSP_NULL_CHECK(es8388_dev, NULL);
esp_codec_dev_cfg_t codec_dev_cfg = {
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
.codec_if = es8388_dev,
.data_if = i2s_data_if,
};
codec = esp_codec_dev_new(&codec_dev_cfg);
BSP_NULL_CHECK(codec, NULL);
return codec;
}
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void)
{
if (i2s_data_if == NULL)
{
/* Initilize I2C */
ESP_ERROR_CHECK(bsp_i2c_init());
/* Configure I2S peripheral and Power Amplifier */
ESP_ERROR_CHECK(bsp_audio_init(NULL));
// i2s_data_if = bsp_get_codec_data_if();
}
assert(i2s_data_if);
i2c_master_bus_handle_t i2c_bus_handle = bsp_i2c_get_handle();
audio_codec_i2c_cfg_t i2c_cfg = {
// .port = BSP_I2C_NUM, // Commented out to avoid legacy I2C driver conflict
.addr = ES7210_CODEC_DEFAULT_ADDR,
.bus_handle = i2c_bus_handle,
};
const audio_codec_ctrl_if_t* i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
BSP_NULL_CHECK(i2c_ctrl_if, NULL);
es7210_codec_cfg_t es7210_cfg = {
.ctrl_if = i2c_ctrl_if, // Codec Control interface
};
es7210_cfg.mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC2 | ES7210_SEL_MIC3 | ES7210_SEL_MIC4;
const audio_codec_if_t* es7210_dev = es7210_codec_new(&es7210_cfg);
BSP_NULL_CHECK(es7210_dev, NULL);
esp_codec_dev_cfg_t codec_es7210_dev_cfg = {
.dev_type =
ESP_CODEC_DEV_TYPE_IN, // Codec device type: Codec input device like ADC (capture data from microphone)
.codec_if = es7210_dev, // Codec interface
.data_if = i2s_data_if, // Codec data interface
};
return esp_codec_dev_new(&codec_es7210_dev_cfg);
}
static esp_err_t bsp_i2s_read(void* audio_buffer, size_t len, size_t* bytes_read, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ret = esp_codec_dev_read(record_dev_handle, audio_buffer, len);
*bytes_read = len;
return ret;
}
static esp_err_t bsp_i2s_write(void* audio_buffer, size_t len, size_t* bytes_written, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ret = esp_codec_dev_write(play_dev_handle, audio_buffer, len);
*bytes_written = len;
return ret;
}
static esp_err_t bsp_codec_set_in_gain(float gain)
{
return esp_codec_dev_set_in_gain(record_dev_handle, gain);
}
static esp_err_t bsp_codec_set_mute(bool enable)
{
esp_err_t ret = ESP_OK;
ret = esp_codec_dev_set_out_mute(play_dev_handle, enable);
return ret;
}
static esp_err_t bsp_codec_set_volume(int v)
{
esp_err_t ret = ESP_OK;
if (v <= 0)
{
volume = 0;
ret = esp_codec_dev_set_out_mute(play_dev_handle, true);
}
else
{
volume = v;
ret = esp_codec_dev_set_out_mute(play_dev_handle, false);
ret |= esp_codec_dev_set_out_vol(play_dev_handle, volume);
}
return ret;
}
static int bsp_codec_get_volume(void)
{
return volume;
}
bsp_codec_config_t* bsp_get_codec_handle(void)
{
return &g_codec_handle;
}
static esp_err_t bsp_codec_es8388_set(uint32_t rate, uint32_t bps, i2s_slot_mode_t ch)
{
esp_err_t ret = ESP_OK;
esp_codec_dev_sample_info_t fs = {
.sample_rate = rate,
.channel = ch,
.bits_per_sample = bps,
};
if (play_dev_handle)
{
ret = esp_codec_dev_close(play_dev_handle);
}
ret = esp_codec_dev_open(play_dev_handle, &fs);
return ret;
}
static esp_err_t bsp_codec_es7210_set(uint32_t rate, uint32_t bps, i2s_slot_mode_t ch)
{
esp_err_t ret = ESP_OK;
esp_codec_dev_sample_info_t fs = {
.sample_rate = rate,
.channel = ch,
.bits_per_sample = bps,
};
if (record_dev_handle)
{
ret = esp_codec_dev_close(record_dev_handle);
}
ret = esp_codec_dev_open(record_dev_handle, &fs);
// esp_codec_dev_set_in_gain(record_dev_handle, 80.0); // Set codec input gain
return ret;
}
void bsp_codec_init(void)
{
// CRITICAL: Lock I2C bus during codec initialization to prevent conflicts
// Audio codecs (ES8388/ES7210) initialization requires extensive I2C register access
// This prevents conflicts with other devices (BMI270, touch screen, etc.) during initialization
if (!bsp_i2c_lock(2000))
{ // 2 second timeout for initialization
ESP_LOGE(TAG, "Failed to lock I2C bus for codec initialization");
return;
}
play_dev_handle = bsp_audio_codec_speaker_init();
assert((play_dev_handle) && "play_dev_handle not initialized");
record_dev_handle = bsp_audio_codec_microphone_init();
assert((record_dev_handle) && "record_dev_handle not initialized");
// bsp_codec_es7210_set(16000, 16, 2);
// bsp_codec_es8388_set(16000, 16, 2);
// bsp_codec_es7210_set(48000, 16, 2);
bsp_codec_es7210_set(48000, 16, 4);
// Initialize ES8388 with default 48000, audio_player will reconfigure if needed
// This ensures codec is opened, but audio_player can change sample rate based on audio file
bsp_codec_es8388_set(48000, 16, 2);
/* åˆå§‹åŒ?codec handle */
bsp_codec_config_t* codec_cfg = bsp_get_codec_handle(); // 获å codec handle
codec_cfg->i2s_read = bsp_i2s_read; // I2S 读数æ?
codec_cfg->i2s_write = bsp_i2s_write; // I2S 写数æ?
codec_cfg->set_mute = bsp_codec_set_mute; // é™éŸ³è®¾ç½®
codec_cfg->set_volume = bsp_codec_set_volume; // 音é‡è®¾ç½®
codec_cfg->get_volume = bsp_codec_get_volume;
codec_cfg->set_in_gain = bsp_codec_set_in_gain; // 麦克风输入增益设�
codec_cfg->codec_reconfig_fn = bsp_codec_es7210_set;
codec_cfg->i2s_reconfig_clk_fn = bsp_codec_es8388_set;
codec_cfg->set_volume(10);
// Unlock I2C bus after codec initialization is complete
bsp_i2c_unlock();
}
uint8_t bsp_codec_feed_channel(void)
{
return 3; // 2*mic_num + ref_num
}
//==================================================================================
// 显示å±ç±»åž‹æ£€æµ?
//==================================================================================
typedef enum
{
BSP_DISPLAY_TYPE_UNKNOWN = 0,
BSP_DISPLAY_TYPE_ST7703_GT911,
BSP_DISPLAY_TYPE_ST7123
} bsp_display_type_t;
static bsp_display_type_t bsp_detect_display_type(void)
{
esp_err_t ret;
// ç¡®ä¿I2Cå·²åˆå§‹åŒ–
if (bsp_i2c_init() != ESP_OK)
{
ESP_LOGE(TAG, "I2C init failed for display detection");
return BSP_DISPLAY_TYPE_UNKNOWN;
}
// 检测GT911触摸å±?(ST7703é…套)
ret = i2c_master_probe(i2c_handle, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, 50);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "Detected GT911 touch controller, using ST7703 display");
return BSP_DISPLAY_TYPE_ST7703_GT911;
}
// 检测ST7123触摸�
ret = i2c_master_probe(i2c_handle, 0x55, 50);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "Detected ST7123 touch controller, using ST7123 display");
return BSP_DISPLAY_TYPE_ST7123;
}
ESP_LOGW(TAG, "No known touch controller detected, defaulting to ST7703");
return BSP_DISPLAY_TYPE_ST7703_GT911;
}
//==================================================================================
// lcd st7703 1280x720 gt911
//==================================================================================
// Bit number used to represent command and parameter
#define LCD_LEDC_CH LEDC_CHANNEL_1 // CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH
esp_err_t bsp_display_brightness_init(void)
{
if (s_lcd_backlight_initialized)
{
return ESP_OK;
}
// gpio_config_t io_conf = {};
// io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
// io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode
// io_conf.pin_bit_mask = 1 << BSP_LCD_BACKLIGHT; //select pin
// io_conf.pull_down_en = 0; //disable pull-down mode
// io_conf.pull_up_en = 0; //disable pull-up mode
// gpio_config(&io_conf); //configure GPIO with the given settings
// gpio_set_level(BSP_LCD_BACKLIGHT, 1);
// Setup LEDC peripheral for PWM backlight control
const ledc_timer_config_t lcd_backlight_timer = {.speed_mode = LEDC_LOW_SPEED_MODE,
// .duty_resolution = LEDC_TIMER_10_BIT,
.duty_resolution = LEDC_TIMER_12_BIT,
.timer_num = LEDC_TIMER_0,
.freq_hz = 5000,
// .freq_hz = 20000,
.clk_cfg = LEDC_AUTO_CLK};
ESP_ERROR_CHECK(ledc_timer_config(&lcd_backlight_timer));
const ledc_channel_config_t lcd_backlight_channel = {.gpio_num = BSP_LCD_BACKLIGHT,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LCD_LEDC_CH,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = LEDC_TIMER_0,
.duty = 0,
.hpoint = 0};
esp_gpio_revoke(BIT64(BSP_LCD_BACKLIGHT));
ESP_ERROR_CHECK(ledc_channel_config(&lcd_backlight_channel));
s_lcd_backlight_initialized = true;
return ESP_OK;
}
esp_err_t bsp_display_brightness_set(int brightness_percent)
{
if (brightness_percent > 100)
{
brightness_percent = 100;
}
if (brightness_percent < 0)
{
brightness_percent = 0;
}
ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
// uint32_t duty_cycle = (1023 * brightness_percent) / 100; // LEDC resolution set to 10bits, thus: 100% = 1023
uint32_t duty_cycle = (4095 * brightness_percent) / 100; // LEDC resolution set to 12bits, thus: 100% = 4095
BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle));
BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH));
return ESP_OK;
}
esp_err_t bsp_display_backlight_off(void)
{
return bsp_display_brightness_set(0);
}
esp_err_t bsp_display_backlight_on(void)
{
return bsp_display_brightness_set(100);
}
static esp_err_t bsp_enable_dsi_phy_power(void)
{
#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
static esp_ldo_channel_handle_t phy_pwr_chan = NULL;
esp_ldo_channel_config_t ldo_cfg = {
.chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN,
.voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV,
};
ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed");
ESP_LOGI(TAG, "MIPI DSI PHY Powered on");
#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0
return ESP_OK;
}
esp_err_t bsp_display_new(const bsp_display_config_t* config, esp_lcd_panel_handle_t* ret_panel,
esp_lcd_panel_io_handle_t* ret_io)
{
esp_err_t ret = ESP_OK;
bsp_lcd_handles_t handles;
ret = bsp_display_new_with_handles(config, &handles);
*ret_panel = handles.panel;
*ret_io = handles.io;
return ret;
}
#define LCD_MIPI_DSI_USE_ILI9881C
#if defined(LCD_MIPI_DSI_USE_ILI9881C) && !defined(LCD_MIPI_DSI_USE_ST7703)
#include "ili9881_init_data.c"
#endif
esp_err_t bsp_display_new_with_handles(const bsp_display_config_t* config, bsp_lcd_handles_t* ret_handles)
{
esp_err_t ret = ESP_OK;
esp_lcd_panel_io_handle_t io = NULL;
esp_lcd_panel_handle_t disp_panel = NULL;
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed");
/* create MIPI DSI bus first, it will initialize the DSI PHY as well */
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS,
};
ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed");
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel");
// we use DBI interface to send LCD commands and parameters
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8, // according to the LCD spec
.lcd_param_bits = 8, // according to the LCD spec
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed");
#if defined(LCD_MIPI_DSI_USE_ILI9881C) && !defined(LCD_MIPI_DSI_USE_ST7703)
ESP_LOGI(TAG, "Install LCD driver of ili9881c");
esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 60, // 720*1280 RGB24 60Hz RGB24 // 80,
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
.num_fbs = 1,
.video_timing =
{
.h_size = BSP_LCD_H_RES,
.v_size = BSP_LCD_V_RES,
.hsync_back_porch = 140,
.hsync_pulse_width = 40,
.hsync_front_porch = 40,
.vsync_back_porch = 20,
.vsync_pulse_width = 4,
.vsync_front_porch = 20,
},
.flags.use_dma2d = true,
};
ili9881c_vendor_config_t vendor_config = {
.init_cmds = tab5_lcd_ili9881c_specific_init_code_default,
.init_cmds_size = sizeof(tab5_lcd_ili9881c_specific_init_code_default) /
sizeof(tab5_lcd_ili9881c_specific_init_code_default[0]),
.mipi_config =
{
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
.lane_num = 2,
},
};
const esp_lcd_panel_dev_config_t lcd_dev_config = {
.bits_per_pixel = 16,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.reset_gpio_num = -1,
.vendor_config = &vendor_config,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(disp_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(disp_panel));
// ESP_ERROR_CHECK(esp_lcd_panel_mirror(disp_panel, false, true));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(disp_panel, true));
#elif defined(LCD_MIPI_DSI_USE_ST7703) && !defined(LCD_MIPI_DSI_USE_ILI9881C)
ESP_LOGI(TAG, "Install LCD driver of ST7703");
esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 60, // LCD_MIPI_DSI_DPI_CLK_MHZ_ST7703,
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, // LCD_COLOR_PIXEL_FORMAT_RGB888,
.num_fbs = 1,
.video_timing =
{
.h_size = BSP_LCD_H_RES, // lcd_param.width,
.v_size = BSP_LCD_V_RES, // lcd_param.height,
.hsync_back_porch = 40,
.hsync_pulse_width = 10,
.hsync_front_porch = 40,
.vsync_back_porch = 16,
.vsync_pulse_width = 4,
.vsync_front_porch = 16,
},
//.flags.use_dma2d = true, // Enable only after waiting for the previous draw to finish.
};
st7703_vendor_config_t vendor_config = {
.flags.use_mipi_interface = 1,
.mipi_config =
{
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
};
esp_lcd_panel_dev_config_t lcd_dev_config = {
.bits_per_pixel = 16, // 24,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.reset_gpio_num = -1,
.vendor_config = &vendor_config,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7703(io, &lcd_dev_config, &disp_panel), err, TAG,
"New LCD panel EK79007 failed");
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
#endif
/* Return all handles */
ret_handles->io = io;
ret_handles->mipi_dsi_bus = mipi_dsi_bus;
ret_handles->panel = disp_panel;
ret_handles->control = NULL;
ESP_LOGI(TAG, "Display initialized with resolution %dx%d", BSP_LCD_H_RES, BSP_LCD_V_RES);
return ret;
err:
if (disp_panel)
{
esp_lcd_panel_del(disp_panel);
}
if (io)
{
esp_lcd_panel_io_del(io);
}
if (mipi_dsi_bus)
{
esp_lcd_del_dsi_bus(mipi_dsi_bus);
}
return ret;
}
#define LCD_MIPI_DSI_USE_ST7123
#ifdef LCD_MIPI_DSI_USE_ST7123
#include "esp_lcd_st7123.h"
// ST7123 è§¦æ‘¸åæ ‡æ‰“å°å›žè°ƒå‡½æ•°
static void st7123_touch_callback(esp_lcd_touch_handle_t tp)
{
uint16_t touch_x[10];
uint16_t touch_y[10];
uint16_t touch_strength[10];
uint8_t touch_cnt = 0;
// 读å–触摸数æ®
esp_err_t ret = esp_lcd_touch_read_data(tp);
if (ret == ESP_OK)
{
// 获å–è§¦æ‘¸åæ ‡
bool pressed = esp_lcd_touch_get_coordinates(tp, touch_x, touch_y, touch_strength, &touch_cnt, 10);
if (pressed && touch_cnt > 0)
{
for (int i = 0; i < touch_cnt; i++)
{
printf("ST7123 Touch %d: x=%d, y=%d, strength=%d\n", i, touch_x[i], touch_y[i], touch_strength[i]);
}
}
}
}
// ST7123 vendor specific initialization commands
static const st7123_lcd_init_cmd_t st7123_vendor_specific_init_default[] = {
{0x60, (uint8_t[]){0x71, 0x23, 0xa2}, 3, 0},
{0x60, (uint8_t[]){0x71, 0x23, 0xa3}, 3, 0},
{0x60, (uint8_t[]){0x71, 0x23, 0xa4}, 3, 0},
{0xA4, (uint8_t[]){0x31}, 1, 0},
{0xD7, (uint8_t[]){0x10, 0x0A, 0x10, 0x2A, 0x80, 0x80}, 6, 0},
{0x90, (uint8_t[]){0x71, 0x23, 0x5A, 0x20, 0x24, 0x09, 0x09}, 7, 0},
{0xA3, (uint8_t[]){0x80, 0x01, 0x88, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x1E, 0x5C, 0x1E, 0x80, 0x00, 0x4F, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x1E, 0x5C, 0x1E, 0x80, 0x00, 0x6F, 0x58, 0x00, 0x00, 0x00, 0xFF},
40, 0},
{0xA6, (uint8_t[]){0x03, 0x00, 0x24, 0x55, 0x36, 0x00, 0x39, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0x24, 0x55, 0x38, 0x00, 0x37, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0xEC, 0x11, 0x00, 0x03, 0x00, 0x03, 0x6E, 0x6E, 0xFF, 0xFF, 0x00, 0x08, 0x80, 0x08, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00},
55, 0},
{0xA7, (uint8_t[]){0x19, 0x19, 0x80, 0x64, 0x40, 0x07, 0x16, 0x40, 0x00, 0x44, 0x03, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x25, 0x34, 0x40, 0x00, 0x02, 0x01, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x6E, 0x6E, 0x84, 0xFF, 0x08, 0x80, 0x44},
60, 0},
{0xAC, (uint8_t[]){0x03, 0x19, 0x19, 0x18, 0x18, 0x06, 0x13, 0x13, 0x11, 0x11, 0x08, 0x08, 0x0A, 0x0A, 0x1C, 0x1C, 0x07, 0x07, 0x00, 0x00, 0x02, 0x02, 0x01, 0x19, 0x19, 0x18, 0x18, 0x06, 0x12, 0x12, 0x10, 0x10, 0x09, 0x09, 0x0B, 0x0B, 0x1C, 0x1C, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01},
44, 0},
{0xAD, (uint8_t[]){0xF0, 0x00, 0x46, 0x00, 0x03, 0x50, 0x50, 0xFF, 0xFF, 0xF0, 0x40, 0x06, 0x01, 0x07, 0x42, 0x42, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF},
25, 0},
{0xAE, (uint8_t[]){0xFE, 0x3F, 0x3F, 0xFE, 0x3F, 0x3F, 0x00}, 7, 0},
{0xB2,
(uint8_t[]){0x15, 0x19, 0x05, 0x23, 0x49, 0xAF, 0x03, 0x2E, 0x5C, 0xD2, 0xFF, 0x10, 0x20, 0xFD, 0x20, 0xC0, 0x00},
17, 0},
{0xE8, (uint8_t[]){0x20, 0x6F, 0x04, 0x97, 0x97, 0x3E, 0x04, 0xDC, 0xDC, 0x3E, 0x06, 0xFA, 0x26, 0x3E}, 15, 0},
{0x75, (uint8_t[]){0x03, 0x04}, 2, 0},
{0xE7, (uint8_t[]){0x3B, 0x00, 0x00, 0x7C, 0xA1, 0x8C, 0x20, 0x1A, 0xF0, 0xB1, 0x50, 0x00, 0x50, 0xB1, 0x50, 0xB1, 0x50, 0xD8, 0x00, 0x55, 0x00, 0xB1, 0x00, 0x45, 0xC9, 0x6A, 0xFF, 0x5A, 0xD8, 0x18, 0x88, 0x15, 0xB1, 0x01, 0x01, 0x77},
36, 0},
{0xEA, (uint8_t[]){0x13, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2C}, 8, 0},
{0xB0, (uint8_t[]){0x22, 0x43, 0x11, 0x61, 0x25, 0x43, 0x43}, 7, 0},
{0xb7, (uint8_t[]){0x00, 0x00, 0x73, 0x73}, 0x04, 0},
{0xBF, (uint8_t[]){0xA6, 0XAA}, 2, 0},
{0xA9, (uint8_t[]){0x00, 0x00, 0x73, 0xFF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03}, 10, 0},
{0xC8, (uint8_t[]){0x00, 0x00, 0x10, 0x1F, 0x36, 0x00, 0x5D, 0x04, 0x9D, 0x05, 0x10, 0xF2, 0x06, 0x60, 0x03, 0x11, 0xAD, 0x00, 0xEF, 0x01, 0x22, 0x2E, 0x0E, 0x74, 0x08, 0x32, 0xDC, 0x09, 0x33, 0x0F, 0xF3, 0x77, 0x0D, 0xB0, 0xDC, 0x03, 0xFF},
37, 0},
{0xC9, (uint8_t[]){0x00, 0x00, 0x10, 0x1F, 0x36, 0x00, 0x5D, 0x04, 0x9D, 0x05, 0x10, 0xF2, 0x06, 0x60, 0x03, 0x11, 0xAD, 0x00, 0xEF, 0x01, 0x22, 0x2E, 0x0E, 0x74, 0x08, 0x32, 0xDC, 0x09, 0x33, 0x0F, 0xF3, 0x77, 0x0D, 0xB0, 0xDC, 0x03, 0xFF},
37, 0},
{0x36, (uint8_t[]){0x00}, 1, 0},
{0x11, (uint8_t[]){0x00}, 1, 100},
{0x29, (uint8_t[]){0x00}, 1, 0},
{0x35, (uint8_t[]){0x00}, 1, 100},
};
// 适é…st7123 tab5 å±å¹•
esp_err_t bsp_display_new_with_handles_to_st7123(const bsp_display_config_t* config, bsp_lcd_handles_t* ret_handles)
{
esp_err_t ret = ESP_OK;
esp_lcd_panel_io_handle_t io = NULL;
esp_lcd_panel_handle_t disp_panel = NULL;
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed");
ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed");
/* create MIPI DSI bus first, it will initialize the DSI PHY as well */
esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = 2, // ST7123 uses 2 data lanes
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = 965, // ST7123 lane bitrate
};
ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed");
ESP_LOGI(TAG, "Install MIPI DSI LCD control panel for ST7123");
// we use DBI interface to send LCD commands and parameters
esp_lcd_dbi_io_config_t dbi_config = {
.virtual_channel = 0,
.lcd_cmd_bits = 8, // according to the LCD spec
.lcd_param_bits = 8, // according to the LCD spec
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed");
ESP_LOGI(TAG, "Install LCD driver of ST7123");
esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 70, // ST7123 DPI clock frequency
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
.num_fbs = 1,
.video_timing =
{
.h_size = 720,
.v_size = 1280,
.hsync_pulse_width = 2,
.hsync_back_porch = 40,
.hsync_front_porch = 40,
.vsync_pulse_width = 2,
.vsync_back_porch = 8,
.vsync_front_porch = 220,
},
.flags =
{
.use_dma2d = true,
},
};
st7123_vendor_config_t vendor_config = {
.init_cmds = st7123_vendor_specific_init_default,
.init_cmds_size = sizeof(st7123_vendor_specific_init_default) / sizeof(st7123_vendor_specific_init_default[0]),
.mipi_config =
{
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
.lane_num = 2,
},
};
const esp_lcd_panel_dev_config_t lcd_dev_config = {
.reset_gpio_num = -1,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
.bits_per_pixel = 24,
.vendor_config = &vendor_config,
};
// 使用实际�ST7123 驱动函数
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7123(io, &lcd_dev_config, &disp_panel), err, TAG,
"New LCD panel ST7123 failed");
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed");
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed");
ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel display on failed");
/* Return all handles */
ret_handles->io = io;
ret_handles->mipi_dsi_bus = mipi_dsi_bus;
ret_handles->panel = disp_panel;
ret_handles->control = NULL;
ESP_LOGI(TAG, "ST7123 Display initialized with resolution %dx%d", 720, 1280);
return ret;
err:
if (disp_panel)
{
esp_lcd_panel_del(disp_panel);
}
if (io)
{
esp_lcd_panel_io_del(io);
}
if (mipi_dsi_bus)
{
esp_lcd_del_dsi_bus(mipi_dsi_bus);
}
return ret;
}
#endif // LCD_MIPI_DSI_USE_ST7123
esp_err_t bsp_touch_new(const bsp_touch_config_t* config, esp_lcd_touch_handle_t* ret_touch)
{
/* Initilize I2C */
BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
/* Initialize touch */
const esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_H_RES,
.y_max = BSP_LCD_V_RES,
.rst_gpio_num = -1, // BSP_LCD_TOUCH_RST, // NC
.int_gpio_num = BSP_LCD_TOUCH_INT,
.levels =
{
.reset = 0,
.interrupt = 0,
},
.flags =
{
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
tp_io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; // 更改 GT911 地å
tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ;
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, "");
return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch);
}
#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
static lv_display_t* bsp_display_lcd_init(const bsp_display_cfg_t* cfg)
{
assert(cfg != NULL);
bsp_lcd_handles_t lcd_panels;
// åŠ¨æ€æ£€æµ‹æ˜¾ç¤ºå±ç±»åž‹
bsp_display_type_t display_type = bsp_detect_display_type();
if (display_type == BSP_DISPLAY_TYPE_ST7123)
{
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles_to_st7123(NULL, &lcd_panels));
}
else
{
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(NULL, &lcd_panels));
}
/* Add LCD screen */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg =
{.io_handle = lcd_panels.io,
.panel_handle = lcd_panels.panel,
.control_handle = lcd_panels.control,
.buffer_size = cfg->buffer_size,
.double_buffer = cfg->double_buffer,
.hres = BSP_LCD_H_RES,
.vres = BSP_LCD_V_RES,
.monochrome = false,
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
.rotation =
{
.swap_xy = false,
.mirror_x = false,
.mirror_y = false,
},
#if LVGL_VERSION_MAJOR >= 9
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
.color_format = LV_COLOR_FORMAT_RGB888,
#else
.color_format = LV_COLOR_FORMAT_RGB565,
#endif
#endif
.flags = {
.buff_dma = cfg->flags.buff_dma,
.buff_spiram = cfg->flags.buff_spiram,
#if LVGL_VERSION_MAJOR >= 9
.swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
#endif
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
.sw_rotate = false, /* Avoid tearing is not supported for SW rotation */
#else
.sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90° and 270° */
#endif
#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH
.full_refresh = true,
#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE
.direct_mode = true,
#endif
} };
const lvgl_port_display_dsi_cfg_t dpi_cfg = {.flags = {
#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR
.avoid_tearing = true,
#else
.avoid_tearing = false,
#endif
}};
return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg);
}
esp_lcd_touch_handle_t _lcd_touch_handle;
esp_lcd_touch_handle_t bsp_display_get_touch_handle(void)
{
return _lcd_touch_handle;
}
bool trail_mate_tab5_touch_interrupt_active(void)
{
return (BSP_LCD_TOUCH_INT != GPIO_NUM_NC) && (gpio_get_level(BSP_LCD_TOUCH_INT) == 0);
}
static void trail_mate_touch_activity_event_cb(lv_event_t* e)
{
const lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING || code == LV_EVENT_RELEASED)
{
trail_mate_idf_note_user_activity();
}
}
static void trail_mate_attach_touch_activity_hook(lv_indev_t* indev)
{
if (indev == NULL)
{
return;
}
lv_indev_add_event_cb(indev, trail_mate_touch_activity_event_cb, LV_EVENT_PRESSED, NULL);
lv_indev_add_event_cb(indev, trail_mate_touch_activity_event_cb, LV_EVENT_PRESSING, NULL);
lv_indev_add_event_cb(indev, trail_mate_touch_activity_event_cb, LV_EVENT_RELEASED, NULL);
}
static void lvgl_read_cb(lv_indev_t* indev, lv_indev_data_t* data)
{
if (_lcd_touch_handle == NULL)
{
data->state = LV_INDEV_STATE_REL;
return;
}
// Lock SYS I2C bus before accessing touch screen
// Use try-lock (0ms timeout) to avoid blocking other sensors
// Touch screen is polled frequently (every LVGL refresh cycle ~33ms), so we must not block
// If bus is busy, skip this read cycle - missing one touch read is acceptable
// This ensures other sensors (BMI270, RX8130, INA226, Module GNSS, etc.) can access I2C bus
if (!bsp_i2c_lock(0))
{ // Try-lock: if bus is busy, skip this read cycle
// Bus is busy, assume no touch and return immediately
// This prevents touch screen from monopolizing the I2C bus
data->state = LV_INDEV_STATE_REL;
return;
}
esp_lcd_touch_point_data_t touch_data[1] = {0};
uint8_t touch_cnt = 0;
esp_err_t ret = esp_lcd_touch_read_data(_lcd_touch_handle);
if (ret != ESP_OK)
{
// If I2C read fails, assume no touch and return
bsp_i2c_unlock();
data->state = LV_INDEV_STATE_REL;
return;
}
ret = esp_lcd_touch_get_data(_lcd_touch_handle, touch_data, &touch_cnt, 1);
// Unlock I2C bus after operation
bsp_i2c_unlock();
if (ret != ESP_OK)
{
data->state = LV_INDEV_STATE_REL;
return;
}
bool touchpad_pressed = (touch_cnt > 0);
// Validate touch coordinates to prevent invalid values from being passed to LVGL
// Invalid coordinates (e.g., -64256, 16383) can cause rendering issues
if (!touchpad_pressed)
{
data->state = LV_INDEV_STATE_REL;
}
else if (touch_cnt > 0 && touch_data[0].x < 2000 && touch_data[0].y < 2000)
{
// Check if coordinates are within valid screen bounds
// Screen size is typically 1280x720, but allow some margin for edge cases
data->state = LV_INDEV_STATE_PR;
data->point.x = touch_data[0].x;
data->point.y = touch_data[0].y;
trail_mate_idf_note_user_activity();
}
else
{
// Invalid coordinates, treat as no touch
data->state = LV_INDEV_STATE_REL;
}
}
static lv_indev_t* bsp_display_indev_init(lv_display_t* disp)
{
esp_lcd_touch_handle_t tp;
BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
esp_lcd_touch_exit_sleep(tp); // !!!
assert(tp);
_lcd_touch_handle = tp;
disp_indev = lv_indev_create();
lv_indev_set_type(disp_indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(disp_indev, lvgl_read_cb);
lv_indev_set_display(disp_indev, disp);
trail_mate_attach_touch_activity_hook(disp_indev);
return disp_indev;
}
static lv_indev_t* bsp_display_indev_init_to_st7123(lv_display_t* disp)
{
esp_err_t ret = ESP_OK;
esp_lcd_touch_handle_t tp = NULL;
/* Initialize I2C for ST7123 touch */
BSP_ERROR_CHECK_RETURN_NULL(bsp_i2c_init());
// bsp_io_expander_pi4ioe_restart_touch();
/* Initialize ST7123 touch panel */
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = 0x55, // ST7123 touch I2C address
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 16,
.flags =
{
.disable_control_phase = 1,
},
};
tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ;
ret = esp_lcd_new_panel_io_i2c_v2(bsp_i2c_get_handle(), &tp_io_config, &tp_io_handle);
// ret = esp_lcd_new_panel_io_i2c(bsp_i2c_get_handle(), &tp_io_config, &tp_io_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to create ST7123 touch I2C IO: %s", esp_err_to_name(ret));
return NULL;
}
const esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_H_RES,
.y_max = BSP_LCD_V_RES,
.rst_gpio_num = -1, // BSP_LCD_TOUCH_RST, // NC
.int_gpio_num = BSP_LCD_TOUCH_INT,
.levels =
{
.reset = 0,
.interrupt = 0,
},
.flags =
{
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
ret = esp_lcd_touch_new_i2c_st7123(tp_io_handle, &tp_cfg, &tp);
if (ret != ESP_OK || tp == NULL)
{
ESP_LOGE(TAG, "Failed to create ST7123 touch panel: %s", esp_err_to_name(ret));
esp_lcd_panel_io_del(tp_io_handle);
return NULL;
}
// Exit sleep mode for touch panel
// esp_lcd_touch_exit_sleep(tp);
// Store the touch handle globally
_lcd_touch_handle = tp;
/* Add touch input to LVGL */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
lv_indev_t* indev = lvgl_port_add_touch(&touch_cfg);
if (indev == NULL)
{
ESP_LOGE(TAG, "Failed to add ST7123 touch to LVGL");
esp_lcd_panel_io_del(tp_io_handle);
return NULL;
}
trail_mate_attach_touch_activity_hook(indev);
ESP_LOGI(TAG, "ST7123 touch panel initialized successfully");
return indev;
}
lv_display_t* bsp_display_start(void)
{
bsp_display_cfg_t cfg = {.lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(),
.buffer_size = BSP_LCD_DRAW_BUFF_SIZE,
.double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE,
.flags = {
#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888
.buff_dma = false,
#else
.buff_dma = true,
#endif
.buff_spiram = false,
.sw_rotate = true,
}};
return bsp_display_start_with_config(&cfg);
}
lv_display_t* bsp_display_start_with_config(const bsp_display_cfg_t* cfg)
{
lv_display_t* disp;
assert(cfg != NULL);
BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg));
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init());
BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL);
// åŠ¨æ€æ£€æµ‹æ˜¾ç¤ºå±ç±»åž‹å¹¶åˆå§‹åŒ–对应的触摸å±
bsp_display_type_t display_type = bsp_detect_display_type();
if (display_type == BSP_DISPLAY_TYPE_ST7123)
{
BSP_NULL_CHECK(disp_indev = bsp_display_indev_init_to_st7123(disp), NULL);
}
else
{
BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL);
}
return disp;
}
lv_indev_t* bsp_display_get_input_dev(void)
{
return disp_indev;
}
void bsp_display_rotate(lv_display_t* disp, lv_disp_rotation_t rotation)
{
lv_disp_set_rotation(disp, rotation);
}
bool bsp_display_lock(uint32_t timeout_ms)
{
return lvgl_port_lock(timeout_ms);
}
void bsp_display_unlock(void)
{
lvgl_port_unlock();
}
#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0)
//==================================================================================
// usb
//==================================================================================
static void usb_lib_task(void* arg)
{
while (1)
{
// Start handling system events
uint32_t event_flags;
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
{
ESP_ERROR_CHECK(usb_host_device_free_all());
}
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
{
ESP_LOGI(TAG, "USB: All devices freed");
// Continue handling USB events to allow device reconnection
// The only way this task can be stopped is by calling bsp_usb_host_stop()
}
}
}
esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA)
{
// Install USB Host driver. Should only be called once in entire application
ESP_LOGI(TAG, "Installing USB Host");
const usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};
BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config));
// Create a task that will handle USB library events
if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE)
{
ESP_LOGE(TAG, "Creating USB host lib task failed");
abort();
}
return ESP_OK;
}
esp_err_t bsp_usb_host_stop(void)
{
usb_host_uninstall();
if (usb_host_task)
{
vTaskSuspend(usb_host_task);
vTaskDelete(usb_host_task);
}
return ESP_OK;
}