从ESP32看ES8388低功耗音频芯片

Source
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhejfl/article/details/87535520

1、ES8388 简介

ES8388是一种高性能、低功耗、低成本的音频编解码器。它由两路ADC,2通道DAC,话筒放大器、耳机放大器、数字音效、模拟混合和增益功能。

ES8388采用先进的多位Δ∑调制技术实现数字与模拟之间的数据转换。多比特Δ∑调制器使器件对时钟抖动和低带外噪声的灵敏度低。它应用于:MID,MP3, MP4, PMP,无线音频,数码相机,摄像机,GPS领域,蓝牙,便携式音频设备。

因为具有双路特性。

ADC特点为:24位,8千赫到96千赫取样频率;95分贝动态范围,95分贝信噪比,85分贝THD + N;立体声或单麦克风接口与麦克风放大器;自动电平控制和噪声门;2模拟输入选择;各种模拟输入混合和增益。

DAC特点为:24位,8千赫到96千赫取样频率;动态范围为96 dB,96 dB的信噪比,83分贝THD + N;40毫瓦耳机放大器无噪音的;耳机无模式;立体声增强;各种模拟输出混合并获得Low Power等等

2、ES8388 Pin脚

I2C/SPI控制接口Pin脚
I2C/SPI_CLK 输入I  28脚 控制时钟输入,同步时钟
I2C/SPI_DAT 输入输出I/0 27脚 控制数据输入输出
I2C_AD/SPI_CE 输入I 26脚 控制悬着或设备地址选择

 

音频接口
MCLK 输入I 1脚 主时钟
SCLK 输入输出I\O  5脚 音频数据位时钟
LRCK 输入输出I\0 7脚 音频数据左右声道时钟
DSDIN 输入I  6脚 DAC音频数据
ASDOUT 输出0 8脚 ADC音频数据

ES8388微控制器的配置接口有I2C和三线SPI,这里主要讲述I2C。

ES8388 I2C的特点

1、SDA数据传输以字节为单位同步到SCL时钟上,每一位在SCL高电平期间采样;

2、从MSB位开始传输;

3、一个字节后跟一个接受方接收到的应答位;

4、传输速率可达100k bps.

芯片地址为{0x20(CE=0)/0x22 (CE=0)  }/0b001000x?(x=PIN CE) ? 读写

与I2C协议不同之处在于从一个寄存器中读取数据,你必须先设置R/W wei为0来访问这个寄存器地址,在设置R/W位为1来从寄存器中读取数据。

写和读寄存器操作图

 

3、根据ESP-ADF源码中audio_hal/driver/esp8388中的es8388源码来学习操作ES8388的方法

3.0 ESP-ADF的audio_hal层主要操作包括哪些

初始化media编解码芯片驱动
audio_hal_handle_t audio_hal_init(audio_hal_codec_config_t* audio_hal_conf, int index);
解除初始化media编解码芯片驱动
esp_err_t audio_hal_deinit(audio_hal_handle_t audio_hal, int index);
启动或停止编解码芯片驱动,设置模式、启停
esp_err_t audio_hal_ctrl_codec(audio_hal_handle_t audio_hal, audio_hal_codec_mode_t mode, audio_hal_ctrl_t audio_hal_ctrl);
设置I2S接口采样率和位宽以及I2S或PCM/DSP 格式
esp_err_t audio_hal_codec_iface_config(audio_hal_handle_t audio_hal, audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t* iface);
获取或设置音量
esp_err_t audio_hal_set_volume(audio_hal_handle_t audio_hal, int volume);
esp_err_t audio_hal_get_volume(audio_hal_handle_t audio_hal, int* volume);

因此对于特定的编解码芯片要根据上述函数功能来实现其对应操作。当然我们的ES8388也不例外。 

3.1 初始化编解码芯片驱动

ESP-ADF解码芯片通用接口audio_hal_handle_t audio_hal_init(audio_hal_codec_config_t *audio_hal_conf, int index)初始化过程主要包括如下

 ret  = audio_hal->audio_codec_initialize(audio_hal_conf);
 ret |= audio_hal->audio_codec_config_iface(AUDIO_HAL_CODEC_MODE_BOTH, &audio_hal_conf->i2s_iface);
 ret |= audio_hal->audio_codec_set_volume(AUDIO_HAL_VOL_DEFAULT);

大约完成模块初始化、模块ADC/DAC/BOTH模式,I2S模式、音量设置。 

audio_codec_initialize实际调用的是es8388_init()接口,看看es8388_init()完成了什么

#define AUDIO_HAL_ES8388_DEFAULT(){                     \
        .adc_input  = AUDIO_HAL_ADC_INPUT_LINE1,        \
        .dac_output = AUDIO_HAL_DAC_OUTPUT_ALL,         \
        .codec_mode = AUDIO_HAL_CODEC_MODE_BOTH,        \
        .i2s_iface = {                                  \
            .mode = AUDIO_HAL_MODE_SLAVE,               \
            .fmt = AUDIO_HAL_I2S_NORMAL,                \
            .samples = AUDIO_HAL_48K_SAMPLES,           \
            .bits = AUDIO_HAL_BIT_LENGTH_16BITS,        \
        },                                              \
};

