From 9626b0b71b78b998da1cbd0c50790df9848e4525 Mon Sep 17 00:00:00 2001 From: Forairaaaaa Date: Thu, 26 Dec 2024 12:24:53 +0800 Subject: [PATCH 1/8] add m5stack cores3 support --- main/CMakeLists.txt | 3 + main/Kconfig.projbuild | 2 + main/audio_codecs/cores3_audio_codec.cc | 245 +++++++++++++++ main/audio_codecs/cores3_audio_codec.h | 37 +++ main/boards/common/i2c_device.cc | 4 + main/boards/common/i2c_device.h | 1 + main/boards/m5stack-core-s3/config.h | 42 +++ .../boards/m5stack-core-s3/m5stack_core_s3.cc | 287 ++++++++++++++++++ main/display/st7789_display.cc | 18 +- main/display/st7789_display.h | 3 + main/idf_component.yml | 21 +- 11 files changed, 652 insertions(+), 11 deletions(-) create mode 100644 main/audio_codecs/cores3_audio_codec.cc create mode 100644 main/audio_codecs/cores3_audio_codec.h create mode 100644 main/boards/m5stack-core-s3/config.h create mode 100644 main/boards/m5stack-core-s3/m5stack_core_s3.cc 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 From 6654d6348ee506077adfbb3713d9669cbcceb353 Mon Sep 17 00:00:00 2001 From: Xiaoxia Date: Thu, 26 Dec 2024 13:28:32 +0800 Subject: [PATCH 2/8] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新群号 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d27adb5b..642c0ede 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ 欢迎所有人参与到项目的开发和改进中来。如果你有任何想法或建议,请随时提出 issue 或加入群聊。 -学习交流 QQ 群:946599635 +学习交流 QQ 群:692728912 ## 已实现功能 From 76990c8c0ea05cbcd68c5619038bb08727c79003 Mon Sep 17 00:00:00 2001 From: imliubo Date: Thu, 26 Dec 2024 13:35:31 +0800 Subject: [PATCH 3/8] cleanup code Signed-off-by: imliubo --- main/audio_codecs/cores3_audio_codec.cc | 4 ++-- main/boards/m5stack-core-s3/config.h | 2 ++ .../boards/m5stack-core-s3/m5stack_core_s3.cc | 7 ++---- main/idf_component.yml | 22 +++++++++---------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/main/audio_codecs/cores3_audio_codec.cc b/main/audio_codecs/cores3_audio_codec.cc index a3ba66ab..51c3bbc0 100644 --- a/main/audio_codecs/cores3_audio_codec.cc +++ b/main/audio_codecs/cores3_audio_codec.cc @@ -28,7 +28,7 @@ CoreS3AudioCodec::CoreS3AudioCodec(void* i2c_master_handle, int input_sample_rat data_if_ = audio_codec_new_i2s_data(&i2s_cfg); assert(data_if_ != NULL); - // Output + // Audio Output(Speaker) audio_codec_i2c_cfg_t i2c_cfg = { .port = (i2c_port_t)1, .addr = aw88298_addr, @@ -58,7 +58,7 @@ CoreS3AudioCodec::CoreS3AudioCodec(void* i2c_master_handle, int input_sample_rat output_dev_ = esp_codec_dev_new(&dev_cfg); assert(output_dev_ != NULL); - // Input + // Audio Input(Microphone) i2c_cfg.addr = es7210_addr; in_ctrl_if_ = audio_codec_new_i2c_ctrl(&i2c_cfg); assert(in_ctrl_if_ != NULL); diff --git a/main/boards/m5stack-core-s3/config.h b/main/boards/m5stack-core-s3/config.h index c11449a4..4929afa0 100644 --- a/main/boards/m5stack-core-s3/config.h +++ b/main/boards/m5stack-core-s3/config.h @@ -1,6 +1,8 @@ #ifndef _BOARD_CONFIG_H_ #define _BOARD_CONFIG_H_ +// M5Stack CoreS3 Board configuration + #include #define AUDIO_INPUT_REFERENCE true diff --git a/main/boards/m5stack-core-s3/m5stack_core_s3.cc b/main/boards/m5stack-core-s3/m5stack_core_s3.cc index d47bc7d4..fbcad424 100644 --- a/main/boards/m5stack-core-s3/m5stack_core_s3.cc +++ b/main/boards/m5stack-core-s3/m5stack_core_s3.cc @@ -18,6 +18,7 @@ class Axp2101 : public I2cDevice { public: + // Power Init Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { uint8_t data = ReadReg(0x90); data |= 0b10110100; @@ -26,7 +27,6 @@ public: WriteReg(0x97, (0b11110 - 2)); WriteReg(0x69, 0b00110101); WriteReg(0x30, 0b111111); - WriteReg(0x90, 0xBF); WriteReg(0x94, 33 - 5); WriteReg(0x95, 33 - 5); @@ -35,11 +35,9 @@ public: class Aw9523 : public I2cDevice { public: + // Exanpd IO Init 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 @@ -247,7 +245,6 @@ private: public: M5StackCoreS3Board() : boot_button_(GPIO_NUM_1) { - // M5StackCoreS3Board() { InitializeI2c(); InitializeAxp2101(); InitializeAw9523(); diff --git a/main/idf_component.yml b/main/idf_component.yml index 204a1511..6cbd79a0 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -1,16 +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 + espressif/esp_lcd_ili9341: "==1.0.0" + 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 From 11a3bb3f35460b1a44764677cb00564eb4fdc467 Mon Sep 17 00:00:00 2001 From: whble <512580942@qq.com> Date: Mon, 30 Dec 2024 02:57:34 +0800 Subject: [PATCH 4/8] The repair screen displays ESP_BOX3 --- main/CMakeLists.txt | 1 + main/boards/esp-box-3/config.h | 13 +- main/boards/esp-box-3/esp_box3_board.cc | 82 +++++- main/display/ili9341_display.cc | 374 ++++++++++++++++++++++++ main/display/ili9341_display.h | 51 ++++ main/idf_component.yml | 2 +- 6 files changed, 515 insertions(+), 8 deletions(-) create mode 100644 main/display/ili9341_display.cc create mode 100644 main/display/ili9341_display.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7fbebf69..3d8f10f1 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES "audio_codecs/audio_codec.cc" "display/display.cc" "display/no_display.cc" "display/st7789_display.cc" + "display/ili9341_display.cc" "display/ssd1306_display.cc" "protocols/protocol.cc" "protocols/mqtt_protocol.cc" diff --git a/main/boards/esp-box-3/config.h b/main/boards/esp-box-3/config.h index a1255732..14bddbd3 100644 --- a/main/boards/esp-box-3/config.h +++ b/main/boards/esp-box-3/config.h @@ -26,12 +26,17 @@ #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 128 -#define DISPLAY_HEIGHT 64 +#define DISPLAY_WIDTH 320 +#define DISPLAY_HEIGHT 240 #define DISPLAY_MIRROR_X true #define DISPLAY_MIRROR_Y true +#define DISPLAY_SWAP_XY false + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_47 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false #endif // _BOARD_CONFIG_H_ diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index c74106f0..dcd80c10 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -1,6 +1,6 @@ #include "wifi_board.h" #include "audio_codecs/box_audio_codec.h" -#include "display/no_display.h" +#include "display/ili9341_display.h" #include "application.h" #include "button.h" #include "led.h" @@ -8,14 +8,37 @@ #include "iot/thing_manager.h" #include +#include #include +#include #define TAG "EspBox3Board" +static const ili9341_lcd_init_cmd_t vendor_specific_init[] = { + {0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0}, + {0xC0, (uint8_t []){0x0E, 0x0E}, 2, 0}, + {0xC5, (uint8_t []){0xD0}, 1, 0}, + {0xC1, (uint8_t []){0x02}, 1, 0}, + {0xB4, (uint8_t []){0x02}, 1, 0}, + {0xE0, (uint8_t []){0x00, 0x03, 0x08, 0x06, 0x13, 0x09, 0x39, 0x39, 0x48, 0x02, 0x0a, 0x08, 0x17, 0x17, 0x0F}, 15, 0}, + {0xE1, (uint8_t []){0x00, 0x28, 0x29, 0x01, 0x0d, 0x03, 0x3f, 0x33, 0x52, 0x04, 0x0f, 0x0e, 0x37, 0x38, 0x0F}, 15, 0}, + + {0xB1, (uint8_t []){00, 0x1B}, 2, 0}, + {0x36, (uint8_t []){0x08}, 1, 0}, + {0x3A, (uint8_t []){0x55}, 1, 0}, + {0xB7, (uint8_t []){0x06}, 1, 0}, + + {0x11, (uint8_t []){0}, 0x80, 0}, + {0x29, (uint8_t []){0}, 0x80, 0}, + + {0, (uint8_t []){0}, 0xff, 0}, +}; + class EspBox3Board : public WifiBoard { private: i2c_master_bus_handle_t i2c_bus_; Button boot_button_; + Ili9341Display* display_; void InitializeI2c() { // Initialize I2C peripheral @@ -34,12 +57,64 @@ private: ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); } + void InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = GPIO_NUM_6; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = GPIO_NUM_7; + 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 InitializeButtons() { boot_button_.OnClick([this]() { Application::GetInstance().ToggleChatState(); }); } + void InitializeIli9341Display() { + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + + // 液晶屏控制IO初始化 + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = GPIO_NUM_5; + io_config.dc_gpio_num = GPIO_NUM_4; + io_config.spi_mode = 0; + 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"); + const ili9341_vendor_config_t vendor_config = { + .init_cmds = &vendor_specific_init[0], + .init_cmds_size = sizeof(vendor_specific_init) / sizeof(ili9341_lcd_init_cmd_t), + }; + + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = GPIO_NUM_48; + panel_config.flags.reset_active_high = 1, + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + panel_config.vendor_config = (void *)&vendor_config; + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel)); + + esp_lcd_panel_reset(panel); + 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); + esp_lcd_panel_disp_on_off(panel, true); + display_ = new Ili9341Display(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); + } + // 物联网初始化,添加对 AI 可见设备 void InitializeIot() { auto& thing_manager = iot::ThingManager::GetInstance(); @@ -49,6 +124,8 @@ private: public: EspBox3Board() : boot_button_(BOOT_BUTTON_GPIO) { InitializeI2c(); + InitializeSpi(); + InitializeIli9341Display(); InitializeButtons(); InitializeIot(); } @@ -70,8 +147,7 @@ public: } virtual Display* GetDisplay() override { - static NoDisplay display; - return &display; + return display_; } }; diff --git a/main/display/ili9341_display.cc b/main/display/ili9341_display.cc new file mode 100644 index 00000000..9daa2355 --- /dev/null +++ b/main/display/ili9341_display.cc @@ -0,0 +1,374 @@ +#include "ili9341_display.h" +#include "font_awesome_symbols.h" + +#include +#include +#include +#include + +#define TAG "Ili9341Display" +#define LCD_LEDC_CH LEDC_CHANNEL_0 + +#define ILI9341_LVGL_TICK_PERIOD_MS 2 +#define ILI9341_LVGL_TASK_MAX_DELAY_MS 20 +#define ILI9341_LVGL_TASK_MIN_DELAY_MS 1 +#define ILI9341_LVGL_TASK_STACK_SIZE (4 * 1024) +#define ILI9341_LVGL_TASK_PRIORITY 10 + +LV_FONT_DECLARE(font_puhui_14_1); +LV_FONT_DECLARE(font_awesome_30_1); +LV_FONT_DECLARE(font_awesome_14_1); + +static lv_disp_drv_t disp_drv; + +static void ili9341_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; + int offsetx1 = area->x1; + int offsetx2 = area->x2; + int offsety1 = area->y1; + int offsety2 = area->y2; + // copy a buffer's content to a specific area of the display + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + lv_disp_flush_ready(&disp_drv); +} + +/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */ +static void ili9341_lvgl_port_update_callback(lv_disp_drv_t *drv) +{ + esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; + + switch (drv->rotated) + { + case LV_DISP_ROT_NONE: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, true, false); +#if CONFIG_ILI9341_LCD_TOUCH_ENABLED + // Rotate LCD touch + esp_lcd_touch_set_mirror_y(tp, false); + esp_lcd_touch_set_mirror_x(tp, false); +#endif + break; + case LV_DISP_ROT_90: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, true, true); +#if CONFIG_ILI9341_LCD_TOUCH_ENABLED + // Rotate LCD touch + esp_lcd_touch_set_mirror_y(tp, false); + esp_lcd_touch_set_mirror_x(tp, false); +#endif + break; + case LV_DISP_ROT_180: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, false, true); +#if CONFIG_ILI9341_LCD_TOUCH_ENABLED + // Rotate LCD touch + esp_lcd_touch_set_mirror_y(tp, false); + esp_lcd_touch_set_mirror_x(tp, false); +#endif + break; + case LV_DISP_ROT_270: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, false, false); +#if CONFIG_ILI9341_LCD_TOUCH_ENABLED + // Rotate LCD touch + esp_lcd_touch_set_mirror_y(tp, false); + esp_lcd_touch_set_mirror_x(tp, false); +#endif + break; + } +} + +void Ili9341Display::LvglTask() { + ESP_LOGI(TAG, "Starting LVGL task"); + uint32_t task_delay_ms = ILI9341_LVGL_TASK_MAX_DELAY_MS; + while (1) + { + // Lock the mutex due to the LVGL APIs are not thread-safe + if (Lock()) + { + task_delay_ms = lv_timer_handler(); + Unlock(); + } + if (task_delay_ms > ILI9341_LVGL_TASK_MAX_DELAY_MS) + { + task_delay_ms = ILI9341_LVGL_TASK_MAX_DELAY_MS; + } + else if (task_delay_ms < ILI9341_LVGL_TASK_MIN_DELAY_MS) + { + task_delay_ms = ILI9341_LVGL_TASK_MIN_DELAY_MS; + } + vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); + } +} + +Ili9341Display::Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, + 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) + : panel_io_(panel_io), panel_(panel), backlight_pin_(backlight_pin), backlight_output_invert_(backlight_output_invert), + mirror_x_(mirror_x), mirror_y_(mirror_y), swap_xy_(swap_xy){ + width_ = width; + height_ = height; + offset_x_ = offset_x; + offset_y_ = offset_y; + + + InitializeBacklight(backlight_pin); + + // draw white + std::vector buffer(width_, 0xFFFF); + for (int y = 0; y < height_; y++) { + esp_lcd_panel_draw_bitmap(panel_, 0, y, width_, y + 1, buffer.data()); + } + + // Set the display to on + ESP_LOGI(TAG, "Turning display on"); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); + + ESP_LOGI(TAG, "Initialize LVGL library"); + lv_init(); + // alloc draw buffers used by LVGL + static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s) + // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized + lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA); + assert(buf1); + lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA); + assert(buf2); + // initialize LVGL draw buffers + lv_disp_draw_buf_init(&disp_buf, buf1, buf2, width_ * 10); + + ESP_LOGI(TAG, "Register display driver to LVGL"); + lv_disp_drv_init(&disp_drv); + disp_drv.hor_res = width_; + disp_drv.ver_res = height_; + disp_drv.offset_x = offset_x_; + disp_drv.offset_y = offset_y_; + disp_drv.flush_cb = ili9341_lvgl_flush_cb; + disp_drv.drv_update_cb = ili9341_lvgl_port_update_callback; + disp_drv.draw_buf = &disp_buf; + disp_drv.user_data = panel_; + + lv_disp_drv_register(&disp_drv); + + ESP_LOGI(TAG, "Install LVGL tick timer"); + // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) + const esp_timer_create_args_t lvgl_tick_timer_args = { + .callback = [](void* arg) { + lv_tick_inc(ILI9341_LVGL_TICK_PERIOD_MS); + }, + .arg = NULL, + .dispatch_method = ESP_TIMER_TASK, + .name = "LVGL Tick Timer", + .skip_unhandled_events = false + }; + ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer_)); + ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer_, ILI9341_LVGL_TICK_PERIOD_MS * 1000)); + + lvgl_mutex_ = xSemaphoreCreateRecursiveMutex(); + assert(lvgl_mutex_ != nullptr); + ESP_LOGI(TAG, "Create LVGL task"); + xTaskCreate([](void *arg) { + static_cast(arg)->LvglTask(); + vTaskDelete(NULL); + }, "LVGL", ILI9341_LVGL_TASK_STACK_SIZE, this, ILI9341_LVGL_TASK_PRIORITY, NULL); + + SetBacklight(100); + + SetupUI(); +} + +Ili9341Display::~Ili9341Display() { + ESP_ERROR_CHECK(esp_timer_stop(lvgl_tick_timer_)); + ESP_ERROR_CHECK(esp_timer_delete(lvgl_tick_timer_)); + + if (network_label_ != nullptr) { + lv_obj_del(ai_messge_label_); + lv_obj_del(user_messge_label_); + } + + if (content_ != nullptr) { + lv_obj_del(content_); + } + if (status_bar_ != nullptr) { + lv_obj_del(status_bar_); + } + if (side_bar_ != nullptr) { + lv_obj_del(side_bar_); + } + if (container_ != nullptr) { + lv_obj_del(container_); + } + + if (panel_ != nullptr) { + esp_lcd_panel_del(panel_); + } + if (panel_io_ != nullptr) { + esp_lcd_panel_io_del(panel_io_); + } + vSemaphoreDelete(lvgl_mutex_); +} + +void Ili9341Display::InitializeBacklight(gpio_num_t backlight_pin) { + if (backlight_pin == GPIO_NUM_NC) { + return; + } + + // Setup LEDC peripheral for PWM backlight control + const ledc_channel_config_t backlight_channel = { + .gpio_num = backlight_pin, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LCD_LEDC_CH, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER_0, + .duty = 0, + .hpoint = 0, + .flags = { + .output_invert = backlight_output_invert_, + } + }; + const ledc_timer_config_t backlight_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_num = LEDC_TIMER_0, + .freq_hz = 5000, + .clk_cfg = LEDC_AUTO_CLK, + .deconfigure = false + }; + + ESP_ERROR_CHECK(ledc_timer_config(&backlight_timer)); + ESP_ERROR_CHECK(ledc_channel_config(&backlight_channel)); +} + +void Ili9341Display::SetBacklight(uint8_t brightness) { + if (backlight_pin_ == GPIO_NUM_NC) { + return; + } + + if (brightness > 100) { + brightness = 100; + } + + ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness); + // LEDC resolution set to 10bits, thus: 100% = 1023 + uint32_t duty_cycle = (1023 * brightness) / 100; + ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle)); + ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); +} + +bool Ili9341Display::Lock(int timeout_ms) { + // Convert timeout in milliseconds to FreeRTOS ticks + // If `timeout_ms` is set to 0, the program will block until the condition is met + const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); + return xSemaphoreTakeRecursive(lvgl_mutex_, timeout_ticks) == pdTRUE; +} + +void Ili9341Display::Unlock() { + xSemaphoreGiveRecursive(lvgl_mutex_); +} + +void Ili9341Display::SetChatMessage(const std::string &role, const std::string &content) { + if (ai_messge_label_== nullptr || user_messge_label_== nullptr) { + return; + } + DisplayLockGuard lock(this); + ESP_LOGI(TAG,"role:%s",role.c_str()); + if(role=="assistant") + { + std::string new_content = "AI:" + content; + lv_label_set_text(ai_messge_label_, new_content.c_str()); + } + else if(role=="user") + { + std::string new_content = "User:" + content; + lv_label_set_text(user_messge_label_, new_content.c_str()); + } + else{ + std::string new_content = "AI:"; + lv_label_set_text(ai_messge_label_, new_content.c_str()); + new_content="User:"; + lv_label_set_text(user_messge_label_, new_content.c_str()); + } +} + +void Ili9341Display::SetupUI() { + DisplayLockGuard lock(this); + + auto screen = lv_disp_get_scr_act(lv_disp_get_default()); + lv_obj_set_style_text_font(screen, &font_puhui_14_1, 0); + lv_obj_set_style_text_color(screen, lv_color_black(), 0); + + /* Container */ + container_ = lv_obj_create(screen); + lv_obj_set_size(container_, LV_HOR_RES, LV_VER_RES); + lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_pad_all(container_, 0, 0); + lv_obj_set_style_border_width(container_, 0, 0); + lv_obj_set_style_pad_row(container_, 0, 0); + + /* Status bar */ + status_bar_ = lv_obj_create(container_); + lv_obj_set_size(status_bar_, LV_HOR_RES, 18); + lv_obj_set_style_radius(status_bar_, 0, 0); + + /* Content */ + content_ = lv_obj_create(container_); + lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_style_radius(content_, 0, 0); + lv_obj_set_width(content_, LV_HOR_RES); + lv_obj_set_flex_grow(content_, 1); + + 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_align(emotion_label_,LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位 + + static lv_style_t style_msg; + lv_style_init(&style_msg); + lv_style_set_width(&style_msg, LV_HOR_RES-25); + + user_messge_label_ = lv_label_create(content_); + lv_obj_set_style_text_font(user_messge_label_, &font_puhui_14_1, 0); + lv_label_set_text(user_messge_label_, "User:"); + lv_obj_add_style(user_messge_label_, &style_msg, 0); + lv_obj_align(user_messge_label_,LV_ALIGN_TOP_LEFT, 2, 25); + + ai_messge_label_ = lv_label_create(content_); + lv_obj_set_style_text_font(ai_messge_label_, &font_puhui_14_1, 0); + lv_label_set_text(ai_messge_label_, "AI:"); + lv_obj_add_style(ai_messge_label_, &style_msg, 0); + lv_obj_align(ai_messge_label_,LV_ALIGN_TOP_LEFT, 2, 77); + + /* Status bar */ + lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); + lv_obj_set_style_pad_all(status_bar_, 0, 0); + lv_obj_set_style_border_width(status_bar_, 0, 0); + lv_obj_set_style_pad_column(status_bar_, 0, 0); + + network_label_ = lv_label_create(status_bar_); + lv_label_set_text(network_label_, ""); + lv_obj_set_style_text_font(network_label_, &font_awesome_14_1, 0); + + notification_label_ = lv_label_create(status_bar_); + lv_obj_set_flex_grow(notification_label_, 1); + lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); + lv_label_set_text(notification_label_, "通知"); + lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); + + status_label_ = lv_label_create(status_bar_); + lv_obj_set_flex_grow(status_label_, 1); + lv_label_set_text(status_label_, "正在初始化"); + lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); + + mute_label_ = lv_label_create(status_bar_); + lv_label_set_text(mute_label_, ""); + lv_obj_set_style_text_font(mute_label_, &font_awesome_14_1, 0); + + battery_label_ = lv_label_create(status_bar_); + lv_label_set_text(battery_label_, ""); + lv_obj_set_style_text_font(battery_label_, &font_awesome_14_1, 0); +} \ No newline at end of file diff --git a/main/display/ili9341_display.h b/main/display/ili9341_display.h new file mode 100644 index 00000000..9beb08b8 --- /dev/null +++ b/main/display/ili9341_display.h @@ -0,0 +1,51 @@ +#ifndef ILI9341_DISPLAY_H +#define ILI9341_DISPLAY_H + +#include "display.h" +#include "esp_lcd_ili9341.h" +#include +#include +#include +#include +#include +#include + +class Ili9341Display : public Display { +private: + esp_lcd_panel_io_handle_t panel_io_ = nullptr; + esp_lcd_panel_handle_t panel_ = nullptr; + gpio_num_t backlight_pin_ = GPIO_NUM_NC; + bool backlight_output_invert_ = false; + bool mirror_x_ = false; + bool mirror_y_ = false; + bool swap_xy_ = false; + int offset_x_ = 0; + int offset_y_ = 0; + SemaphoreHandle_t lvgl_mutex_ = nullptr; + esp_timer_handle_t lvgl_tick_timer_ = nullptr; + + lv_obj_t* status_bar_ = nullptr; + lv_obj_t* content_ = nullptr; + lv_obj_t* container_ = nullptr; + lv_obj_t* side_bar_ = nullptr; + + lv_obj_t *user_messge_label_ = nullptr; + lv_obj_t *ai_messge_label_ = nullptr; + + void InitializeBacklight(gpio_num_t backlight_pin); + void SetBacklight(uint8_t brightness); + void SetupUI(); + void LvglTask(); + + virtual bool Lock(int timeout_ms = 0) override; + virtual void Unlock() override; + virtual void SetChatMessage(const std::string &role, const std::string &content) override; + +public: + Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, + 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); + ~Ili9341Display(); +}; + +#endif // Ili9341_DISPLAY_H diff --git a/main/idf_component.yml b/main/idf_component.yml index 6cbd79a0..45049fdd 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -1,6 +1,6 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_lcd_ili9341: "==1.0.0" + espressif/esp_lcd_ili9341: "==1.2.0" 78/esp-wifi-connect: "~1.4.1" 78/esp-opus-encoder: "~2.0.0" 78/esp-ml307: "~1.7.0" From f98be270002fa545e18250118ce9174d4427673f Mon Sep 17 00:00:00 2001 From: whble <512580942@qq.com> Date: Wed, 1 Jan 2025 22:43:20 +0800 Subject: [PATCH 5/8] fix display for esp32 box3 --- main/CMakeLists.txt | 1 - main/boards/esp-box-3/esp_box3_board.cc | 94 +++++- main/display/ili9341_display.cc | 374 ------------------------ main/display/ili9341_display.h | 51 ---- main/display/st7789_display.h | 4 +- 5 files changed, 95 insertions(+), 429 deletions(-) delete mode 100644 main/display/ili9341_display.cc delete mode 100644 main/display/ili9341_display.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 3d8f10f1..7fbebf69 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -6,7 +6,6 @@ set(SOURCES "audio_codecs/audio_codec.cc" "display/display.cc" "display/no_display.cc" "display/st7789_display.cc" - "display/ili9341_display.cc" "display/ssd1306_display.cc" "protocols/protocol.cc" "protocols/mqtt_protocol.cc" diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index dcd80c10..f1fa0fb4 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -1,6 +1,8 @@ #include "wifi_board.h" #include "audio_codecs/box_audio_codec.h" -#include "display/ili9341_display.h" +#include "display/st7789_display.h" +#include "esp_lcd_ili9341.h" +#include "font_awesome_symbols.h" #include "application.h" #include "button.h" #include "led.h" @@ -14,6 +16,12 @@ #define TAG "EspBox3Board" +// Can move to display/st7789_display.h +LV_FONT_DECLARE(font_puhui_14_1); +LV_FONT_DECLARE(font_awesome_30_1); +LV_FONT_DECLARE(font_awesome_14_1); + +// Init ili9341 by custom cmd static const ili9341_lcd_init_cmd_t vendor_specific_init[] = { {0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0}, {0xC0, (uint8_t []){0x0E, 0x0E}, 2, 0}, @@ -34,6 +42,82 @@ static const ili9341_lcd_init_cmd_t vendor_specific_init[] = { {0, (uint8_t []){0}, 0xff, 0}, }; +// Example Display and UI overwrite in different board +class Ili9341Display : public St7789Display { +private: + lv_obj_t *user_messge_label_ = nullptr; + lv_obj_t *ai_messge_label_ = nullptr; +public: + Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, + 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(panel_io, panel, backlight_pin, backlight_output_invert, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {} + + void SetStatus(const std::string &status) override + { + if (status_label_ == nullptr) { + return; + } + if(status=="待命") + { + SetChatMessage(" "," "); + } + DisplayLockGuard lock(this); + lv_label_set_text(status_label_, status.c_str()); + } + + void SetChatMessage(const std::string &role, const std::string &content) override { + if (ai_messge_label_== nullptr || user_messge_label_== nullptr) { + return; + } + DisplayLockGuard lock(this); + ESP_LOGI(TAG,"role:%s",role.c_str()); + if(role=="assistant") + { + std::string new_content = "AI:" + content; + lv_label_set_text(ai_messge_label_, new_content.c_str()); + } + else if(role=="user") + { + std::string new_content = "User:" + content; + lv_label_set_text(user_messge_label_, new_content.c_str()); + } + else{ + std::string new_content = "AI:"; + lv_label_set_text(ai_messge_label_, new_content.c_str()); + new_content="User:"; + lv_label_set_text(user_messge_label_, new_content.c_str()); + } + } + void SetupUI() override { + DisplayLockGuard lock(this); + + lv_obj_del(chat_message_label_); + + lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY); // 子对象居中对齐,等距分布 + + 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_align(emotion_label_,LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位 + + static lv_style_t style_msg; + lv_style_init(&style_msg); + lv_style_set_width(&style_msg, LV_HOR_RES-25); + + user_messge_label_ = lv_label_create(content_); + lv_obj_set_style_text_font(user_messge_label_, &font_puhui_14_1, 0); + lv_label_set_text(user_messge_label_, "User:"); + lv_obj_add_style(user_messge_label_, &style_msg, 0); + lv_obj_align(user_messge_label_,LV_ALIGN_TOP_LEFT, 2, 25); + + ai_messge_label_ = lv_label_create(content_); + lv_obj_set_style_text_font(ai_messge_label_, &font_puhui_14_1, 0); + lv_label_set_text(ai_messge_label_, "AI:"); + lv_obj_add_style(ai_messge_label_, &style_msg, 0); + lv_obj_align(ai_messge_label_,LV_ALIGN_TOP_LEFT, 2, 77); + } +}; + class EspBox3Board : public WifiBoard { private: i2c_master_bus_handle_t i2c_bus_; @@ -113,6 +197,7 @@ private: esp_lcd_panel_disp_on_off(panel, true); display_ = new Ili9341Display(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); + display_->SetupUI(); } // 物联网初始化,添加对 AI 可见设备 @@ -149,6 +234,13 @@ public: virtual Display* GetDisplay() override { return display_; } + + virtual bool GetBatteryLevel(int &level, bool& charging) override + { + charging = false; + level =60; + return true; + }; }; DECLARE_BOARD(EspBox3Board); diff --git a/main/display/ili9341_display.cc b/main/display/ili9341_display.cc deleted file mode 100644 index 9daa2355..00000000 --- a/main/display/ili9341_display.cc +++ /dev/null @@ -1,374 +0,0 @@ -#include "ili9341_display.h" -#include "font_awesome_symbols.h" - -#include -#include -#include -#include - -#define TAG "Ili9341Display" -#define LCD_LEDC_CH LEDC_CHANNEL_0 - -#define ILI9341_LVGL_TICK_PERIOD_MS 2 -#define ILI9341_LVGL_TASK_MAX_DELAY_MS 20 -#define ILI9341_LVGL_TASK_MIN_DELAY_MS 1 -#define ILI9341_LVGL_TASK_STACK_SIZE (4 * 1024) -#define ILI9341_LVGL_TASK_PRIORITY 10 - -LV_FONT_DECLARE(font_puhui_14_1); -LV_FONT_DECLARE(font_awesome_30_1); -LV_FONT_DECLARE(font_awesome_14_1); - -static lv_disp_drv_t disp_drv; - -static void ili9341_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; - int offsetx1 = area->x1; - int offsetx2 = area->x2; - int offsety1 = area->y1; - int offsety2 = area->y2; - // copy a buffer's content to a specific area of the display - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - lv_disp_flush_ready(&disp_drv); -} - -/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */ -static void ili9341_lvgl_port_update_callback(lv_disp_drv_t *drv) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; - - switch (drv->rotated) - { - case LV_DISP_ROT_NONE: - // Rotate LCD display - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, true, false); -#if CONFIG_ILI9341_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif - break; - case LV_DISP_ROT_90: - // Rotate LCD display - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, true, true); -#if CONFIG_ILI9341_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif - break; - case LV_DISP_ROT_180: - // Rotate LCD display - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, false, true); -#if CONFIG_ILI9341_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif - break; - case LV_DISP_ROT_270: - // Rotate LCD display - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, false, false); -#if CONFIG_ILI9341_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif - break; - } -} - -void Ili9341Display::LvglTask() { - ESP_LOGI(TAG, "Starting LVGL task"); - uint32_t task_delay_ms = ILI9341_LVGL_TASK_MAX_DELAY_MS; - while (1) - { - // Lock the mutex due to the LVGL APIs are not thread-safe - if (Lock()) - { - task_delay_ms = lv_timer_handler(); - Unlock(); - } - if (task_delay_ms > ILI9341_LVGL_TASK_MAX_DELAY_MS) - { - task_delay_ms = ILI9341_LVGL_TASK_MAX_DELAY_MS; - } - else if (task_delay_ms < ILI9341_LVGL_TASK_MIN_DELAY_MS) - { - task_delay_ms = ILI9341_LVGL_TASK_MIN_DELAY_MS; - } - vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); - } -} - -Ili9341Display::Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, - 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) - : panel_io_(panel_io), panel_(panel), backlight_pin_(backlight_pin), backlight_output_invert_(backlight_output_invert), - mirror_x_(mirror_x), mirror_y_(mirror_y), swap_xy_(swap_xy){ - width_ = width; - height_ = height; - offset_x_ = offset_x; - offset_y_ = offset_y; - - - InitializeBacklight(backlight_pin); - - // draw white - std::vector buffer(width_, 0xFFFF); - for (int y = 0; y < height_; y++) { - esp_lcd_panel_draw_bitmap(panel_, 0, y, width_, y + 1, buffer.data()); - } - - // Set the display to on - ESP_LOGI(TAG, "Turning display on"); - ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); - - ESP_LOGI(TAG, "Initialize LVGL library"); - lv_init(); - // alloc draw buffers used by LVGL - static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s) - // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized - lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA); - assert(buf1); - lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA); - assert(buf2); - // initialize LVGL draw buffers - lv_disp_draw_buf_init(&disp_buf, buf1, buf2, width_ * 10); - - ESP_LOGI(TAG, "Register display driver to LVGL"); - lv_disp_drv_init(&disp_drv); - disp_drv.hor_res = width_; - disp_drv.ver_res = height_; - disp_drv.offset_x = offset_x_; - disp_drv.offset_y = offset_y_; - disp_drv.flush_cb = ili9341_lvgl_flush_cb; - disp_drv.drv_update_cb = ili9341_lvgl_port_update_callback; - disp_drv.draw_buf = &disp_buf; - disp_drv.user_data = panel_; - - lv_disp_drv_register(&disp_drv); - - ESP_LOGI(TAG, "Install LVGL tick timer"); - // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) - const esp_timer_create_args_t lvgl_tick_timer_args = { - .callback = [](void* arg) { - lv_tick_inc(ILI9341_LVGL_TICK_PERIOD_MS); - }, - .arg = NULL, - .dispatch_method = ESP_TIMER_TASK, - .name = "LVGL Tick Timer", - .skip_unhandled_events = false - }; - ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer_)); - ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer_, ILI9341_LVGL_TICK_PERIOD_MS * 1000)); - - lvgl_mutex_ = xSemaphoreCreateRecursiveMutex(); - assert(lvgl_mutex_ != nullptr); - ESP_LOGI(TAG, "Create LVGL task"); - xTaskCreate([](void *arg) { - static_cast(arg)->LvglTask(); - vTaskDelete(NULL); - }, "LVGL", ILI9341_LVGL_TASK_STACK_SIZE, this, ILI9341_LVGL_TASK_PRIORITY, NULL); - - SetBacklight(100); - - SetupUI(); -} - -Ili9341Display::~Ili9341Display() { - ESP_ERROR_CHECK(esp_timer_stop(lvgl_tick_timer_)); - ESP_ERROR_CHECK(esp_timer_delete(lvgl_tick_timer_)); - - if (network_label_ != nullptr) { - lv_obj_del(ai_messge_label_); - lv_obj_del(user_messge_label_); - } - - if (content_ != nullptr) { - lv_obj_del(content_); - } - if (status_bar_ != nullptr) { - lv_obj_del(status_bar_); - } - if (side_bar_ != nullptr) { - lv_obj_del(side_bar_); - } - if (container_ != nullptr) { - lv_obj_del(container_); - } - - if (panel_ != nullptr) { - esp_lcd_panel_del(panel_); - } - if (panel_io_ != nullptr) { - esp_lcd_panel_io_del(panel_io_); - } - vSemaphoreDelete(lvgl_mutex_); -} - -void Ili9341Display::InitializeBacklight(gpio_num_t backlight_pin) { - if (backlight_pin == GPIO_NUM_NC) { - return; - } - - // Setup LEDC peripheral for PWM backlight control - const ledc_channel_config_t backlight_channel = { - .gpio_num = backlight_pin, - .speed_mode = LEDC_LOW_SPEED_MODE, - .channel = LCD_LEDC_CH, - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = LEDC_TIMER_0, - .duty = 0, - .hpoint = 0, - .flags = { - .output_invert = backlight_output_invert_, - } - }; - const ledc_timer_config_t backlight_timer = { - .speed_mode = LEDC_LOW_SPEED_MODE, - .duty_resolution = LEDC_TIMER_10_BIT, - .timer_num = LEDC_TIMER_0, - .freq_hz = 5000, - .clk_cfg = LEDC_AUTO_CLK, - .deconfigure = false - }; - - ESP_ERROR_CHECK(ledc_timer_config(&backlight_timer)); - ESP_ERROR_CHECK(ledc_channel_config(&backlight_channel)); -} - -void Ili9341Display::SetBacklight(uint8_t brightness) { - if (backlight_pin_ == GPIO_NUM_NC) { - return; - } - - if (brightness > 100) { - brightness = 100; - } - - ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness); - // LEDC resolution set to 10bits, thus: 100% = 1023 - uint32_t duty_cycle = (1023 * brightness) / 100; - ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle)); - ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); -} - -bool Ili9341Display::Lock(int timeout_ms) { - // Convert timeout in milliseconds to FreeRTOS ticks - // If `timeout_ms` is set to 0, the program will block until the condition is met - const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); - return xSemaphoreTakeRecursive(lvgl_mutex_, timeout_ticks) == pdTRUE; -} - -void Ili9341Display::Unlock() { - xSemaphoreGiveRecursive(lvgl_mutex_); -} - -void Ili9341Display::SetChatMessage(const std::string &role, const std::string &content) { - if (ai_messge_label_== nullptr || user_messge_label_== nullptr) { - return; - } - DisplayLockGuard lock(this); - ESP_LOGI(TAG,"role:%s",role.c_str()); - if(role=="assistant") - { - std::string new_content = "AI:" + content; - lv_label_set_text(ai_messge_label_, new_content.c_str()); - } - else if(role=="user") - { - std::string new_content = "User:" + content; - lv_label_set_text(user_messge_label_, new_content.c_str()); - } - else{ - std::string new_content = "AI:"; - lv_label_set_text(ai_messge_label_, new_content.c_str()); - new_content="User:"; - lv_label_set_text(user_messge_label_, new_content.c_str()); - } -} - -void Ili9341Display::SetupUI() { - DisplayLockGuard lock(this); - - auto screen = lv_disp_get_scr_act(lv_disp_get_default()); - lv_obj_set_style_text_font(screen, &font_puhui_14_1, 0); - lv_obj_set_style_text_color(screen, lv_color_black(), 0); - - /* Container */ - container_ = lv_obj_create(screen); - lv_obj_set_size(container_, LV_HOR_RES, LV_VER_RES); - lv_obj_set_flex_flow(container_, LV_FLEX_FLOW_COLUMN); - lv_obj_set_style_pad_all(container_, 0, 0); - lv_obj_set_style_border_width(container_, 0, 0); - lv_obj_set_style_pad_row(container_, 0, 0); - - /* Status bar */ - status_bar_ = lv_obj_create(container_); - lv_obj_set_size(status_bar_, LV_HOR_RES, 18); - lv_obj_set_style_radius(status_bar_, 0, 0); - - /* Content */ - content_ = lv_obj_create(container_); - lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); - lv_obj_set_style_radius(content_, 0, 0); - lv_obj_set_width(content_, LV_HOR_RES); - lv_obj_set_flex_grow(content_, 1); - - 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_align(emotion_label_,LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位 - - static lv_style_t style_msg; - lv_style_init(&style_msg); - lv_style_set_width(&style_msg, LV_HOR_RES-25); - - user_messge_label_ = lv_label_create(content_); - lv_obj_set_style_text_font(user_messge_label_, &font_puhui_14_1, 0); - lv_label_set_text(user_messge_label_, "User:"); - lv_obj_add_style(user_messge_label_, &style_msg, 0); - lv_obj_align(user_messge_label_,LV_ALIGN_TOP_LEFT, 2, 25); - - ai_messge_label_ = lv_label_create(content_); - lv_obj_set_style_text_font(ai_messge_label_, &font_puhui_14_1, 0); - lv_label_set_text(ai_messge_label_, "AI:"); - lv_obj_add_style(ai_messge_label_, &style_msg, 0); - lv_obj_align(ai_messge_label_,LV_ALIGN_TOP_LEFT, 2, 77); - - /* Status bar */ - lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); - lv_obj_set_style_pad_all(status_bar_, 0, 0); - lv_obj_set_style_border_width(status_bar_, 0, 0); - lv_obj_set_style_pad_column(status_bar_, 0, 0); - - network_label_ = lv_label_create(status_bar_); - lv_label_set_text(network_label_, ""); - lv_obj_set_style_text_font(network_label_, &font_awesome_14_1, 0); - - notification_label_ = lv_label_create(status_bar_); - lv_obj_set_flex_grow(notification_label_, 1); - lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(notification_label_, "通知"); - lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); - - status_label_ = lv_label_create(status_bar_); - lv_obj_set_flex_grow(status_label_, 1); - lv_label_set_text(status_label_, "正在初始化"); - lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); - - mute_label_ = lv_label_create(status_bar_); - lv_label_set_text(mute_label_, ""); - lv_obj_set_style_text_font(mute_label_, &font_awesome_14_1, 0); - - battery_label_ = lv_label_create(status_bar_); - lv_label_set_text(battery_label_, ""); - lv_obj_set_style_text_font(battery_label_, &font_awesome_14_1, 0); -} \ No newline at end of file diff --git a/main/display/ili9341_display.h b/main/display/ili9341_display.h deleted file mode 100644 index 9beb08b8..00000000 --- a/main/display/ili9341_display.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef ILI9341_DISPLAY_H -#define ILI9341_DISPLAY_H - -#include "display.h" -#include "esp_lcd_ili9341.h" -#include -#include -#include -#include -#include -#include - -class Ili9341Display : public Display { -private: - esp_lcd_panel_io_handle_t panel_io_ = nullptr; - esp_lcd_panel_handle_t panel_ = nullptr; - gpio_num_t backlight_pin_ = GPIO_NUM_NC; - bool backlight_output_invert_ = false; - bool mirror_x_ = false; - bool mirror_y_ = false; - bool swap_xy_ = false; - int offset_x_ = 0; - int offset_y_ = 0; - SemaphoreHandle_t lvgl_mutex_ = nullptr; - esp_timer_handle_t lvgl_tick_timer_ = nullptr; - - lv_obj_t* status_bar_ = nullptr; - lv_obj_t* content_ = nullptr; - lv_obj_t* container_ = nullptr; - lv_obj_t* side_bar_ = nullptr; - - lv_obj_t *user_messge_label_ = nullptr; - lv_obj_t *ai_messge_label_ = nullptr; - - void InitializeBacklight(gpio_num_t backlight_pin); - void SetBacklight(uint8_t brightness); - void SetupUI(); - void LvglTask(); - - virtual bool Lock(int timeout_ms = 0) override; - virtual void Unlock() override; - virtual void SetChatMessage(const std::string &role, const std::string &content) override; - -public: - Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, - 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); - ~Ili9341Display(); -}; - -#endif // Ili9341_DISPLAY_H diff --git a/main/display/st7789_display.h b/main/display/st7789_display.h index 724bc2c0..e053e344 100644 --- a/main/display/st7789_display.h +++ b/main/display/st7789_display.h @@ -11,7 +11,7 @@ #include class St7789Display : public Display { -private: +protected: esp_lcd_panel_io_handle_t panel_io_ = nullptr; esp_lcd_panel_handle_t panel_ = nullptr; gpio_num_t backlight_pin_ = GPIO_NUM_NC; @@ -32,9 +32,9 @@ private: void InitializeBacklight(gpio_num_t backlight_pin); void SetBacklight(uint8_t brightness); - void SetupUI(); void LvglTask(); + virtual void SetupUI(); virtual bool Lock(int timeout_ms = 0) override; virtual void Unlock() override; From 498fd08cc85a86411f7336ca4912ab708da8d721 Mon Sep 17 00:00:00 2001 From: Xiaoxia Date: Thu, 2 Jan 2025 17:42:07 +0800 Subject: [PATCH 6/8] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新 QQ 群号码 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 642c0ede..d27adb5b 100755 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ 欢迎所有人参与到项目的开发和改进中来。如果你有任何想法或建议,请随时提出 issue 或加入群聊。 -学习交流 QQ 群:692728912 +学习交流 QQ 群:946599635 ## 已实现功能 From ded4f40e3423aeff6eee69dcd80d2f8f45256e43 Mon Sep 17 00:00:00 2001 From: Xiaoxia Date: Fri, 3 Jan 2025 00:50:58 +0800 Subject: [PATCH 7/8] Update esp_box3_board.cc fix spacing --- main/boards/esp-box-3/esp_box3_board.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index f1fa0fb4..7df36c19 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -238,7 +238,7 @@ public: virtual bool GetBatteryLevel(int &level, bool& charging) override { charging = false; - level =60; + level = 60; return true; }; }; From ad5f68773467a11659bb3e4d26c6bb4d1ebdf66c Mon Sep 17 00:00:00 2001 From: Xiaoxia Date: Fri, 3 Jan 2025 01:02:58 +0800 Subject: [PATCH 8/8] Update README.md Add star history --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d27adb5b..15b423a2 100755 --- a/README.md +++ b/README.md @@ -63,3 +63,12 @@ 详细的使用说明以及测试服的注意事项,请参考 [小智测试服的帮助说明](https://xiaozhi.me/help)。 +## Star History + + + + + + Star History Chart + +