从ESP32看ES8388低功耗音频芯片

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等等

1.1 参考资料

ESP-ADF源码

ES8388 数据手册

2、ES8388 

2.1 ES8388 Pin脚

从ESP32看ES8388低功耗音频芯片_第1张图片

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

 

音频接口(绿色)
MCLK 输入I 1脚 主时钟,必须等于fs(音频采样率)的256倍
SCLK 输入输出I\O  5脚 音频数据位时钟 用于同步
LRCK 输入输出I\0 7脚 音频数据左右声道对齐时钟
DSDIN 输入I  6脚 DAC音频数据
ASDOUT 输出0 8脚 ADC音频数据

一般使用ES8388作为从机,接收LRCK和SCLK。I2S接口支持左(left justify serial)音频数据格式、右(right justify serial)音频数据格式、飞利浦(I2S)音频数据格式、DSP/PCM模式音频数据格式。这里我们采用I2S音频数据格式16bit数据格式。

I2S标准模式,数据在跟随LRCK输出的BCLK的第二个上升沿时传输MSB,其他位一直到LSB按顺序传输。

传输依赖于字长、BCLK频率和采样率,在每个采样的LSB都和下一个采样的MSB之间都应该有未用的BCLK周期。

从ESP32看ES8388低功耗音频芯片_第2张图片

图中,fs即音频信号的采样率,LRCK的频率就是音频信号的采样率。MCLK的频率必须等于256f,也就是音频采样率的256倍。

ES8388内部有很多的模拟开关,用于选择通道,同时有很多的调节器,用于设置增益和音量

重要的要

设置ROUT1EN和LOUT1EN,使能耳机输出.....

LOUT2EN 和ROUT2En,使能喇叭输出

左右声道混合器使能、使能左右声道DAC

设置字长、I2S音频数据格式

设置增益 音量

/****************************************************************************************************************************************/

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) ? 读写

从ESP32看ES8388低功耗音频芯片_第3张图片

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

从ESP32看ES8388低功耗音频芯片_第4张图片 写和读寄存器操作图

2.2时钟模式和采样频率

处于从模式时,LRCK和SCL有外部提供,且必须由系统时钟同步派生得到。根据下表,设备自动检测MCLK/LRCK的比率。

从ESP32看ES8388低功耗音频芯片_第5张图片

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也不例外。 

看看范例中对ESP8388的初始化启动过程

ESP_LOGI(TAG, "[ 2 ] Start codec chip");
audio_hal_codec_config_t audio_hal_codec_cfg =  AUDIO_HAL_ES8388_DEFAULT();
audio_hal_codec_cfg.i2s_iface.samples = AUDIO_HAL_44K_SAMPLES;
audio_hal_handle_t hal = audio_hal_init(&audio_hal_codec_cfg, 0);  //ES8388芯片初始化
audio_hal_ctrl_codec(hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);  //启动编解码芯片

接下去详细分析这些操作内部做了些什么,方便我们利用这些API更好的操纵芯片以实现我们需要的功能。

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模式、音量设置。 学习一下对RTOS下的初始化操作。接下去对三个操作具体分析

/*********************************************************具体分析******************************************************************/

                                                                /****************ES8388初始化*******************/

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;
}

在初始化IIC之后,对ES8388 芯片的寄存器按照需求进行设置。

第一步、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配置,不具体了叙述

看看I2C_init()的具体内容

typedef enum{
    I2C_NUM_0 = 0,  /*!< I2C port 0 */
    I2C_NUM_1 ,     /*!< I2C port 1 */
    I2C_NUM_MAX
} i2c_port_t;

static const i2c_config_t es_i2c_cfg = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = IIC_DATA,
    .scl_io_num = IIC_CLK,
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = 100000
};

static int i2c_init()
{
    int res;
    res = i2c_param_config(I2C_NUM_0, &es_i2c_cfg);
    res |= i2c_driver_install(I2C_NUM_0, es_i2c_cfg.mode, 0, 0, 0);
    ES_ASSERT(res, "i2c_init error", -1);
    return res;
}

                                                     /****************ES8388初始化*******************/
接下去看看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 & 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 & 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 & 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 & 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解码功能启动。

 

水平有限,持续修改。

 

 

 

 

你可能感兴趣的:(ESP32开发)