diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ad69202a..7fbebf69 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,6 +2,7 @@ set(SOURCES "audio_codecs/audio_codec.cc" "audio_codecs/no_audio_codec.cc" "audio_codecs/box_audio_codec.cc" "audio_codecs/es8311_audio_codec.cc" + "audio_codecs/cores3_audio_codec.cc" "display/display.cc" "display/no_display.cc" "display/st7789_display.cc" @@ -52,6 +53,8 @@ elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV) set(BOARD_TYPE "lichuang-dev") elseif(CONFIG_BOARD_TYPE_TERRENCE_C3_DEV) set(BOARD_TYPE "terrence-c3-dev") +elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_S3) + set(BOARD_TYPE "m5stack-core-s3") endif() file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc) list(APPEND SOURCES ${BOARD_SOURCES}) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 76eb8cf9..dc040ae3 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -50,6 +50,8 @@ choice BOARD_TYPE bool "Kevin C3" config BOARD_TYPE_LICHUANG_DEV bool "立创开发板" + config BOARD_TYPE_M5STACK_CORE_S3 + bool "M5Stack CoreS3" endchoice endmenu diff --git a/main/audio_codecs/cores3_audio_codec.cc b/main/audio_codecs/cores3_audio_codec.cc new file mode 100644 index 00000000..a3ba66ab --- /dev/null +++ b/main/audio_codecs/cores3_audio_codec.cc @@ -0,0 +1,245 @@ +#include "cores3_audio_codec.h" + +#include +#include +#include +#include + + +static const char TAG[] = "CoreS3AudioCodec"; + +CoreS3AudioCodec::CoreS3AudioCodec(void* i2c_master_handle, int input_sample_rate, int output_sample_rate, + gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din, + uint8_t aw88298_addr, uint8_t es7210_addr, bool input_reference) { + duplex_ = true; // 是否双工 + input_reference_ = input_reference; // 是否使用参考输入,实现回声消除 + input_channels_ = input_reference_ ? 2 : 1; // 输入通道数 + input_sample_rate_ = input_sample_rate; + output_sample_rate_ = output_sample_rate; + + CreateDuplexChannels(mclk, bclk, ws, dout, din); + + // Do initialize of related interface: data_if, ctrl_if and gpio_if + audio_codec_i2s_cfg_t i2s_cfg = { + .port = I2S_NUM_0, + .rx_handle = rx_handle_, + .tx_handle = tx_handle_, + }; + data_if_ = audio_codec_new_i2s_data(&i2s_cfg); + assert(data_if_ != NULL); + + // Output + audio_codec_i2c_cfg_t i2c_cfg = { + .port = (i2c_port_t)1, + .addr = aw88298_addr, + .bus_handle = i2c_master_handle, + }; + out_ctrl_if_ = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(out_ctrl_if_ != NULL); + + gpio_if_ = audio_codec_new_gpio(); + assert(gpio_if_ != NULL); + + aw88298_codec_cfg_t aw88298_cfg = {}; + aw88298_cfg.ctrl_if = out_ctrl_if_; + aw88298_cfg.gpio_if = gpio_if_; + aw88298_cfg.reset_pin = GPIO_NUM_NC; + aw88298_cfg.hw_gain.pa_voltage = 5.0; + aw88298_cfg.hw_gain.codec_dac_voltage = 3.3; + aw88298_cfg.hw_gain.pa_gain = 1; + out_codec_if_ = aw88298_codec_new(&aw88298_cfg); + assert(out_codec_if_ != NULL); + + esp_codec_dev_cfg_t dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_OUT, + .codec_if = out_codec_if_, + .data_if = data_if_, + }; + output_dev_ = esp_codec_dev_new(&dev_cfg); + assert(output_dev_ != NULL); + + // Input + i2c_cfg.addr = es7210_addr; + in_ctrl_if_ = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(in_ctrl_if_ != NULL); + + es7210_codec_cfg_t es7210_cfg = {}; + es7210_cfg.ctrl_if = in_ctrl_if_; + es7210_cfg.mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC2 | ES7120_SEL_MIC3; + in_codec_if_ = es7210_codec_new(&es7210_cfg); + assert(in_codec_if_ != NULL); + + dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN; + dev_cfg.codec_if = in_codec_if_; + input_dev_ = esp_codec_dev_new(&dev_cfg); + assert(input_dev_ != NULL); + + ESP_LOGI(TAG, "CoreS3AudioCodec initialized"); +} + +CoreS3AudioCodec::~CoreS3AudioCodec() { + ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_)); + esp_codec_dev_delete(output_dev_); + ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_)); + esp_codec_dev_delete(input_dev_); + + audio_codec_delete_codec_if(in_codec_if_); + audio_codec_delete_ctrl_if(in_ctrl_if_); + audio_codec_delete_codec_if(out_codec_if_); + audio_codec_delete_ctrl_if(out_ctrl_if_); + audio_codec_delete_gpio_if(gpio_if_); + audio_codec_delete_data_if(data_if_); +} + +void CoreS3AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) { + assert(input_sample_rate_ == output_sample_rate_); + + ESP_LOGI(TAG, "Audio IOs: mclk: %d, bclk: %d, ws: %d, dout: %d, din: %d", mclk, bclk, ws, dout, din); + + i2s_chan_config_t chan_cfg = { + .id = I2S_NUM_0, + .role = I2S_ROLE_MASTER, + .dma_desc_num = 2, + .dma_frame_num = 240 * 3, + .auto_clear_after_cb = true, + .auto_clear_before_cb = false, + .intr_priority = 0, + }; + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, &rx_handle_)); + + i2s_std_config_t std_cfg = { + .clk_cfg = { + .sample_rate_hz = (uint32_t)output_sample_rate_, + .clk_src = I2S_CLK_SRC_DEFAULT, + .ext_clk_freq_hz = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_256 + }, + .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_STD_SLOT_BOTH, + .ws_width = I2S_DATA_BIT_WIDTH_16BIT, + .ws_pol = false, + .bit_shift = true, + .left_align = true, + .big_endian = false, + .bit_order_lsb = false + }, + .gpio_cfg = { + .mclk = mclk, + .bclk = bclk, + .ws = ws, + .dout = dout, + .din = I2S_GPIO_UNUSED, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false + } + } + }; + + i2s_tdm_config_t tdm_cfg = { + .clk_cfg = { + .sample_rate_hz = (uint32_t)input_sample_rate_, + .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_slot_mask_t(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 = { + .mclk = mclk, + .bclk = bclk, + .ws = ws, + .dout = I2S_GPIO_UNUSED, + .din = din, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false + } + } + }; + + ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg)); + ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_handle_, &tdm_cfg)); + ESP_LOGI(TAG, "Duplex channels created"); +} + +void CoreS3AudioCodec::SetOutputVolume(int volume) { + ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, volume)); + AudioCodec::SetOutputVolume(volume); +} + +void CoreS3AudioCodec::EnableInput(bool enable) { + if (enable == input_enabled_) { + return; + } + if (enable) { + esp_codec_dev_sample_info_t fs = { + .bits_per_sample = 16, + .channel = 2, + .channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0), + .sample_rate = (uint32_t)output_sample_rate_, + .mclk_multiple = 0, + }; + if (input_reference_) { + fs.channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1); + } + ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs)); + ESP_ERROR_CHECK(esp_codec_dev_set_in_channel_gain(input_dev_, ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0), 40.0)); + } else { + ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_)); + } + AudioCodec::EnableInput(enable); +} + +void CoreS3AudioCodec::EnableOutput(bool enable) { + if (enable == output_enabled_) { + return; + } + if (enable) { + // Play 16bit 1 channel + esp_codec_dev_sample_info_t fs = { + .bits_per_sample = 16, + .channel = 1, + .channel_mask = 0, + .sample_rate = (uint32_t)output_sample_rate_, + .mclk_multiple = 0, + }; + ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs)); + ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_)); + } else { + ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_)); + } + AudioCodec::EnableOutput(enable); +} + +int CoreS3AudioCodec::Read(int16_t* dest, int samples) { + if (input_enabled_) { + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_read(input_dev_, (void*)dest, samples * sizeof(int16_t))); + } + return samples; +} + +int CoreS3AudioCodec::Write(const int16_t* data, int samples) { + if (output_enabled_) { + ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t))); + } + return samples; +} diff --git a/main/audio_codecs/cores3_audio_codec.h b/main/audio_codecs/cores3_audio_codec.h new file mode 100644 index 00000000..4b034b48 --- /dev/null +++ b/main/audio_codecs/cores3_audio_codec.h @@ -0,0 +1,37 @@ +#ifndef _BOX_AUDIO_CODEC_H +#define _BOX_AUDIO_CODEC_H + +#include "audio_codec.h" + +#include +#include + +class CoreS3AudioCodec : public AudioCodec { +private: + const audio_codec_data_if_t* data_if_ = nullptr; + const audio_codec_ctrl_if_t* out_ctrl_if_ = nullptr; + const audio_codec_if_t* out_codec_if_ = nullptr; + const audio_codec_ctrl_if_t* in_ctrl_if_ = nullptr; + const audio_codec_if_t* in_codec_if_ = nullptr; + const audio_codec_gpio_if_t* gpio_if_ = nullptr; + + esp_codec_dev_handle_t output_dev_ = nullptr; + esp_codec_dev_handle_t input_dev_ = nullptr; + + void CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din); + + virtual int Read(int16_t* dest, int samples) override; + virtual int Write(const int16_t* data, int samples) override; + +public: + CoreS3AudioCodec(void* i2c_master_handle, int input_sample_rate, int output_sample_rate, + gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din, + uint8_t aw88298_addr, uint8_t es7210_addr, bool input_reference); + virtual ~CoreS3AudioCodec(); + + virtual void SetOutputVolume(int volume) override; + virtual void EnableInput(bool enable) override; + virtual void EnableOutput(bool enable) override; +}; + +#endif // _BOX_AUDIO_CODEC_H diff --git a/main/boards/common/i2c_device.cc b/main/boards/common/i2c_device.cc index 187efa5a..5a9667aa 100644 --- a/main/boards/common/i2c_device.cc +++ b/main/boards/common/i2c_device.cc @@ -29,3 +29,7 @@ uint8_t I2cDevice::ReadReg(uint8_t reg) { ESP_ERROR_CHECK(i2c_master_transmit_receive(i2c_device_, ®, 1, buffer, 1, 100)); return buffer[0]; } + +void I2cDevice::ReadRegs(uint8_t reg, uint8_t* buffer, size_t length) { + ESP_ERROR_CHECK(i2c_master_transmit_receive(i2c_device_, ®, 1, buffer, length, 100)); +} \ No newline at end of file diff --git a/main/boards/common/i2c_device.h b/main/boards/common/i2c_device.h index c095b027..7bc917bf 100644 --- a/main/boards/common/i2c_device.h +++ b/main/boards/common/i2c_device.h @@ -12,6 +12,7 @@ protected: void WriteReg(uint8_t reg, uint8_t value); uint8_t ReadReg(uint8_t reg); + void ReadRegs(uint8_t reg, uint8_t* buffer, size_t length); }; #endif // I2C_DEVICE_H diff --git a/main/boards/m5stack-core-s3/config.h b/main/boards/m5stack-core-s3/config.h new file mode 100644 index 00000000..c11449a4 --- /dev/null +++ b/main/boards/m5stack-core-s3/config.h @@ -0,0 +1,42 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_REFERENCE true +#define AUDIO_INPUT_SAMPLE_RATE 24000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 +#define AUDIO_DEFAULT_OUTPUT_VOLUME 80 + +#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_0 +#define AUDIO_I2S_GPIO_WS GPIO_NUM_33 +#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_34 +#define AUDIO_I2S_GPIO_DIN GPIO_NUM_14 +#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_13 + +#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_12 +#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_11 +#define AUDIO_CODEC_AW88298_ADDR AW88298_CODEC_DEFAULT_ADDR +#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR + +#define BUILTIN_LED_GPIO GPIO_NUM_NC +#define BOOT_BUTTON_GPIO GPIO_NUM_0 +#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC +#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC + +#define DISPLAY_SDA_PIN GPIO_NUM_NC +#define DISPLAY_SCL_PIN GPIO_NUM_NC +#define DISPLAY_WIDTH 320 +#define DISPLAY_HEIGHT 240 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true + + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/m5stack-core-s3/m5stack_core_s3.cc b/main/boards/m5stack-core-s3/m5stack_core_s3.cc new file mode 100644 index 00000000..d47bc7d4 --- /dev/null +++ b/main/boards/m5stack-core-s3/m5stack_core_s3.cc @@ -0,0 +1,287 @@ +#include "wifi_board.h" +#include "audio_codecs/cores3_audio_codec.h" +#include "display/st7789_display.h" +#include "application.h" +#include "button.h" +#include "led.h" +#include "config.h" +#include "i2c_device.h" +#include "iot/thing_manager.h" + +#include +#include +#include +#include +#include "esp_lcd_ili9341.h" + +#define TAG "M5StackCoreS3Board" + +class Axp2101 : public I2cDevice { +public: + Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { + uint8_t data = ReadReg(0x90); + data |= 0b10110100; + WriteReg(0x90, data); + WriteReg(0x99, (0b11110 - 5)); + WriteReg(0x97, (0b11110 - 2)); + WriteReg(0x69, 0b00110101); + WriteReg(0x30, 0b111111); + + WriteReg(0x90, 0xBF); + WriteReg(0x94, 33 - 5); + WriteReg(0x95, 33 - 5); + } +}; + +class Aw9523 : public I2cDevice { +public: + Aw9523(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { + // WriteReg(0x02, 0b00000101); // P0 + // WriteReg(0x03, 0b00000011); // P1 + WriteReg(0x02, 0b00000111); // P0 + // WriteReg(0x03, 0b10000011); // P1 + WriteReg(0x03, 0b10001111); // P1 + WriteReg(0x04, 0b00011000); // CONFIG_P0 + WriteReg(0x05, 0b00001100); // CONFIG_P1 + WriteReg(0x11, 0b00010000); // GCR P0 port is Push-Pull mode. + WriteReg(0x12, 0b11111111); // LEDMODE_P0 + WriteReg(0x13, 0b11111111); // LEDMODE_P1 + } + + void ResetAw88298() { + ESP_LOGI(TAG, "Reset AW88298"); + WriteReg(0x02, 0b00000011); + vTaskDelay(pdMS_TO_TICKS(10)); + WriteReg(0x02, 0b00000111); + vTaskDelay(pdMS_TO_TICKS(50)); + } + + void ResetIli9342() { + ESP_LOGI(TAG, "Reset IlI9342"); + WriteReg(0x03, 0b10000001); + vTaskDelay(pdMS_TO_TICKS(20)); + WriteReg(0x03, 0b10000011); + vTaskDelay(pdMS_TO_TICKS(10)); + } +}; + +class Ft6336 : public I2cDevice { +public: + struct TouchPoint_t { + int num = 0; + int x = -1; + int y = -1; + }; + + Ft6336(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { + uint8_t chip_id = ReadReg(0xA3); + ESP_LOGI(TAG, "Get chip ID: 0x%02X", chip_id); + read_buffer_ = new uint8_t[6]; + } + + ~Ft6336() { + delete[] read_buffer_; + } + + void UpdateTouchPoint() { + ReadRegs(0x02, read_buffer_, 6); + tp_.num = read_buffer_[0] & 0x0F; + tp_.x = ((read_buffer_[1] & 0x0F) << 8) | read_buffer_[2]; + tp_.y = ((read_buffer_[3] & 0x0F) << 8) | read_buffer_[4]; + } + + const TouchPoint_t& GetTouchPoint() { + return tp_; + } + +private: + uint8_t* read_buffer_ = nullptr; + TouchPoint_t tp_; +}; + +class M5StackCoreS3Board : public WifiBoard { +private: + i2c_master_bus_handle_t i2c_bus_; + Axp2101* axp2101_; + Aw9523* aw9523_; + Ft6336* ft6336_; + St7789Display* display_; + Button boot_button_; + + void InitializeI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = (i2c_port_t)1, + .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN, + .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .intr_priority = 0, + .trans_queue_depth = 0, + .flags = { + .enable_internal_pullup = 1, + }, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); + } + + void I2cDetect() { + uint8_t address; + printf(" 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; + esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200)); + if (ret == ESP_OK) { + printf("%02x ", address); + } else if (ret == ESP_ERR_TIMEOUT) { + printf("UU "); + } else { + printf("-- "); + } + } + printf("\r\n"); + } + } + + void InitializeAxp2101() { + ESP_LOGI(TAG, "Init AXP2101"); + axp2101_ = new Axp2101(i2c_bus_, 0x34); + } + + void InitializeAw9523() { + ESP_LOGI(TAG, "Init AW9523"); + aw9523_ = new Aw9523(i2c_bus_, 0x58); + vTaskDelay(pdMS_TO_TICKS(50)); + } + + static void touchpad_daemon(void* param) { + vTaskDelay(pdMS_TO_TICKS(2000)); + auto& board = (M5StackCoreS3Board&)Board::GetInstance(); + auto touchpad = board.GetTouchpad(); + bool was_touched = false; + while (1) { + touchpad->UpdateTouchPoint(); + if (touchpad->GetTouchPoint().num > 0) { + // On press + if (!was_touched) { + was_touched = true; + Application::GetInstance().ToggleChatState(); + } + } + // On release + else if (was_touched) { + was_touched = false; + } + vTaskDelay(pdMS_TO_TICKS(50)); + } + vTaskDelete(NULL); + } + + void InitializeFt6336TouchPad() { + ESP_LOGI(TAG, "Init FT6336"); + ft6336_ = new Ft6336(i2c_bus_, 0x38); + xTaskCreate(touchpad_daemon, "tp", 2048, NULL, 5, NULL); + } + + void InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = GPIO_NUM_37; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = GPIO_NUM_36; + buscfg.quadwp_io_num = GPIO_NUM_NC; + buscfg.quadhd_io_num = GPIO_NUM_NC; + buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t); + ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); + } + + void InitializeIli9342Display() { + ESP_LOGI(TAG, "Init IlI9342"); + + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = GPIO_NUM_3; + io_config.dc_gpio_num = GPIO_NUM_35; + io_config.spi_mode = 2; + io_config.pclk_hz = 40 * 1000 * 1000; + io_config.trans_queue_depth = 10; + io_config.lcd_cmd_bits = 8; + io_config.lcd_param_bits = 8; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io)); + + ESP_LOGD(TAG, "Install LCD driver"); + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = GPIO_NUM_NC; + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel)); + + esp_lcd_panel_reset(panel); + aw9523_->ResetIli9342(); + + esp_lcd_panel_init(panel); + esp_lcd_panel_invert_color(panel, true); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + + display_ = new St7789Display(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + Application::GetInstance().ToggleChatState(); + }); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + } + +public: + M5StackCoreS3Board() : boot_button_(GPIO_NUM_1) { + // M5StackCoreS3Board() { + InitializeI2c(); + InitializeAxp2101(); + InitializeAw9523(); + InitializeFt6336TouchPad(); + I2cDetect(); + InitializeSpi(); + InitializeIli9342Display(); + InitializeButtons(); + InitializeIot(); + } + + virtual Led* GetBuiltinLed() override { + static Led led(GPIO_NUM_NC); + return &led; + } + + virtual AudioCodec* GetAudioCodec() override { + static CoreS3AudioCodec* audio_codec = nullptr; + if (audio_codec == nullptr) { + aw9523_->ResetAw88298(); + audio_codec = new CoreS3AudioCodec(i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, + AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, + AUDIO_CODEC_AW88298_ADDR, AUDIO_CODEC_ES7210_ADDR, AUDIO_INPUT_REFERENCE); + } + return audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } + + Ft6336* GetTouchpad() { + return ft6336_; + } +}; + +DECLARE_BOARD(M5StackCoreS3Board); diff --git a/main/display/st7789_display.cc b/main/display/st7789_display.cc index 37e0c652..1daa67f4 100644 --- a/main/display/st7789_display.cc +++ b/main/display/st7789_display.cc @@ -292,10 +292,19 @@ void St7789Display::SetupUI() { lv_obj_set_width(content_, LV_HOR_RES); lv_obj_set_flex_grow(content_, 1); + lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_COLUMN); // 垂直布局(从上到下) + lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY); // 子对象居中对齐,等距分布 + emotion_label_ = lv_label_create(content_); lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0); lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP); - lv_obj_center(emotion_label_); + // lv_obj_center(emotion_label_); + + chat_message_label_ = lv_label_create(content_); + lv_label_set_text(chat_message_label_, ""); + lv_obj_set_width(chat_message_label_, LV_HOR_RES * 0.8); // 限制宽度为屏幕宽度的 80% + lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_WRAP); // 设置为自动换行模式 + lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0); // 设置文本居中对齐 /* Status bar */ lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); @@ -326,3 +335,10 @@ void St7789Display::SetupUI() { lv_label_set_text(battery_label_, ""); lv_obj_set_style_text_font(battery_label_, &font_awesome_14_1, 0); } + +void St7789Display::SetChatMessage(const std::string &role, const std::string &content) { + if (chat_message_label_ == nullptr) { + return; + } + lv_label_set_text(chat_message_label_, content.c_str()); +} diff --git a/main/display/st7789_display.h b/main/display/st7789_display.h index 604263d3..724bc2c0 100644 --- a/main/display/st7789_display.h +++ b/main/display/st7789_display.h @@ -28,6 +28,7 @@ private: lv_obj_t* content_ = nullptr; lv_obj_t* container_ = nullptr; lv_obj_t* side_bar_ = nullptr; + lv_obj_t* chat_message_label_ = nullptr; void InitializeBacklight(gpio_num_t backlight_pin); void SetBacklight(uint8_t brightness); @@ -42,6 +43,8 @@ public: gpio_num_t backlight_pin, bool backlight_output_invert, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy); ~St7789Display(); + + void SetChatMessage(const std::string &role, const std::string &content) override; }; #endif // ST7789_DISPLAY_H diff --git a/main/idf_component.yml b/main/idf_component.yml index 346960e1..204a1511 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -1,15 +1,16 @@ ## IDF Component Manager Manifest File dependencies: - 78/esp-wifi-connect: "~1.4.1" - 78/esp-opus-encoder: "~2.0.0" - 78/esp-ml307: "~1.7.0" - espressif/led_strip: "^2.4.1" - espressif/esp_codec_dev: "~1.3.2" - espressif/esp-sr: "^1.9.0" - espressif/button: "^3.3.1" - lvgl/lvgl: "~8.4.0" - esp_lvgl_port: "~2.4.1" + 78/esp-wifi-connect: ~1.4.1 + 78/esp-opus-encoder: ~2.0.0 + 78/esp-ml307: ~1.7.0 + espressif/led_strip: ^2.4.1 + espressif/esp_codec_dev: ~1.3.2 + espressif/esp-sr: ^1.9.0 + espressif/button: ^3.3.1 + lvgl/lvgl: ~8.4.0 + esp_lvgl_port: ~2.4.1 ## Required IDF version idf: - version: ">=5.3" + version: '>=5.3' + espressif/esp_lcd_ili9341: ==1.0.0