From 1f803dec1676b954815b26802102772c7639afc8 Mon Sep 17 00:00:00 2001 From: HonestQiao Date: Sat, 8 Mar 2025 02:22:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20DFRobot=20=E8=A1=8C?= =?UTF-8?q?=E7=A9=BA=E6=9D=BF=20K10=20=20(#299)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加DF设备ESP32-S3-K10 添加DFS3设备板子K10 * 完善DFRobot 行空板 K10适配 * Enable K10 RGB LED * Enable K10 SetOutputVolume * 优化K10按键处理 * 添加K10编译配置文件 --------- Co-authored-by: vonweller <154695311+vonweller@users.noreply.github.com> --- main/CMakeLists.txt | 2 + main/Kconfig.projbuild | 2 + main/boards/df-k10/README.md | 37 ++++ main/boards/df-k10/config.h | 45 +++++ main/boards/df-k10/config.json | 11 ++ main/boards/df-k10/df_k10_board.cc | 255 ++++++++++++++++++++++++++ main/boards/df-k10/k10_audio_codec.cc | 226 +++++++++++++++++++++++ main/boards/df-k10/k10_audio_codec.h | 37 ++++ 8 files changed, 615 insertions(+) create mode 100644 main/boards/df-k10/README.md create mode 100644 main/boards/df-k10/config.h create mode 100644 main/boards/df-k10/config.json create mode 100644 main/boards/df-k10/df_k10_board.cc create mode 100644 main/boards/df-k10/k10_audio_codec.cc create mode 100644 main/boards/df-k10/k10_audio_codec.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7430a1ec..e411f671 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -41,6 +41,8 @@ elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307) set(BOARD_TYPE "bread-compact-ml307") elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32) set(BOARD_TYPE "bread-compact-esp32") +elseif(CONFIG_BOARD_TYPE_DF_K10) + set(BOARD_TYPE "df-k10") elseif(CONFIG_BOARD_TYPE_ESP_BOX_3) set(BOARD_TYPE "esp-box-3") elseif(CONFIG_BOARD_TYPE_ESP_BOX) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index b0bfefed..b4b3864f 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -80,6 +80,8 @@ choice BOARD_TYPE bool "立创·实战派ESP32-S3开发板" config BOARD_TYPE_LICHUANG_C3_DEV bool "立创·实战派ESP32-C3开发板" + config BOARD_TYPE_DF_K10 + bool "DFRobot 行空板 k10" config BOARD_TYPE_MAGICLICK_2P4 bool "神奇按钮 Magiclick_2.4" config BOARD_TYPE_MAGICLICK_2P5 diff --git a/main/boards/df-k10/README.md b/main/boards/df-k10/README.md new file mode 100644 index 00000000..ce8b9875 --- /dev/null +++ b/main/boards/df-k10/README.md @@ -0,0 +1,37 @@ +# DFRobot 行空板 K10 + +## 按键配置 +* A:短按-打断/唤醒,长按1s-音量调大 +* B:短按-打断/唤醒,长按1s-音量调小 + +## 编译配置命令 + +**配置编译目标为 ESP32S3:** + +```bash +idf.py set-target esp32s3 +``` + +**打开 menuconfig:** + +```bash +idf.py menuconfig +``` + +**选择板子:** + +``` +Xiaozhi Assistant -> Board Type -> DFRobot 行空板 K10 +``` + +**修改 psram 配置:** + +``` +Component config -> ESP PSRAM -> SPI RAM config -> Mode (QUAD/OCT) -> Quad Mode PSRAM +``` + +**编译:** + +```bash +idf.py build +``` \ No newline at end of file diff --git a/main/boards/df-k10/config.h b/main/boards/df-k10/config.h new file mode 100644 index 00000000..a0eaa648 --- /dev/null +++ b/main/boards/df-k10/config.h @@ -0,0 +1,45 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 24000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_INPUT_REFERENCE true + +#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_3 +#define AUDIO_I2S_GPIO_WS GPIO_NUM_38 +#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_0 +#define AUDIO_I2S_GPIO_DIN GPIO_NUM_39 +#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_45 + +#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC +#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_47 +#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_48 +#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR +#define AUDIO_CODEC_ES7210_ADDR 0x23 + +#define BUILTIN_LED_GPIO GPIO_NUM_46 +#define BOOT_BUTTON_GPIO GPIO_NUM_0 +#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC +#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC + +/* Expander */ +#define DRV_IO_EXP_INPUT_MASK (IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_12) + + +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 320 +#define DISPLAY_MIRROR_X false +#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_NC +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false + + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/df-k10/config.json b/main/boards/df-k10/config.json new file mode 100644 index 00000000..91aef9b7 --- /dev/null +++ b/main/boards/df-k10/config.json @@ -0,0 +1,11 @@ +{ + "target": "esp32s3", + "builds": [ + { + "name": "dfrobot-unihiker-k10", + "sdkconfig_append": [ + "CONFIG_SPIRAM_MODE_OCT=y","CONFIG_SPIRAM_MODE_QUAD=n" + ] + } + ] +} \ No newline at end of file diff --git a/main/boards/df-k10/df_k10_board.cc b/main/boards/df-k10/df_k10_board.cc new file mode 100644 index 00000000..059b85b3 --- /dev/null +++ b/main/boards/df-k10/df_k10_board.cc @@ -0,0 +1,255 @@ +#include "wifi_board.h" +#include "k10_audio_codec.h" +#include "display/lcd_display.h" +#include "esp_lcd_ili9341.h" +#include "font_awesome_symbols.h" +#include "application.h" +#include "button.h" +#include "config.h" +#include "iot/thing_manager.h" +#include "led/circular_strip.h" +#include "assets/lang_config.h" + +#include +#include +#include +#include +#include + +#include "esp_io_expander_tca95xx_16bit.h" + +#define TAG "DF-K10" + +LV_FONT_DECLARE(font_puhui_20_4); +LV_FONT_DECLARE(font_awesome_20_4); + +class Df_K10Board : public WifiBoard { +private: + i2c_master_bus_handle_t i2c_bus_; + esp_io_expander_handle_t io_expander; + LcdDisplay *display_; + button_handle_t btn_a; + button_handle_t btn_b; + + 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 InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = GPIO_NUM_21; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = GPIO_NUM_12; + 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)); + } + + esp_err_t IoExpanderSetLevel(uint16_t pin_mask, uint8_t level) { + return esp_io_expander_set_level(io_expander, pin_mask, level); + } + + uint8_t IoExpanderGetLevel(uint16_t pin_mask) { + uint32_t pin_val = 0; + esp_io_expander_get_level(io_expander, DRV_IO_EXP_INPUT_MASK, &pin_val); + pin_mask &= DRV_IO_EXP_INPUT_MASK; + return (uint8_t)((pin_val & pin_mask) ? 1 : 0); + } + + void InitializeIoExpander() { + esp_io_expander_new_i2c_tca95xx_16bit( + i2c_bus_, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000, &io_expander); + + esp_err_t ret; + ret = esp_io_expander_print_state(io_expander); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Print state failed: %s", esp_err_to_name(ret)); + } + ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0, + IO_EXPANDER_OUTPUT); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Set direction failed: %s", esp_err_to_name(ret)); + } + ret = esp_io_expander_set_level(io_expander, 0, 1); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Set level failed: %s", esp_err_to_name(ret)); + } + ret = esp_io_expander_set_dir( + io_expander, DRV_IO_EXP_INPUT_MASK, + IO_EXPANDER_INPUT); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Set direction failed: %s", esp_err_to_name(ret)); + } + } + void InitializeButtons() { + // Button A + button_config_t btn_a_config = { + .type = BUTTON_TYPE_CUSTOM, + .long_press_time = 1000, + .short_press_time = 50, + .custom_button_config = { + .active_level = 0, + .button_custom_init =nullptr, + .button_custom_get_key_value = [](void *param) -> uint8_t { + auto self = static_cast(param); + return self->IoExpanderGetLevel(IO_EXPANDER_PIN_NUM_2); + }, + .button_custom_deinit = nullptr, + .priv = this, + }, + }; + btn_a = iot_button_create(&btn_a_config); + iot_button_register_cb(btn_a, BUTTON_SINGLE_CLICK, [](void* button_handle, void* usr_data) { + auto self = static_cast(usr_data); + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + self->ResetWifiConfiguration(); + } + app.ToggleChatState(); + }, this); + iot_button_register_cb(btn_a, BUTTON_LONG_PRESS_START, [](void* button_handle, void* usr_data) { + auto self = static_cast(usr_data); + auto codec = self->GetAudioCodec(); + auto volume = codec->output_volume() - 10; + if (volume < 0) { + volume = 0; + } + codec->SetOutputVolume(volume); + self->GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); + }, this); + + // Button B + button_config_t btn_b_config = { + .type = BUTTON_TYPE_CUSTOM, + .long_press_time = 1000, + .short_press_time = 50, + .custom_button_config = { + .active_level = 0, + .button_custom_init =nullptr, + .button_custom_get_key_value = [](void *param) -> uint8_t { + auto self = static_cast(param); + return self->IoExpanderGetLevel(IO_EXPANDER_PIN_NUM_12); + }, + .button_custom_deinit = nullptr, + .priv = this, + }, + }; + btn_b = iot_button_create(&btn_b_config); + iot_button_register_cb(btn_b, BUTTON_SINGLE_CLICK, [](void* button_handle, void* usr_data) { + auto self = static_cast(usr_data); + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + self->ResetWifiConfiguration(); + } + app.ToggleChatState(); + }, this); + iot_button_register_cb(btn_b, BUTTON_LONG_PRESS_START, [](void* button_handle, void* usr_data) { + auto self = static_cast(usr_data); + auto codec = self->GetAudioCodec(); + auto volume = codec->output_volume() + 10; + if (volume > 100) { + volume = 100; + } + codec->SetOutputVolume(volume); + self->GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); + }, this); + } + + 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_14; + io_config.dc_gpio_num = GPIO_NUM_13; + 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"); + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = GPIO_NUM_NC; + panel_config.bits_per_pixel = 16; + panel_config.color_space = ESP_LCD_COLOR_SPACE_BGR; + + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, false)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY)); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y)); + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true)); + + display_ = new SpiLcdDisplay(panel_io, panel, + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, + { + .text_font = &font_puhui_20_4, + .icon_font = &font_awesome_20_4, + .emoji_font = font_emoji_64_init(), + }); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto &thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + } + +public: + Df_K10Board() { + InitializeI2c(); + InitializeIoExpander(); + InitializeSpi(); + InitializeIli9341Display(); + InitializeButtons(); + InitializeIot(); + } + + virtual Led* GetLed() override { + static CircularStrip led(BUILTIN_LED_GPIO, 3); + return &led; + } + + virtual AudioCodec *GetAudioCodec() override { + static K10AudioCodec audio_codec( + 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_PA_PIN, + AUDIO_CODEC_ES8311_ADDR, + AUDIO_CODEC_ES7210_ADDR, + AUDIO_INPUT_REFERENCE); + return &audio_codec; + } + + virtual Display *GetDisplay() override { + return display_; + } +}; + +DECLARE_BOARD(Df_K10Board); \ No newline at end of file diff --git a/main/boards/df-k10/k10_audio_codec.cc b/main/boards/df-k10/k10_audio_codec.cc new file mode 100644 index 00000000..1f6364b9 --- /dev/null +++ b/main/boards/df-k10/k10_audio_codec.cc @@ -0,0 +1,226 @@ +#include "k10_audio_codec.h" + +#include +#include +#include +#include + +static const char TAG[] = "K10AudioCodec"; + +K10AudioCodec::K10AudioCodec(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, + gpio_num_t pa_pin, uint8_t es8311_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); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = I2C_NUM_1, + .addr = es7210_addr, + .bus_handle = i2c_master_handle, + }; + const audio_codec_ctrl_if_t *in_ctrl_if_ = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(in_ctrl_if_ != NULL); + + es7243e_codec_cfg_t es7243e_cfg = { + .ctrl_if = in_ctrl_if_, + }; + const audio_codec_if_t *in_codec_if_ = es7243e_codec_new(&es7243e_cfg); + assert(in_codec_if_ != NULL); + + + esp_codec_dev_cfg_t codec_es7243e_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = in_codec_if_, + .data_if = data_if_, + }; + input_dev_ = esp_codec_dev_new(&codec_es7243e_dev_cfg); + + assert(input_dev_ != NULL); + + ESP_LOGI(TAG, "DF-K10 AudioDevice initialized"); +} + +K10AudioCodec::~K10AudioCodec() { + 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 K10AudioCodec::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_); + + i2s_chan_config_t chan_cfg = { + .id = I2S_NUM_0, + .role = I2S_ROLE_MASTER, + .dma_desc_num = 6, + .dma_frame_num = 240, + .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_MONO, + .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 K10AudioCodec::SetOutputVolume(int volume) { + AudioCodec::SetOutputVolume(volume); +} + +void K10AudioCodec::EnableInput(bool enable) { + if (enable == input_enabled_) { + return; + } + if (enable) { + esp_codec_dev_sample_info_t fs = { + .bits_per_sample = 16, + .channel = 4, + .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 K10AudioCodec::EnableOutput(bool enable) { + if (enable == output_enabled_) { + return; + } + AudioCodec::SetOutputVolume(output_volume_); + AudioCodec::EnableOutput(enable); +} + +int K10AudioCodec::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 K10AudioCodec::Write(const int16_t* data, int samples) { + if (output_enabled_) { + std::vector buffer(samples * 2); // Allocate buffer for 2x samples + + // Apply volume adjustment (same as before) + int32_t volume_factor = pow(double(output_volume_) / 100.0, 2) * 65536; + for (int i = 0; i < samples; i++) { + int64_t temp = int64_t(data[i]) * volume_factor; + if (temp > INT32_MAX) { + buffer[i * 2] = INT32_MAX; + } else if (temp < INT32_MIN) { + buffer[i * 2] = INT32_MIN; + } else { + buffer[i * 2] = static_cast(temp); + } + + // Repeat each sample for slow playback (assuming mono audio) + buffer[i * 2 + 1] = buffer[i * 2]; + } + + size_t bytes_written; + ESP_ERROR_CHECK(i2s_channel_write(tx_handle_, buffer.data(), samples * 2 * sizeof(int32_t), &bytes_written, portMAX_DELAY)); + return bytes_written / sizeof(int32_t); + } + return samples; +} \ No newline at end of file diff --git a/main/boards/df-k10/k10_audio_codec.h b/main/boards/df-k10/k10_audio_codec.h new file mode 100644 index 00000000..061adbe1 --- /dev/null +++ b/main/boards/df-k10/k10_audio_codec.h @@ -0,0 +1,37 @@ +#ifndef _BOX_AUDIO_CODEC_H +#define _BOX_AUDIO_CODEC_H + +#include "audio_codec.h" + +#include +#include + +class K10AudioCodec : 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: + K10AudioCodec(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, + gpio_num_t pa_pin, uint8_t es8311_addr, uint8_t es7210_addr, bool input_reference); + virtual ~K10AudioCodec(); + + virtual void SetOutputVolume(int volume) override; + virtual void EnableInput(bool enable) override; + virtual void EnableOutput(bool enable) override; +}; + +#endif // _BOX_AUDIO_CODEC_H