/** * @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 */