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_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 & 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解碼功能啓動。
水平有限,持續修改。