diff --git a/main/APP/main_app/main_app.c b/main/APP/main_app/main_app.c index 32daacb..f0422de 100644 --- a/main/APP/main_app/main_app.c +++ b/main/APP/main_app/main_app.c @@ -37,6 +37,7 @@ #include "imp_msg_queue.h" #include "ext_trans_service.h" #include "display_service.h" +#include "audio_service.h" #include #include "imp_out_port.h" @@ -109,6 +110,9 @@ uint8_t _main_task_wake_up_services() xTaskCreate(imp_display_service_task, imp_main_task_table[IMP_TASK_ID_DISP_SERVICE_TASK], 8192, NULL, 11, NULL); + xTaskCreate(imp_audio_service_task, + imp_main_task_table[IMP_TASK_ID_AUDIO_SERVICE_TASK], 2048, NULL, + 12, NULL); return 0; } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d04294d..b7e1e1e 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -16,6 +16,8 @@ idf_component_register( "services/display_service" "components/screen_pages/test_page" "components/imp_esp_i2c_master_dev_op" + "services/audio_service" + "components/Codec/ES8388" EXCLUDE_SRCS "utilities/imp_util_ring_queue/ring_queue_test.c" @@ -38,13 +40,16 @@ idf_component_register( "services/display_service" "components/screen_pages/test_page" "components/imp_esp_i2c_master_dev_op" + "services/audio_service" + "components/Codec/ES8388" + "components/Codec" LDFRAGMENTS "utilities/letter_shell/port/esp-idf/shell.lf" ) -# 添加源文件 +# 添加源文件进行强制编译 set(SOURCE_FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/services/display_service/display_service.c diff --git a/main/components/Codec/ES8388/es8388.c b/main/components/Codec/ES8388/es8388.c new file mode 100644 index 0000000..14e0ec2 --- /dev/null +++ b/main/components/Codec/ES8388/es8388.c @@ -0,0 +1,668 @@ +/** + * @file es8388.c + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-22 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-22 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +/* Includes ------------------------------------------------------------------*/ +#include "es8388.h" +#include "imp_codec.h" +#include +#include +#include +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ + +/* variables -----------------------------------------------------------------*/ + +static imp_es8388_cfg_t handle = { 0 }; +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +void es8388_read_all() +{ + if (!handle.is_init) { + return; + } + for (int i = 0; i < 50; i++) { + uint8_t reg = 0; + handle.read_reg(i, ®); + ESP_LOGI("ES8388", "%x: %x", i, reg); + } +} + +int es8388_write_reg(uint8_t reg_add, uint8_t data) +{ + if (!handle.is_init) { + return 1; + } + return handle.write_reg(reg_add, data); +} + +/** + * @brief Configure ES8388 ADC and DAC volume. Basicly you can consider this as ADC and DAC gain + * + * @param mode: set ADC or DAC or all + * @param volume: -96 ~ 0 for example Es8388SetAdcDacVolume(ES_MODULE_ADC, 30, 6); means set ADC volume -30.5db + * @param dot: whether include 0.5. for example Es8388SetAdcDacVolume(ES_MODULE_ADC, 30, 4); means set ADC volume -30db + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +static int es8388_set_adc_dac_volume(int mode, int volume, int dot) +{ + if (!handle.is_init) { + return 1; + } + int res = 0; + if (volume < -96 || volume > 0) { + ESP_LOGW("ES8388", "Warning: volume < -96! or > 0!\n"); + if (volume < -96) + volume = -96; + else + volume = 0; + } + dot = (dot >= 5 ? 1 : 0); + volume = (-volume << 1) + dot; + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res |= handle.write_reg(ES8388_ADCCONTROL8, volume); + res |= handle.write_reg(ES8388_ADCCONTROL9, + volume); //ADC Right Volume=0db + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res |= handle.write_reg(ES8388_DACCONTROL5, volume); + res |= handle.write_reg(ES8388_DACCONTROL4, volume); + } + return res; +} + +/** + * @brief Power Management + * + * @param mod: if ES_POWER_CHIP, the whole chip including ADC and DAC is enabled + * @param enable: false to disable true to enable + * + * @return + * - (-1) Error + * - (0) Success + */ +int es8388_start(es_module_t mode) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t prev_data = 0, data = 0; + handle.read_reg(ES8388_DACCONTROL21, &prev_data); + if (mode == ES_MODULE_LINE) { + // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 by pass enable + res |= handle.write_reg(ES8388_DACCONTROL16, 0x09); + // left DAC to left mixer enable and LIN signal to left mixer enable 0db : bupass enable + res |= handle.write_reg(ES8388_DACCONTROL17, 0x50); + // right DAC to right mixer enable and LIN signal to right mixer enable 0db : bupass enable + res |= handle.write_reg(ES8388_DACCONTROL20, 0x50); + res |= handle.write_reg(ES8388_DACCONTROL21, 0xC0); //enable adc + } else { + res |= handle.write_reg(ES8388_DACCONTROL21, 0x80); //enable dac + } + handle.read_reg(ES8388_DACCONTROL21, &data); + if (prev_data != data) { + //start state machine + res |= handle.write_reg(ES8388_CHIPPOWER, 0xF0); + // res |= handle.write_reg(ES8388_CONTROL1, 0x16); + // res |= handle.write_reg(ES8388_CONTROL2, 0x50); + //start state machine + res |= handle.write_reg(ES8388_CHIPPOWER, 0x00); + } + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC + || mode == ES_MODULE_LINE) { + //power up adc and line in + res |= handle.write_reg(ES8388_ADCPOWER, 0x00); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC + || mode == ES_MODULE_LINE) { + //power up dac and line out + res |= handle.write_reg(ES8388_DACPOWER, 0x3c); + res |= es8388_set_voice_mute(false); + ESP_LOGD("ES8388", "es8388_start default is mode:%d", mode); + } + + return res; +} + +/** + * @brief Power Management + * + * @param mod: if ES_POWER_CHIP, the whole chip including ADC and DAC is enabled + * @param enable: false to disable true to enable + * + * @return + * - (-1) Error + * - (0) Success + */ +int es8388_stop(es_module_t mode) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + if (mode == ES_MODULE_LINE) { + //enable dac + res |= handle.write_reg(ES8388_DACCONTROL21, 0x80); + // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 + res |= handle.write_reg(ES8388_DACCONTROL16, 0x00); + // only left DAC to left mixer enable 0db + res |= handle.write_reg(ES8388_DACCONTROL17, 0x90); + // only right DAC to right mixer enable 0db + res |= handle.write_reg(ES8388_DACCONTROL20, 0x90); + return res; + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res |= handle.write_reg(ES8388_DACPOWER, 0x00); + res |= es8388_set_voice_mute( + true); //res |= Es8388SetAdcDacVolume(ES_MODULE_DAC, -96, 5); // 0db + //res |= handle.write_reg(ES8388_DACPOWER, 0xC0); //power down dac and line out + } + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + //res |= Es8388SetAdcDacVolume(ES_MODULE_ADC, -96, 5); // 0db + res |= handle.write_reg(ES8388_ADCPOWER, + 0xFF); //power down adc and line in + } + if (mode == ES_MODULE_ADC_DAC) { + //disable mclk + res |= handle.write_reg(ES8388_DACCONTROL21, 0x9C); + // res |= handle.write_reg(ES8388_CONTROL1, 0x00); + // res |= handle.write_reg(ES8388_CONTROL2, 0x58); + // res |= handle.write_reg(ES8388_CHIPPOWER, 0xF3); //stop state machine + } + + return res; +} + +/** + * @brief Config I2s clock in MSATER mode + * + * @param cfg.sclkDiv: generate SCLK by dividing MCLK in MSATER mode + * @param cfg.lclkDiv: generate LCLK by dividing MCLK in MSATER mode + * + * @return + * - (-1) Error + * - (0) Success + */ +int es8388_i2s_config_clock(es_i2s_clock_t cfg) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + res |= handle.write_reg(ES8388_MASTERMODE, cfg.sclk_div); + res |= handle.write_reg(ES8388_ADCCONTROL5, + cfg.lclk_div); //ADCFsMode,singel SPEED,RATIO=256 + res |= handle.write_reg(ES8388_DACCONTROL2, + cfg.lclk_div); //ADCFsMode,singel SPEED,RATIO=256 + return res; +} + +int es8388_deinit(void) +{ + if (!handle.is_init) { + return 1; + } + int res = 0; + res = handle.write_reg(ES8388_CHIPPOWER, + 0xFF); //reset and stop es8388 + // i2c_bus_delete(i2c_handle); +#ifdef CONFIG_ESP_LYRAT_V4_3_BOARD + headphone_detect_deinit(); +#endif + + // audio_codec_volume_deinit(dac_vol_handle); + return res; +} + +/** + * @return + * - (-1) Error + * - (0) Success + */ +static int es8388_init_regs(imp_es8388_cfg_t* cfg) +{ + if (!handle.is_init) { + return 1; + } + int res = 0; +#ifdef CONFIG_ESP_LYRAT_V4_3_BOARD + headphone_detect_init(get_headphone_detect_gpio()); +#endif + ESP_LOGI("ES8388", "run %s at line %d", __FUNCTION__, __LINE__); + + res = 0; // ESP32 in master mode + + // 0x04 mute/0x00 unmute&ramp;DAC unmute and disabled digital volume control soft ramp + res |= handle.write_reg(ES8388_DACCONTROL3, 0x04); + /* Chip Control and Power Management */ + res |= handle.write_reg(ES8388_CONTROL2, 0x50); + //normal all and power up all + res |= handle.write_reg(ES8388_CHIPPOWER, 0x00); + + // Disable the internal DLL to improve 8K sample rate + res |= handle.write_reg(0x35, 0xA0); + res |= handle.write_reg(0x37, 0xD0); + res |= handle.write_reg(0x39, 0xD0); + + //CODEC IN I2S SLAVE MODE TODO + res |= handle.write_reg(ES8388_MASTERMODE, cfg->mode); + + /* dac */ + //disable DAC and disable Lout/Rout/1/2 + res |= handle.write_reg(ES8388_DACPOWER, 0xC0); + //Enfr=0,Play&Record Mode,(0x17-both of mic&paly) + res |= handle.write_reg(ES8388_CONTROL1, 0x12); + // res |= handle.write_reg(ES8388_CONTROL2, 0); //LPVrefBuf=0,Pdn_ana=0 + //1a 0x18:16bit iis , 0x00:24 + res |= handle.write_reg(ES8388_DACCONTROL1, 0x18); + //DACFsMode,SINGLE SPEED; DACFsRatio,256 + res |= handle.write_reg(ES8388_DACCONTROL2, 0x02); + // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 + res |= handle.write_reg(ES8388_DACCONTROL16, 0x00); + // only left DAC to left mixer enable 0db + res |= handle.write_reg(ES8388_DACCONTROL17, 0x90); + // only right DAC to right mixer enable 0db + res |= handle.write_reg(ES8388_DACCONTROL20, 0x90); + // set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK + res |= handle.write_reg(ES8388_DACCONTROL21, 0x80); + res |= handle.write_reg(ES8388_DACCONTROL23, 0x00); // vroi=0 + + // Set L1 R1 L2 R2 volume. 0x00: -30dB, 0x1E: 0dB, 0x21: 3dB + res |= handle.write_reg(ES8388_DACCONTROL24, 0x1E); + res |= handle.write_reg(ES8388_DACCONTROL25, 0x1E); + res |= handle.write_reg(ES8388_DACCONTROL26, 0); + res |= handle.write_reg(ES8388_DACCONTROL27, 0); + // res |= es8388_set_adc_dac_volume(ES_MODULE_DAC, 0, 0); // 0db + int tmp = 0; + if (EM_IMP_ES8388_LINE_OUT_1 == cfg->dac_output) { + tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_ROUT1; + } else if (EM_IMP_ES8388_LINE_OUT_2 == cfg->dac_output) { + tmp = DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT2; + } else { + tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 + | DAC_OUTPUT_ROUT2; + } + + //0x3c Enable DAC and Enable Lout/Rout/1/2 + res |= handle.write_reg(ES8388_DACPOWER, tmp); + /* adc */ + res |= handle.write_reg(ES8388_ADCPOWER, 0xFF); + // MIC Left and Right channel PGA gain + res |= handle.write_reg(ES8388_ADCCONTROL1, 0xbb); + tmp = 0; + if (EM_IMP_ES8388_LINE_IN_1 == cfg->adc_input) { + tmp = ADC_INPUT_LINPUT1_RINPUT1; + } else if (EM_IMP_ES8388_LINE_IN_2 == cfg->adc_input) { + tmp = ADC_INPUT_LINPUT2_RINPUT2; + } else { + tmp = ADC_INPUT_DIFFERENCE; + } + + //0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1 + res |= handle.write_reg(ES8388_ADCCONTROL2, tmp); + res |= handle.write_reg(ES8388_ADCCONTROL3, 0x02); + // 16 Bits length and I2S serial audio data format + res |= handle.write_reg(ES8388_ADCCONTROL4, 0x0c); + //ADCFsMode,singel SPEED,RATIO=256 + res |= handle.write_reg(ES8388_ADCCONTROL5, 0x02); + //ALC for Microphone + res |= es8388_set_adc_dac_volume(ES_MODULE_ADC, 0, 0); // 0db + // Power on ADC, enable LIN&RIN, power off MICBIAS, and set int1lp to low power mode + res |= handle.write_reg(ES8388_ADCPOWER, 0x09); + + /* es8388 PA gpio_config */ + // gpio_config_t io_conf; + // memset(&io_conf, 0, sizeof(io_conf)); + // io_conf.mode = GPIO_MODE_OUTPUT; + // io_conf.pin_bit_mask = BIT64(get_pa_enable_gpio()); + // io_conf.pull_down_en = 0; + // io_conf.pull_up_en = 0; + // gpio_config(&io_conf); + // /* enable es8388 PA */ + // es8388_pa_power(true); + + // codec_dac_volume_config_t vol_cfg = ES8388_DAC_VOL_CFG_DEFAULT(); + // dac_vol_handle = audio_codec_volume_init(&vol_cfg); + ESP_LOGI("ES8388", "init,out:%02x, in:%02x", cfg->dac_output, + cfg->adc_input); + return res; +} + +/** + * @brief Configure ES8388 I2S format + * + * @param mode: set ADC or DAC or all + * @param bitPerSample: see Es8388I2sFmt + * + * @return + * - (-1) Error + * - (0) Success + */ +int es8388_config_fmt(es_module_t mode, es_i2s_fmt_t fmt) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t reg = 0; + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res = handle.read_reg(ES8388_ADCCONTROL4, ®); + reg = reg & 0xfc; + res |= handle.write_reg(ES8388_ADCCONTROL4, reg | fmt); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res = handle.read_reg(ES8388_DACCONTROL1, ®); + reg = reg & 0xf9; + res |= handle.write_reg(ES8388_DACCONTROL1, reg | (fmt << 1)); + } + return res; +} + +/** + * @brief Set voice volume + * + * @note Register values. 0xC0: -96 dB, 0x64: -50 dB, 0x00: 0 dB + * @note Accuracy of gain is 0.5 dB + * + * @param volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_set_voice_volume(int volume) +{ + if (!handle.is_init) { + return 1; + } + + // (min/mute)4 - 100(max) + if (volume < 4) { + volume = 4; + } + if (volume > 100) { + volume = 100; + } + + // 0 - 96 + volume = volume - 4; + + int res = ESP_OK; + uint8_t reg = 0; + reg = 192 - (volume * 2); // TODO 转换配置 + res |= handle.write_reg(ES8388_DACCONTROL5, reg); + res |= handle.write_reg(ES8388_DACCONTROL4, reg); + ESP_LOGD("ES8388", "Set volume:%.2d reg_value:0x%.2x dB:%.1f", (int)volume, + reg, 0); + return res; +} + +int es8388_get_voice_volume(int* volume) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t reg = 0; + res = handle.read_reg(ES8388_DACCONTROL4, ®); + + // TODO: 转换reg到值 + *volume = (reg - 192) / 2; + + // if (res == ESP_FAIL) { + // *volume = 0; + // } else { + // if (reg == dac_vol_handle->reg_value) { + // *volume = dac_vol_handle->user_volume; + // } else { + // *volume = 0; + // res = ESP_FAIL; + // } + // } + ESP_LOGD("ES8388", "Get volume:%.2d reg_value:0x%.2x", *volume, reg); + return res; +} + +/** + * @brief Configure ES8388 data sample bits + * + * @param mode: set ADC or DAC or all + * @param bitPerSample: see BitsLength + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +int es8388_set_bits_per_sample(es_module_t mode, es_bits_length_t bits_length) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t reg = 0; + int bits = (int)bits_length; + + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + res = handle.read_reg(ES8388_ADCCONTROL4, ®); + reg = reg & 0xe3; + res |= handle.write_reg(ES8388_ADCCONTROL4, reg | (bits << 2)); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + res = handle.read_reg(ES8388_DACCONTROL1, ®); + reg = reg & 0xc7; + res |= handle.write_reg(ES8388_DACCONTROL1, reg | (bits << 3)); + } + return res; +} + +/** + * @brief Configure ES8388 DAC mute or not. Basically you can use this function to mute the output or unmute + * + * @param enable: enable or disable + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +int es8388_set_voice_mute(bool enable) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t reg = 0; + res = handle.read_reg(ES8388_DACCONTROL3, ®); + reg = reg & 0xFB; + res |= handle.write_reg(ES8388_DACCONTROL3, reg | (((int)enable) << 2)); + return res; +} + +int es8388_get_voice_mute(void) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + uint8_t reg = 0; + res = handle.read_reg(ES8388_DACCONTROL3, ®); + if (res == ESP_OK) { + reg = (reg & 0x04) >> 2; + } + return res == ESP_OK ? reg : res; +} + +/** + * @param gain: Config DAC Output + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +int es8388_config_dac_output(es_dac_output_t output) +{ + if (!handle.is_init) { + return 1; + } + int res; + uint8_t reg = 0; + res = handle.read_reg(ES8388_DACPOWER, ®); + reg = reg & 0xc3; + res |= handle.write_reg(ES8388_DACPOWER, reg | output); + return res; +} + +/** + * @param gain: Config ADC input + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +int es8388_config_adc_input(es_adc_input_t input) +{ + if (!handle.is_init) { + return 1; + } + int res; + uint8_t reg = 0; + res = handle.read_reg(ES8388_ADCCONTROL2, ®); + reg = reg & 0x0f; + res |= handle.write_reg(ES8388_ADCCONTROL2, reg | input); + return res; +} + +/** + * @param gain: see es_mic_gain_t + * + * @return + * - (-1) Parameter error + * - (0) Success + */ +int es8388_set_mic_gain(es_mic_gain_t gain) +{ + if (!handle.is_init) { + return 1; + } + int res, gain_n; + gain_n = (int)gain / 3; + gain_n = (gain_n << 4) + gain_n; + res = handle.write_reg(ES8388_ADCCONTROL1, gain_n); //MIC PGA + return res; +} + +int es8388_ctrl_state(codec_work_mode_t mode, uint8_t start_flag) +{ + if (!handle.is_init) { + return 1; + } + int res = 0; + int es_mode_t = 0; + switch (mode) { + case EM_IMP_CODEC_DEV_WORK_MODE_ADC: + es_mode_t = ES_MODULE_ADC; + break; + case EM_IMP_CODEC_DEV_WORK_MODE_LINE: + es_mode_t = ES_MODULE_LINE; + break; + case EM_IMP_CODEC_DEV_WORK_MODE_DAC: + es_mode_t = ES_MODULE_DAC; + break; + case EM_IMP_CODEC_DEV_WORK_MODE_BOTH: + es_mode_t = ES_MODULE_ADC_DAC; + break; + default: + es_mode_t = ES_MODULE_DAC; + ESP_LOGW("ES8388", + "Codec mode not support, default is decode mode"); + break; + } + if (start_flag) { + res = es8388_start(es_mode_t); + ESP_LOGD("ES8388", "start default is decode mode:%d", es_mode_t); + } else { + res = es8388_stop(es_mode_t); + } + return res; +} + +int es8388_config_i2s(es_i2s_fmt_t fmt, es_bits_length_t bit) +{ + if (!handle.is_init) { + return 1; + } + int res = ESP_OK; + int tmp = 0; + res |= es8388_config_fmt(handle.mode, fmt); + if (bit == BIT_LENGTH_16BITS) { + tmp = BIT_LENGTH_16BITS; + } else if (bit == BIT_LENGTH_24BITS) { + tmp = BIT_LENGTH_24BITS; + } else { + tmp = BIT_LENGTH_32BITS; + } + res |= es8388_set_bits_per_sample(handle.mode, tmp); + return res; +} + +int es8388_pa_power(bool enable) +{ + if (!handle.is_init) { + return 1; + } + + int res = ESP_OK; + // if (enable) { + // res = gpio_set_level(get_pa_enable_gpio(), 1); + // } else { + // res = gpio_set_level(get_pa_enable_gpio(), 0); + // } + return res; +} + +int es8388_init(es_mode_t mode, imp_es_8388_io_port_e dac_output, + imp_es_8388_io_port_e adc_input, + uint8_t (*read_reg)(uint8_t reg_addr, uint8_t* reg_data), + uint8_t (*write_reg)(uint8_t reg_addr, uint8_t reg_data)) +{ + ESP_LOGI("ES8388", "run %s at line %d", __FUNCTION__, __LINE__); + if (NULL == read_reg || NULL == write_reg) { + return 1; + } + ESP_LOGI("ES8388", "run %s at line %d", __FUNCTION__, __LINE__); + + handle.mode = mode; + handle.dac_output = dac_output; + handle.adc_input = adc_input; + handle.read_reg = read_reg; + handle.write_reg = write_reg; + handle.is_init = 1; + es8388_init_regs(&handle); + return 0; +} + +/* + * EOF + */ \ No newline at end of file diff --git a/main/components/Codec/ES8388/es8388.h b/main/components/Codec/ES8388/es8388.h new file mode 100644 index 0000000..cf543cd --- /dev/null +++ b/main/components/Codec/ES8388/es8388.h @@ -0,0 +1,611 @@ +/** + * @file es8388.h + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-22 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-22 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __ES8388_H__ +#define __ES8388_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include "imp_codec.h" +/* define --------------------------------------------------------------------*/ +/* ES8388 register */ + +/* chip control and power */ + +#define ES8388_CONTROL1 0x00 +#define ES8388_CONTROL1_DEFAULT_VALUE 0x06 +/// @brief reset all reg data to default +#define ES8388_CONTROL1_SET_ALL_RESET(origin, value) \ + ((origin) & 0x7F | (value) << 7) & 0xFF; + +#define ES8388_CONTROL2 0x01 +#define ES8388_CONTROL2_DEFAULT_VALUE 0x5C +/// @brief set internal bias? +#define ES8388_CONTROL2_SET_PDN_IBIAS_GEN(origin, value) \ + ((origin) & 0xFD | (value) << 1) & 0xFF; + +#define ES8388_CHIPPOWER 0x02 +#define ES8388_CHIPPOWER_DEFAULT_VALUE 0xC3 + +#define ES8388_ADCPOWER 0x03 +#define ES8388_ADCPOWER_DEFAULT_VALUE 0xFC + +#define ES8388_DACPOWER 0x04 +#define ES8388_DACPOWER_DEFAULT_VALUE 0xC0 + +#define ES8388_CHIPLOPOW1 0x05 +#define ES8388_CHIPLOPOW1_DEFAULT_VALUE 0x00 + +#define ES8388_CHIPLOPOW2 0x06 +#define ES8388_CHIPLOPOW2_DEFAULT_VALUE 0x00 + +#define ES8388_ANAVOLMANAG 0x07 +#define ES8388_ANAVOLMANAG_DEFAULT_VALUE 0x7C + +#define ES8388_MASTERMODE 0x08 +#define ES8388_MASTERMODE_DEFAULT_VALUE 0x80 + +/* ADC */ + +#define ES8388_ADCCONTROL1 0x09 +#define ES8388_ADCCONTROL1_DEFAULT_VALUE 0x00 + +#define ES8388_ADCCONTROL2 0x0a +#define ES8388_ADCCONTROL2_DEFAULT_VALUE 0x00 + +#define ES8388_ADCCONTROL3 0x0b +#define ES8388_ADCCONTROL3_DEFAULT_VALUE 0x00 + +#define ES8388_ADCCONTROL4 0x0c +#define ES8388_ADCCONTROL4_DEFAULT_VALUE 0x06 + +#define ES8388_ADCCONTROL5 0x0d +#define ES8388_ADCCONTROL5_DEFAULT_VALUE 0x30 + +#define ES8388_ADCCONTROL6 0x0e +#define ES8388_ADCCONTROL6_DEFAULT_VALUE 0x20 + +#define ES8388_ADCCONTROL7 0x0f +#define ES8388_ADCCONTROL7_DEFAULT_VALUE 0x20 + +#define ES8388_ADCCONTROL8 0x10 +#define ES8388_ADCCONTROL8_DEFAULT_VALUE 0xC0 + +#define ES8388_ADCCONTROL9 0x11 +#define ES8388_ADCCONTROL9_DEFAULT_VALUE 0xC0 + +#define ES8388_ADCCONTROL10 0x12 +#define ES8388_ADCCONTROL10_DEFAULT_VALUE 0x38 + +#define ES8388_ADCCONTROL11 0x13 +#define ES8388_ADCCONTROL11_DEFAULT_VALUE 0xB0 + +#define ES8388_ADCCONTROL12 0x14 +#define ES8388_ADCCONTROL12_DEFAULT_VALUE 0x32 + +#define ES8388_ADCCONTROL13 0x15 +#define ES8388_ADCCONTROL13_DEFAULT_VALUE 0x06 + +#define ES8388_ADCCONTROL14 0x16 +#define ES8388_ADCCONTROL14_DEFAULT_VALUE 0x00 + +/* DAC */ + +#define ES8388_DACCONTROL1 0x17 +#define ES8388_DACCONTROL1_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL2 0x18 +#define ES8388_DACCONTROL2_DEFAULT_VALUE 0x06 + +#define ES8388_DACCONTROL3 0x19 +#define ES8388_DACCONTROL3_DEFAULT_VALUE 0x22 + +#define ES8388_DACCONTROL4 0x1a +#define ES8388_DACCONTROL4_DEFAULT_VALUE 0xC0 + +#define ES8388_DACCONTROL5 0x1b +#define ES8388_DACCONTROL5_DEFAULT_VALUE 0xC0 + +#define ES8388_DACCONTROL6 0x1c +#define ES8388_DACCONTROL6_DEFAULT_VALUE 0x08 + +#define ES8388_DACCONTROL7 0x1d +#define ES8388_DACCONTROL7_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL8 0x1e +#define ES8388_DACCONTROL8_DEFAULT_VALUE 0x1F + +#define ES8388_DACCONTROL9 0x1f +#define ES8388_DACCONTROL9_DEFAULT_VALUE 0xF7 + +#define ES8388_DACCONTROL10 0x20 +#define ES8388_DACCONTROL10_DEFAULT_VALUE 0xFD + +#define ES8388_DACCONTROL11 0x21 +#define ES8388_DACCONTROL11_DEFAULT_VALUE 0xFF + +#define ES8388_DACCONTROL12 0x22 +#define ES8388_DACCONTROL12_DEFAULT_VALUE 0x1F + +#define ES8388_DACCONTROL13 0x23 +#define ES8388_DACCONTROL13_DEFAULT_VALUE 0xF7 + +#define ES8388_DACCONTROL14 0x24 +#define ES8388_DACCONTROL14_DEFAULT_VALUE 0xFD + +#define ES8388_DACCONTROL15 0x25 +#define ES8388_DACCONTROL15_DEFAULT_VALUE 0xFF + +#define ES8388_DACCONTROL16 0x26 +#define ES8388_DACCONTROL16_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL17 0x27 +#define ES8388_DACCONTROL17_DEFAULT_VALUE 0x38 + +#define ES8388_DACCONTROL18 0x28 +#define ES8388_DACCONTROL18_DEFAULT_VALUE 0x28 + +#define ES8388_DACCONTROL19 0x29 +#define ES8388_DACCONTROL19_DEFAULT_VALUE 0x28 + +#define ES8388_DACCONTROL20 0x2a +#define ES8388_DACCONTROL20_DEFAULT_VALUE 0x38 + +#define ES8388_DACCONTROL21 0x2b +#define ES8388_DACCONTROL21_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL22 0x2c +#define ES8388_DACCONTROL22_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL23 0x2d +#define ES8388_DACCONTROL23_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL24 0x2e +#define ES8388_DACCONTROL24_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL25 0x2f +#define ES8388_DACCONTROL25_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL26 0x30 +#define ES8388_DACCONTROL26_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL27 0x31 +#define ES8388_DACCONTROL27_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL28 0x32 +#define ES8388_DACCONTROL28_DEFAULT_VALUE 0x00 + +#define ES8388_DACCONTROL29 0x33 +#define ES8388_DACCONTROL29_DEFAULT_VALUE 0xAA + +#define ES8388_DACCONTROL30 0x34 +#define ES8388_DACCONTROL30_DEFAULT_VALUE 0xAA +/* typedef -------------------------------------------------------------------*/ + +#define CODEC_MEM_CHECK(ptr) \ + if (ptr == NULL) { \ + ESP_LOGE("ES8388", "Fail to alloc memory at %s:%d", __FUNCTION__, \ + __LINE__); \ + } + +#define BITS(n) (1 << n) + +#define MCLK_DEFAULT_DIV (256) + +typedef enum +{ + BIT_LENGTH_MIN = -1, + BIT_LENGTH_16BITS = 0x03, + BIT_LENGTH_18BITS = 0x02, + BIT_LENGTH_20BITS = 0x01, + BIT_LENGTH_24BITS = 0x00, + BIT_LENGTH_32BITS = 0x04, + BIT_LENGTH_MAX, +} es_bits_length_t; + +typedef enum +{ + MCLK_DIV_MIN = -1, + MCLK_DIV_1 = 1, + MCLK_DIV_2 = 2, + MCLK_DIV_3 = 3, + MCLK_DIV_4 = 4, + MCLK_DIV_6 = 5, + MCLK_DIV_8 = 6, + MCLK_DIV_9 = 7, + MCLK_DIV_11 = 8, + MCLK_DIV_12 = 9, + MCLK_DIV_16 = 10, + MCLK_DIV_18 = 11, + MCLK_DIV_22 = 12, + MCLK_DIV_24 = 13, + MCLK_DIV_33 = 14, + MCLK_DIV_36 = 15, + MCLK_DIV_44 = 16, + MCLK_DIV_48 = 17, + MCLK_DIV_66 = 18, + MCLK_DIV_72 = 19, + MCLK_DIV_5 = 20, + MCLK_DIV_10 = 21, + MCLK_DIV_15 = 22, + MCLK_DIV_17 = 23, + MCLK_DIV_20 = 24, + MCLK_DIV_25 = 25, + MCLK_DIV_30 = 26, + MCLK_DIV_32 = 27, + MCLK_DIV_34 = 28, + MCLK_DIV_7 = 29, + MCLK_DIV_13 = 30, + MCLK_DIV_14 = 31, + MCLK_DIV_MAX, +} es_sclk_div_t; + +typedef enum +{ + LCLK_DIV_MIN = -1, + LCLK_DIV_128 = 0, + LCLK_DIV_192 = 1, + LCLK_DIV_256 = 2, + LCLK_DIV_384 = 3, + LCLK_DIV_512 = 4, + LCLK_DIV_576 = 5, + LCLK_DIV_768 = 6, + LCLK_DIV_1024 = 7, + LCLK_DIV_1152 = 8, + LCLK_DIV_1408 = 9, + LCLK_DIV_1536 = 10, + LCLK_DIV_2112 = 11, + LCLK_DIV_2304 = 12, + + LCLK_DIV_125 = 16, + LCLK_DIV_136 = 17, + LCLK_DIV_250 = 18, + LCLK_DIV_272 = 19, + LCLK_DIV_375 = 20, + LCLK_DIV_500 = 21, + LCLK_DIV_544 = 22, + LCLK_DIV_750 = 23, + LCLK_DIV_1000 = 24, + LCLK_DIV_1088 = 25, + LCLK_DIV_1496 = 26, + LCLK_DIV_1500 = 27, + LCLK_DIV_MAX, +} es_lclk_div_t; + +typedef enum +{ + D2SE_PGA_GAIN_MIN = -1, + D2SE_PGA_GAIN_DIS = 0, + D2SE_PGA_GAIN_EN = 1, + D2SE_PGA_GAIN_MAX = 2, +} es_d2se_pga_t; + +typedef enum +{ + ADC_INPUT_MIN = -1, + ADC_INPUT_LINPUT1_RINPUT1 = 0x00, + ADC_INPUT_MIC1 = 0x05, + ADC_INPUT_MIC2 = 0x06, + ADC_INPUT_LINPUT2_RINPUT2 = 0x50, + ADC_INPUT_DIFFERENCE = 0xf0, + ADC_INPUT_MAX, +} es_adc_input_t; + +typedef enum +{ + DAC_OUTPUT_MIN = -1, + DAC_OUTPUT_LOUT1 = 0x04, + DAC_OUTPUT_LOUT2 = 0x08, + DAC_OUTPUT_SPK = 0x09, + DAC_OUTPUT_ROUT1 = 0x10, + DAC_OUTPUT_ROUT2 = 0x20, + DAC_OUTPUT_ALL = 0x3c, + DAC_OUTPUT_MAX, +} es_dac_output_t; + +typedef enum +{ + MIC_GAIN_MIN = -1, + MIC_GAIN_0DB = 0, + MIC_GAIN_3DB = 3, + MIC_GAIN_6DB = 6, + MIC_GAIN_9DB = 9, + MIC_GAIN_12DB = 12, + MIC_GAIN_15DB = 15, + MIC_GAIN_18DB = 18, + MIC_GAIN_21DB = 21, + MIC_GAIN_24DB = 24, + MIC_GAIN_MAX, +} es_mic_gain_t; + +typedef enum +{ + ES_MODULE_MIN = -1, + ES_MODULE_ADC = 0x01, + ES_MODULE_DAC = 0x02, + ES_MODULE_ADC_DAC = 0x03, + ES_MODULE_LINE = 0x04, + ES_MODULE_MAX +} es_module_t; + +typedef enum +{ + ES_MODE_MIN = -1, + ES_MODE_SLAVE = 0x00, + ES_MODE_MASTER = 0x01, + ES_MODE_MAX, +} es_mode_t; + +typedef enum +{ + ES_I2S_MIN = -1, + ES_I2S_NORMAL = 0, + ES_I2S_LEFT = 1, + ES_I2S_RIGHT = 2, + ES_I2S_DSP = 3, + ES_I2S_MAX +} es_i2s_fmt_t; + +typedef struct +{ + es_sclk_div_t sclk_div; /*!< bits clock divide */ + es_lclk_div_t lclk_div; /*!< WS clock divide */ +} es_i2s_clock_t; + +typedef enum +{ + ES_PA_SETUP = 1, + ES_PA_ENABLE = (1 << 1), + ES_PA_DISABLE = (1 << 2), +} es_pa_setting_t; + +typedef enum __imp_es_8388_io_port_e__ +{ + EM_IMP_ES8388_LINE_IN_1 = 0x00, + EM_IMP_ES8388_LINE_IN_2, + EM_IMP_ES8388_LINE_IN_ALL, + EM_IMP_ES8388_LINE_OUT_1, + EM_IMP_ES8388_LINE_OUT_2, +} imp_es_8388_io_port_e; + +typedef struct __imp_es8388_cfg_t__ +{ + uint8_t is_init; + es_mode_t mode; + imp_es_8388_io_port_e dac_output; + imp_es_8388_io_port_e adc_input; + es_i2s_fmt_t fmt; + es_bits_length_t bit; + uint8_t (*read_reg)(uint8_t reg_addr, uint8_t* reg_data); + uint8_t (*write_reg)(uint8_t reg_addr, uint8_t reg_data); +} imp_es8388_cfg_t; +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ +/** + * @brief Initialize ES8388 codec chip + * + * @param cfg configuration of ES8388 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_init(es_mode_t mode, imp_es_8388_io_port_e dac_output, + imp_es_8388_io_port_e adc_input, + uint8_t (*read_reg)(uint8_t reg_addr, uint8_t* reg_data), + uint8_t (*write_reg)(uint8_t reg_addr, uint8_t reg_data)); + +/** + * @brief Deinitialize ES8388 codec chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_deinit(void); + +/** + * @brief Configure ES8388 I2S format + * + * @param mod: set ADC or DAC or both + * @param cfg: ES8388 I2S format + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_config_fmt(es_module_t mod, es_i2s_fmt_t cfg); + +/** + * @brief Configure I2s clock in MSATER mode + * + * @param cfg: set bits clock and WS clock + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_i2s_config_clock(es_i2s_clock_t cfg); + +/** + * @brief Configure ES8388 data sample bits + * + * @param mode: set ADC or DAC or both + * @param bit_per_sample: bit number of per sample + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_set_bits_per_sample(es_module_t mode, + es_bits_length_t bit_per_sample); + +/** + * @brief Start ES8388 codec chip + * + * @param mode: set ADC or DAC or both + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_start(es_module_t mode); + +/** + * @brief Stop ES8388 codec chip + * + * @param mode: set ADC or DAC or both + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_stop(es_module_t mode); + +/** + * @brief Set voice volume + * + * @param volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_set_voice_volume(int volume); + +/** + * @brief Get voice volume + * + * @param[out] *volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +int es8388_get_voice_volume(int* volume); + +/** + * @brief Configure ES8388 DAC mute or not. Basically you can use this function to mute the output or unmute + * + * @param enable enable(1) or disable(0) + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_set_voice_mute(bool enable); + +/** + * @brief Get ES8388 DAC mute status + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_get_voice_mute(void); + +/** + * @brief Set ES8388 mic gain + * + * @param gain db of mic gain + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_set_mic_gain(es_mic_gain_t gain); + +/** + * @brief Set ES8388 adc input mode + * + * @param input adc input mode + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_config_adc_input(es_adc_input_t input); + +/** + * @brief Set ES8388 dac output mode + * + * @param output dac output mode + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_config_dac_output(es_dac_output_t output); + +/** + * @brief Print all ES8388 registers + */ +void es8388_read_all(void); + +/** + * @brief Configure ES8388 codec mode and I2S interface + * + * @param fmt codec fmt + * @param bit I2S bit + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_config_i2s(es_i2s_fmt_t fmt, es_bits_length_t bit); + +/** + * @brief Control ES8388 codec chip + * + * @param mode codec mode + * @param start_flag start or stop decode or encode progress + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +int es8388_ctrl_state(codec_work_mode_t mode, uint8_t start_flag); + +/** + * @brief Set ES8388 PA power + * + * @param enable true for enable PA power, false for disable PA power + * + * @return + * - ESP_ERR_INVALID_ARG + * - ESP_OK + */ +int es8388_pa_power(bool enable); + +#ifdef __cplusplus +} +#endif +#endif //__ES8388_H__ + /* + * EOF + */ \ No newline at end of file diff --git a/main/components/Codec/README.md b/main/components/Codec/README.md new file mode 100644 index 0000000..e69de29 diff --git a/main/components/Codec/imp_codec.h b/main/components/Codec/imp_codec.h new file mode 100644 index 0000000..1851128 --- /dev/null +++ b/main/components/Codec/imp_codec.h @@ -0,0 +1,67 @@ +/** + * @file imp_codec.h + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-25 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-25 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __IMP_CODEC_H__ +#define __IMP_CODEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +/* define --------------------------------------------------------------------*/ +#define ESP_CODEC_DEV_OK (0) +#define ESP_CODEC_DEV_DRV_ERR (ESP_FAIL) +#define ESP_CODEC_DEV_INVALID_ARG (ESP_ERR_INVALID_ARG) +#define ESP_CODEC_DEV_NO_MEM (ESP_ERR_NO_MEM) +#define ESP_CODEC_DEV_NOT_SUPPORT (ESP_ERR_NOT_SUPPORTED) +#define ESP_CODEC_DEV_NOT_FOUND (ESP_ERR_NOT_FOUND) +#define ESP_CODEC_DEV_WRONG_STATE (ESP_ERR_INVALID_STATE) +#define ESP_CODEC_DEV_WRITE_FAIL (0x10D) +#define ESP_CODEC_DEV_READ_FAIL (0x10E) +/* typedef -------------------------------------------------------------------*/ +typedef enum +{ + EM_IMP_CODEC_DEV_WORK_MODE_NONE, + EM_IMP_CODEC_DEV_WORK_MODE_ADC = + (1 << 0), /*!< Enable ADC, only support input */ + EM_IMP_CODEC_DEV_WORK_MODE_DAC = + (1 << 1), /*!< Enable DAC, only support output */ + EM_IMP_CODEC_DEV_WORK_MODE_BOTH = + (EM_IMP_CODEC_DEV_WORK_MODE_ADC + | EM_IMP_CODEC_DEV_WORK_MODE_DAC), /*!< Support both DAC and ADC */ + EM_IMP_CODEC_DEV_WORK_MODE_LINE = (1 << 2), /*!< Line mode */ +} codec_work_mode_t; + +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif +#endif //__IMP_CODEC_H__ + +/* + * EOF + */ \ No newline at end of file diff --git a/main/components/imp_esp_i2c_master_dev_op/imp_esp_i2c_master_dev_op.c b/main/components/imp_esp_i2c_master_dev_op/imp_esp_i2c_master_dev_op.c index 988662e..aeead19 100644 --- a/main/components/imp_esp_i2c_master_dev_op/imp_esp_i2c_master_dev_op.c +++ b/main/components/imp_esp_i2c_master_dev_op/imp_esp_i2c_master_dev_op.c @@ -31,13 +31,17 @@ /* define --------------------------------------------------------------------*/ #define I2C_BUS_0_SCL_PIN 34 #define I2C_BUS_0_SDA_PIN 33 + +#define I2C_BUS_1_SCL_PIN 12 +#define I2C_BUS_1_SDA_PIN 13 /* typedef -------------------------------------------------------------------*/ /* variables -----------------------------------------------------------------*/ static uint8_t is_bus_0_inited = 0; +static uint8_t is_bus_1_inited = 0; i2c_master_bus_handle_t bus0_handle; -static imp_esp_i2c_master_dev_op_t i2c_bus_0_handle = { 0 }; +i2c_master_bus_handle_t bus1_handle; /* Private function(only *.c) -----------------------------------------------*/ void _imp_esp_i2c_master_dev_op_bus_init( @@ -53,6 +57,16 @@ void _imp_esp_i2c_master_dev_op_bus_init( .flags.enable_internal_pullup = true, }; i2c_new_master_bus(&bus_config, &bus0_handle); + } else if (port == EM_IMP_ESP_I2C_PORT_1) { + i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_NUM_1, + .sda_io_num = I2C_BUS_1_SDA_PIN, + .scl_io_num = I2C_BUS_1_SCL_PIN, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_new_master_bus(&bus_config, &bus1_handle); } } @@ -89,11 +103,20 @@ imp_esp_i2c_master_dev_op_init(imp_esp_i2c_master_dev_op_t* handle, handle->bus_handle = bus0_handle; _imp_esp_i2c_master_dev_op_handle_init(handle); break; + case EM_IMP_ESP_I2C_PORT_1: + if (!is_bus_1_inited) { + _imp_esp_i2c_master_dev_op_bus_init(EM_IMP_ESP_I2C_PORT_1); + is_bus_1_inited = 1; + } + handle->bus_handle = bus1_handle; + _imp_esp_i2c_master_dev_op_handle_init(handle); + break; default: break; } return 0; } + uint8_t imp_esp_i2c_master_dev_op_read(imp_esp_i2c_master_dev_op_t* handle, uint8_t* reg, uint16_t reg_len, uint8_t* data, uint16_t data_len) @@ -107,6 +130,7 @@ uint8_t imp_esp_i2c_master_dev_op_read(imp_esp_i2c_master_dev_op_t* handle, return 0; } + uint8_t imp_esp_i2c_master_dev_op_write(imp_esp_i2c_master_dev_op_t* handle, uint8_t* data, uint16_t data_len) { diff --git a/main/components/key_scan/key_scan.c b/main/components/key_scan/key_scan.c new file mode 100644 index 0000000..e69de29 diff --git a/main/components/key_scan/key_scan.drawio.svg b/main/components/key_scan/key_scan.drawio.svg new file mode 100644 index 0000000..7326aec --- /dev/null +++ b/main/components/key_scan/key_scan.drawio.svg @@ -0,0 +1,474 @@ + + + + + + + + + + + PRESS + + + + + + +
+
+
+ PRESS++ +
+ key_result++ +
+
+
+
+ + PRESS++... + +
+
+ + + + + +
+
+
+ Press +
+ last_state = RELASE +
+ debounce = 0 +
+
+
+
+ + Press... + +
+
+ + + + + +
+
+
+ debounce = 0  | +
+ RELEASE > 300ms +
+ key_result +
+
+
+
+ + debounce = 0  |... + +
+
+ + + + + +
+
+
+ debounce = 1 +
+
+
+
+ + debounce = 1 + +
+
+ + + + + + RELESE + + + + + + +
+
+
+ RELEASE++ +
+
+
+
+ + RELEASE++ + +
+
+ + + + + +
+
+
+ + state = last_state +
+ debounce +
+
+
+
+
+
+ + state = last_state... + +
+
+ + + + + + DEBOUNCE + + + + + + +
+
+
+ state = last_state +
+ DEBOUNCE++ +
+ keep_key: debounce = 1 +
+ no_keep: debounce = 0 +
+
+
+
+ + state = last_state... + +
+
+ + + + + + + + +
+
+
+ Press +
+ last_state = IDLE +
+ debounce = 0 +
+
+
+
+ + Press... + +
+
+ + + + + +
+
+
+ debounce = 1 +
+ Press +
+
+
+
+ + debounce = 1... + +
+
+ + + + + +
+
+
+ + state = last_state +
+
+ + debounce + + +
+
+
+
+
+
+ + state = last_state... + +
+
+ + + + + +
+
+
+ Release +
+ last_state = PRESS +
+ debounce = 0 +
+
+
+
+ + Release... + +
+
+ + + + + +
+
+
+ + state = last_state +
+
+ + debounce + + +
+
+
+
+
+
+ + state = last_state... + +
+
+ + + + + + + + RESULT + + + + + + + +
+
+
+ + debounce = 1 +
+
+
+
+
+
+ + debounce = 1 + +
+
+ + + + + +
+
+
+ debounce = 0 +
+
+
+
+ + debounce = 0 + +
+
+ + + + +
+
+
+ send key_result +
+
+
+
+ + send key_result + +
+
+ + + + + + IDLE + + + + + +
+
+
+ key_result = 0; +
+
+
+
+ + key_result = 0; + +
+
+ + + + + + IDLE + + + + + +
+
+
+ key_result = 0; +
+
+
+
+ + key_result = 0; + +
+
+ + + + + + DEBOUNCE + + + + + +
+
+
+ state = last_state +
+ DEBOUNCE++ +
+ keep_key: debounce = 1 +
+ no_keep: debounce = 0 +
+
+
+
+ + state = last_state... + +
+
+ + + + + + PRESS + + + + + +
+
+
+ PRESS++ +
+ key_result++ +
+
+
+
+ + PRESS++... + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/main/components/key_scan/key_scan.h b/main/components/key_scan/key_scan.h new file mode 100644 index 0000000..9662917 --- /dev/null +++ b/main/components/key_scan/key_scan.h @@ -0,0 +1,71 @@ +/** + * @file key_scan.h + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-23 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-23 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __KEY_SCAN_H__ +#define __KEY_SCAN_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +typedef enum __key_scan_key_result_e__ +{ + EM_KEY_SCAN_RESULT_IDLE = 0x00, + EM_KEY_SCAN_RESULT_ONSHOT, + EM_KEY_SCAN_RESULT_DOUBLE, + EM_KEY_SCAN_RESULT_LONG, +} key_scan_key_result_e; + +typedef enum __key_scan_key_stm_e__ +{ + EM_KEY_SCAN_KEY_STM_IDLE = 0x00, + EM_KEY_SCAN_KEY_STM_DEBOUNCE, + EM_KEY_SCAN_KEY_STM_PRESS, + EM_KEY_SCAN_KEY_STM_RELEASE, + EM_KEY_SCAN_KEY_STM_SEND_KEY, +} key_scan_key_stm_e; + +typedef struct __key_scan_key_state_t__ +{ + key_scan_key_stm_e state; + key_scan_key_stm_e last_state; + uint16_t press_cnt; + uint16_t release_cnt; + uint8_t debounce_ret; +} key_scan_key_state_t; +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +uint8_t key_scan_init(); + +#ifdef __cplusplus +} +#endif +#endif //__KEY_SCAN_H__ + /* + * EOF + */ \ No newline at end of file diff --git a/main/components/key_scan/key_scan_cfg.c b/main/components/key_scan/key_scan_cfg.c new file mode 100644 index 0000000..5988a72 --- /dev/null +++ b/main/components/key_scan/key_scan_cfg.c @@ -0,0 +1,32 @@ +/** + * @file key_scan_cfg.c + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-23 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-23 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +/* Includes ------------------------------------------------------------------*/ +#include "key_scan.h" +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ +/* + * EOF + */ \ No newline at end of file diff --git a/main/main_common.c b/main/main_common.c index 69f361f..c2e6bf9 100644 --- a/main/main_common.c +++ b/main/main_common.c @@ -37,14 +37,11 @@ /// @brief 所有Task的命名集合,名字长度不能超过configMAX_TASK_NAME_LEN = 16 char* imp_main_task_table[] = { - "idle", - "main_task", - "ext_trans_task", - "display", + "idle", "main_task", "ext_trans_task", "display", "audio", }; /* Private function(only *.c) -----------------------------------------------*/ -char cdc_print_buf[1024] = { 0 }; +char cdc_print_buf[1024] = { 0 }; uint8_t _imp_set_out_port(int argc, char** argv) { diff --git a/main/main_common.h b/main/main_common.h index 95eae9a..86f1936 100644 --- a/main/main_common.h +++ b/main/main_common.h @@ -36,6 +36,7 @@ extern "C" { #define IMP_TASK_ID_MAIN_TASK (1) #define IMP_TASK_ID_EXT_TRANS_SERVICE_TASK (2) #define IMP_TASK_ID_DISP_SERVICE_TASK (3) +#define IMP_TASK_ID_AUDIO_SERVICE_TASK (4) /* typedef -------------------------------------------------------------------*/ /* variables -----------------------------------------------------------------*/ @@ -46,6 +47,7 @@ extern char* imp_main_task_table[]; /* Exported functions --------------------------------------------------------*/ uint8_t imp_main_common_init(); + int cdc_printf(const char* fmt, ...); #ifdef __cplusplus diff --git a/main/services/audio_service/audio_service.c b/main/services/audio_service/audio_service.c new file mode 100644 index 0000000..ae41f85 --- /dev/null +++ b/main/services/audio_service/audio_service.c @@ -0,0 +1,125 @@ +/** + * @file audio_service.c + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-24 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-24 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +/* Includes ------------------------------------------------------------------*/ +#include "audio_service.h" +#include "audio_service_process.h" +#include "imp_msg_queue.h" +#include "shell_port.h" +#include +#include +#include "es8388.h" +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +/* variables -----------------------------------------------------------------*/ +static imp_msg_queue_t* msg_q_handle = NULL; +/* Private function(only *.c) -----------------------------------------------*/ + +uint8_t _read_all_codec_reg(int argc, char** argv) +{ + int i = 0; + int reg_addr = 0; + uint8_t reg_data = 0; + for (i = 0; i < 53; i++) { + if (i == 0) { + cdc_printf("chip control and power---------\r\n"); + } else if (i == 0x09) { + cdc_printf("ADC-----------------------------\r\n"); + } else if (i == 0x17) { + cdc_printf("DAC-----------------------------\r\n"); + } + reg_data = 0; + reg_addr = i; + imp_audio_service_codec_read_process_reg(reg_addr, ®_data); + cdc_printf("read 0x%02x value 0x%02x \r\n", reg_addr, reg_data); + } + cdc_printf("finish--------------------------\r\n"); + es8388_read_all(); + return 0; +} + +uint8_t _codec_io(int argc, char** argv) +{ + // codec_io 0(read)/1(write) reg_addr reg_data + if (argc < 3) { + return 1; + } + int is_write = atoi(argv[1]); + int reg_addr = atoi(argv[2]); + uint8_t reg_data = 0; + if (is_write) { + if (argc < 4) { + return 2; + } + reg_data = atoi(argv[3]); + imp_audio_service_codec_write_process_reg(reg_addr, reg_data); + cdc_printf("write 0x%02x value 0x%02x ok \r\n", reg_addr, reg_data); + } else { + imp_audio_service_codec_read_process_reg(reg_addr, ®_data); + cdc_printf("read 0x%02x value 0x%02x \r\n", reg_addr, reg_data); + } + + return 0; +} + +/* Exported functions --------------------------------------------------------*/ +void imp_audio_service_task(void*) +{ + uint8_t msg_recv_ret = 0; + imp_msg_item_t msg_item = { 0 }; + imp_audio_service_codec_process_init(); + msg_q_handle = imp_msg_queue_create_handle(IMP_TASK_ID_AUDIO_SERVICE_TASK); + imp_audio_service_codec_write_process_reg(ES8388_CONTROL1, 0x80); + + while (1) { + msg_recv_ret = imp_msg_queue_recv_msg(msg_q_handle, &msg_item, 10); + if (!msg_recv_ret) { + cdc_printf("%s get msg from %s OK\r\n", + imp_main_task_table[msg_item.recv_id], + imp_main_task_table[msg_item.send_id]); + switch (msg_item.msg_data) { + case 1: + imp_audio_service_codec_initialize(); + break; + case 2: + imp_audio_service_codec_start(); + break; + case 3: + imp_audio_service_codec_stop(); + break; + case 4: + imp_audio_service_codec_set_vol(100); + break; + } + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), + codec_io, _codec_io, codec read write); +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0) | SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), + read_all_codec, _read_all_codec_reg, read_all_codec_reg); + +/* + * EOF + */ \ No newline at end of file diff --git a/main/services/audio_service/audio_service.h b/main/services/audio_service/audio_service.h new file mode 100644 index 0000000..f4fc0ca --- /dev/null +++ b/main/services/audio_service/audio_service.h @@ -0,0 +1,46 @@ +/** + * @file audio_service.h + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-24 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-24 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __AUDIO_SERVICE_H__ +#define __AUDIO_SERVICE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include "main_common.h" +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ +void imp_audio_service_task(void*); +#ifdef __cplusplus +} +#endif +#endif //__AUDIO_SERVICE_H__ + +/* + * EOF + */ \ No newline at end of file diff --git a/main/services/audio_service/audio_service_codec_process.c b/main/services/audio_service/audio_service_codec_process.c new file mode 100644 index 0000000..3ed7775 --- /dev/null +++ b/main/services/audio_service/audio_service_codec_process.c @@ -0,0 +1,93 @@ +/** + * @file audio_service_codec_process.c + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-24 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-24 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +/* Includes ------------------------------------------------------------------*/ +#include "audio_service.h" +#include "imp_esp_i2c_master_dev_op.h" +#include "es8388.h" +#include "audio_service_process.h" +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +/* variables -----------------------------------------------------------------*/ +imp_esp_i2c_master_dev_op_t i2c_codec_handle; +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +uint8_t imp_audio_service_codec_process_init() +{ + imp_esp_i2c_master_dev_op_init(&i2c_codec_handle, EM_IMP_ESP_I2C_PORT_1, 1, + 0x11, 400000); + return 0; +} + +uint8_t imp_audio_service_codec_read_process_reg(uint8_t reg_addr, + uint8_t* reg_data) +{ + imp_esp_i2c_master_dev_op_read(&i2c_codec_handle, ®_addr, 1, reg_data, + 1); + return 0; +} + +uint8_t imp_audio_service_codec_write_process_reg(uint8_t reg_addr, + uint8_t reg_data) +{ + uint8_t data[2] = { 0 }; + data[0] = reg_addr; + data[1] = reg_data; + imp_esp_i2c_master_dev_op_write(&i2c_codec_handle, data, 2); + return 0; +} + +void imp_audio_service_codec_initialize() +{ + es_mode_t mode = ES_MODE_SLAVE; + es_module_t io_mode = ES_MODULE_ADC_DAC; + es_i2s_fmt_t fmt = ES_I2S_NORMAL; + imp_es_8388_io_port_e dac_output = EM_IMP_ES8388_LINE_OUT_1; + imp_es_8388_io_port_e adc_input = EM_IMP_ES8388_LINE_IN_1; + // es_i2s_clock_t cfg = + es8388_init(mode, dac_output, adc_input, + imp_audio_service_codec_read_process_reg, + imp_audio_service_codec_write_process_reg); + es8388_start(io_mode); +} + +void imp_audio_service_codec_start() +{ + es_module_t io_mode = ES_MODULE_ADC_DAC; + es8388_start(io_mode); +} + +void imp_audio_service_codec_stop() +{ + es_module_t io_mode = ES_MODULE_ADC_DAC; + es8388_stop(io_mode); +} + +void imp_audio_service_codec_set_vol(uint8_t vol) +{ + es8388_set_voice_volume(vol); +} + +/* + * EOF + */ \ No newline at end of file diff --git a/main/services/audio_service/audio_service_process.h b/main/services/audio_service/audio_service_process.h new file mode 100644 index 0000000..9952b67 --- /dev/null +++ b/main/services/audio_service/audio_service_process.h @@ -0,0 +1,55 @@ +/** + * @file audio_service_process.h + * @author Alvin Young (impressionyang@outlook.com) + * @brief + * @version 0.1 + * @date 2025-03-26 + * + * _ _ + * (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _ + * / / ' \/ _ \/ __/ -_|_-<(_- + * Date Version Author Description + * 2025-03-26 v1.0 Alvin Young 首次创建 + * + * + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __AUDIO_SERVICE_PROCESS_H__ +#define __AUDIO_SERVICE_PROCESS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +/* define --------------------------------------------------------------------*/ +/* typedef -------------------------------------------------------------------*/ +/* variables -----------------------------------------------------------------*/ +/* Private function(only *.c) -----------------------------------------------*/ +/* Exported functions --------------------------------------------------------*/ + +uint8_t imp_audio_service_codec_process_init(); +uint8_t imp_audio_service_codec_read_process_reg(uint8_t reg_addr, + uint8_t* reg_data); +uint8_t imp_audio_service_codec_write_process_reg(uint8_t reg_addr, + uint8_t reg_data); +void imp_audio_service_codec_initialize(); +void imp_audio_service_codec_start(); +void imp_audio_service_codec_stop(); +void imp_audio_service_codec_set_vol(uint8_t vol); + +#ifdef __cplusplus +} +#endif +#endif //__AUDIO_SERVICE_PROCESS_H__ + +/* + * EOF + */ \ No newline at end of file diff --git a/main/utilities/u8g2/port/imp_ug82_port.c b/main/utilities/u8g2/port/imp_ug82_port.c index 75d98aa..bac8316 100644 --- a/main/utilities/u8g2/port/imp_ug82_port.c +++ b/main/utilities/u8g2/port/imp_ug82_port.c @@ -75,7 +75,7 @@ static void _imp_i2c_ssd_oled_init(void) // i2c_param_config(I2C_NUM_0, &i2c_config); // i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0); imp_esp_i2c_master_dev_op_init(&i2c_oled_handle, EM_IMP_ESP_I2C_PORT_0, 1, - 0x3C, 4000000); + 0x3C, 400000); } /* Exported functions --------------------------------------------------------*/