esp_err_t es8388_init(audio_hal_codec_config_t *cfg)
{
    int res = 0;
#ifdef CONFIG_ESP_LYRAT_V4_3_BOARD
    #include "headphone_detect.h"
    headphone_detect_init();
#endif

    res = i2c_init(); // ESP32 in master mode

    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x04);  // 0x04 mute/0x00 unmute&ramp;DAC unmute and  disabled digital volume control soft ramp
    /* Chip Control and Power Management */
    res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50);
    res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00); //normal all and power up all
    res |= es_write_reg(ES8388_ADDR, ES8388_MASTERMODE, cfg->i2s_iface.mode); //CODEC IN I2S SLAVE MODE

    /* dac */
    res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0xC0);  //disable DAC and disable Lout/Rout/1/2
    res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x12);  //Enfr=0,Play&Record Mode,(0x17-both of mic&paly)
//    res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0);  //LPVrefBuf=0,Pdn_ana=0
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, 0x18);//1a 0x18:16bit iis , 0x00:24
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL2, 0x02);  //DACFsMode,SINGLE SPEED; DACFsRatio,256
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1,  0x09 LIN2&RIN2
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); //set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL23, 0x00);   //vroi=0
    res |= es8388_set_adc_dac_volume(ES_MODULE_DAC, 0, 0);          // 0db
    int tmp = 0;
    if (AUDIO_HAL_DAC_OUTPUT_LINE2 == cfg->dac_output) {
        tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_ROUT1;
    } else if (AUDIO_HAL_DAC_OUTPUT_LINE1 == cfg->dac_output) {
        tmp = DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT2;
    } else {
        tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2;
    }
    res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, tmp);  //0x3c Enable DAC and Enable Lout/Rout/1/2
    /* adc */
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xFF);
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain
    tmp = 0;
    if (AUDIO_HAL_ADC_INPUT_LINE1 == cfg->adc_input) {
        tmp = ADC_INPUT_LINPUT1_RINPUT1;
    } else if (AUDIO_HAL_ADC_INPUT_LINE2 == cfg->adc_input) {
        tmp = ADC_INPUT_LINPUT2_RINPUT2;
    } else {
        tmp = ADC_INPUT_DIFFERENCE;
    }
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, tmp);  //0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL3, 0x02);
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, 0x0d); // Left/Right data, Left/Right justified mode, Bits length, I2S format
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, 0x02);  //ADCFsMode,singel SPEED,RATIO=256
    //ALC for Microphone
    res |= es8388_set_adc_dac_volume(ES_MODULE_ADC, 0, 0);      // 0db
    res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x09); //Power up ADC, Enable LIN&RIN, Power down MICBIAS, set int1lp to low power mode
    /* enable es8388 PA */
    es8388_pa_power(true);   
    ESP_LOGI(ES_TAG, "init,out:%02x, in:%02x", cfg->dac_output, cfg->adc_input);
    return res;
}

第一步、DAC两个通道都设为静音;

第二步、power all up, 模式设置为I2S 从机模式

第三步、DAC电源设为默认值(即DAC Lout/Rout 1 2均失能);I2S位宽设置为16bit;

设置主模DAC MCLK与采样频率之比为256;输出混合左右选择位LIN1和RIN1;only left DAC到左混合使能设置增益为0db;only right DAC到右混合使能设置增益为0db;设置ADC和DAC使用相同的LRCK clock,均使用DAC_LRCK作为内部LRCK时钟;VREF到模拟输出的阻抗为1.5K;设置数字音量控制衰减信号为0db

第四步、根据输出,使能DAC 和 Lout/Rout/1/2电源、关闭ADC相关电源;设置ADC左右通道的PGA增益。

第五步、ADC配置,不具体了


接下去看看audio_codec_config_iface()实际调用es8388_config_i2s()接口的是完成模块模式配置、I2S格式配置还设置了位宽

int es8388_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface)
{
    int res = 0;
    int tmp = 0;
    res |= es8388_config_fmt(ES_MODULE_ADC_DAC, iface->fmt);
    if (iface->bits == AUDIO_HAL_BIT_LENGTH_16BITS) {
        tmp = BIT_LENGTH_16BITS;
    } else if (iface->bits == AUDIO_HAL_BIT_LENGTH_24BITS) {
        tmp = BIT_LENGTH_24BITS;
    } else {
        tmp = BIT_LENGTH_32BITS;
    }
    res |= es8388_set_bits_per_sample(ES_MODULE_ADC_DAC, tmp);
    return res;
}

/**
 * @brief Select media hal codec mode
 */
typedef enum {
    AUDIO_HAL_CODEC_MODE_ENCODE = 1,  /*!< select adc */
    AUDIO_HAL_CODEC_MODE_DECODE,      /*!< select dac */
    AUDIO_HAL_CODEC_MODE_BOTH,        /*!< select both adc and dac */
    AUDIO_HAL_CODEC_MODE_LINE_IN,     /*!< set adc channel */
} audio_hal_codec_mode_t;
/**
 * @brief Select I2S interface format for audio codec chip
 */
