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等等
ESP-ADF源码
ES8388 数据手册
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周期。
图中,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来从寄存器中读取数据。
处于从模式时,LRCK和SCL有外部提供,且必须由系统时钟同步派生得到。根据下表,设备自动检测MCLK/LRCK的比率。
初始化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更好的操纵芯片以实现我们需要的功能。
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;
}
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解码功能启动。
水平有限,持续修改。