esp32s2_bare_board/main/components/Codec/ES8388/es8388.c
2025-04-14 17:33:55 +08:00

668 lines
20 KiB
C

/**
* @file es8388.c
* @author Alvin Young (impressionyang@outlook.com)
* @brief
* @version 0.1
* @date 2025-03-22
*
* _ _
* (_)_ _ ___ _______ ___ ___ (_)__ ___ __ _____ ____ ___ _
* / / ' \/ _ \/ __/ -_|_-<(_-</ / _ \/ _ \/ // / _ `/ _ \/ _ `/
* /_/_/_/_/ .__/_/ \__/___/___/_/\___/_//_/\_, /\_,_/_//_/\_, /
* /_/ /___/ /___/
* @copyright Copyright (c) 2025 impressionyang
*
* @par 修改日志:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2025-03-22 <td>v1.0 <td>Alvin Young <td>首次创建
* </table>
*
*/
/* Define to prevent recursive inclusion -------------------------------------*/
/* Includes ------------------------------------------------------------------*/
#include "es8388.h"
#include "imp_codec.h"
#include <stdlib.h>
#include <string.h>
#include <esp_log.h>
/* 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, &reg);
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 = 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 = 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, &reg);
// 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 = 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 = 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 = 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, &reg);
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 = 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 = 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
*/