typedef enum {
    AUDIO_HAL_I2S_NORMAL = 0,  /*!< set normal I2S format */
    AUDIO_HAL_I2S_LEFT,        /*!< set all left format */
    AUDIO_HAL_I2S_RIGHT,       /*!< set all right format */
    AUDIO_HAL_I2S_DSP,         /*!< set dsp/pcm format */
} audio_hal_iface_format_t;

//设置es8388模块模式和I2S格式
int es8388_config_fmt(es_module_t mode, es_i2s_fmt_t fmt)
{
    int res = 0;
    uint8_t reg = 0;
    if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
        res = es_read_reg(ES8388_ADCCONTROL4, &reg);
        reg = reg & 0xfc;
        res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, reg | fmt);
    }
    if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
        res = es_read_reg(ES8388_DACCONTROL1, &reg);
        reg = reg & 0xf9;
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, reg | (fmt << 1));
    }
    return res;
}

//设置采样位宽
int es8388_set_bits_per_sample(es_module_t mode, es_bits_length_t bits_length)
{
    int res = 0;
    uint8_t reg = 0;
    int bits = (int)bits_length;

    if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
        res = es_read_reg(ES8388_ADCCONTROL4, &reg);
        reg = reg & 0xe3;
        res |=  es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, reg | (bits << 2));
    }
    if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
        res = es_read_reg(ES8388_DACCONTROL1, &reg);
        reg = reg & 0xc7;
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, reg | (bits << 3));
    }
    return res;
}

audio_codec_set_volume()接口实际调用es8388_set_voice_volume()接口来设置音量。

int es8388_set_voice_volume(int volume)
{
    int res;
    if (volume < 0)
        volume = 0;
    else if (volume > 100)
        volume = 100;
    volume /= 3;
    res = es_write_reg(ES8388_ADDR, ES8388_DACCONTROL24, volume);  //设置LOUT1音量
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL25, volume); //设置ROUT1音量
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL26, 0);      //设置LOUT2音量
    res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL27, 0);      //设置ROUT2音量
    return res;
}

3.2 启动或停止编解码芯片驱动,设置模式、启停

audio_hal_ctrl_codec(hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);

audio_hal_ctrl_codec()接口核心代码如下

ret = audio_hal->audio_codec_ctrl(mode, audio_hal_state);

对于ES8388实际上调用的是es8388_ctrl_state()接口,

es8388_ctrl_state(AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START)

int es8388_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state)
{
    int res = 0;
    int es_mode_t = 0;
    switch (mode) {
        case AUDIO_HAL_CODEC_MODE_ENCODE:
            es_mode_t  = ES_MODULE_ADC;
            break;
        case AUDIO_HAL_CODEC_MODE_LINE_IN:
            es_mode_t  = ES_MODULE_LINE;
            break;
        case AUDIO_HAL_CODEC_MODE_DECODE:
            es_mode_t  = ES_MODULE_DAC;
            break;
        case AUDIO_HAL_CODEC_MODE_BOTH:
            es_mode_t  = ES_MODULE_ADC_DAC;
            break;
        default:
            es_mode_t = ES_MODULE_DAC;
            ESP_LOGW(ES_TAG, "Codec mode not support, default is decode mode");
            break;
    }
    if (AUDIO_HAL_CTRL_STOP == ctrl_state) {
        res = es8388_stop(es_mode_t);
    } else {
        res = es8388_start(es_mode_t);
        ESP_LOGD(ES_TAG, "start default is decode mode:%d", es_mode_t);
    }
    return res;
}

最终世界上es8388_start(ES_MODULE_DAC),查看es8388_start源码

int es8388_start(es_module_t mode)
{
    int res = 0;
    uint8_t prev_data = 0, data = 0;
    es_read_reg(ES8388_DACCONTROL21, &prev_data);
    if (mode == ES_MODULE_LINE) {
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x09); // 0x00 audio on LIN1&RIN1,  0x09 LIN2&RIN2 by pass enable
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x50); // left DAC to left mixer enable  and  LIN signal to left mixer enable 0db  : bupass enable
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x50); // right DAC to right mixer enable  and  LIN signal to right mixer enable 0db : bupass enable
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0xC0); //enable adc
    } else {
        res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80);   //enable dac
    }
    es_read_reg(ES8388_DACCONTROL21, &data);
    if (prev_data != data) {   //复位+重启
        res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0xF0);   //start state machine
        // res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x16);
        // res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50);
        res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00);   //start state machine
    }
    if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC || mode == ES_MODULE_LINE) {
        res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x00);   //power up adc and line in
    }
    if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC || mode == ES_MODULE_LINE) {
        res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3c);   //power up dac and line out
        res |= es8388_set_voice_mute(false);
        ESP_LOGD(ES_TAG, "es8388_start default is mode:%d", mode);
    }

    return res;
}

设置ADC和DAC使用相同的LRCK clock,均使用DAC_LRCK作为内部LRCK时钟;left/right DAC Power up 和LOUT/ROUT 1、2 enabled.设置为非静音模式

自此,ES8388解码功能启动。