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

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作爲從機,接收LRCK和SCLK。I2S接口支持左(left justify serial)音頻數據格式、右(right justify serial)音頻數據格式、飛利浦(I2S)音頻數據格式、DSP/PCM模式音頻數據格式。這裏我們採用I2S音頻數據格式16bit數據格式。

I2S標準模式,數據在跟隨LRCK輸出的BCLK的第二個上升沿時傳輸MSB,其他位一直到LSB按順序傳輸。

傳輸依賴於字長、BCLK頻率和採樣率,在每個採樣的LSB都和下一個採樣的MSB之間都應該有未用的BCLK週期。

圖中,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) ? 讀寫

與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解碼功能啓動。

 

水平有限,持續修改。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章