I2S总线基础概念
I2S概念
I2S(Inter-IC Sound)总线, 又称集成电路内置音频总线,最早是由现在的恩智浦半导体公司针对数字音频设备之间的音频数据传输而制定的总线标准。该总线专门用于音频设备间的传输,广泛用于各种多媒体系统。它传输的是PCM格式数据。
PCM音频数据
PCM (脉冲编码调制),由A.里弗斯于1937年提出的记录音频的格式。PCM是模拟信号经过采样、量化、编码转换成标准数字音频的原始数据格式。
转换成PCM格式的三个参数
音频数据量=采样频率×量化位数×声道数/8(字节/秒)
采样频率(声音周期量化)
1秒同时对多个声道完成adc采样的次数。采样频率越高,声音质量越好,还原越真实,但同时它占的资源比较多。
采样位数(声音的幅度量化)
每个采样点用多少二进制位表示数据范围。量化位数越多,音质越好,数据量也越大
声道数(单声道,立体声)
使用声音通道的个数,有单声道和立体声之分,立体声比单声道数据量翻倍。
I2S总线通讯方式
- 支持全双工和半双工通信
- 支持主/从模式
I2S总线引脚
- SCK:(continuous serial clock) 串行时钟,I2S的心跳。
串行时钟SCK,也叫位时钟BCLK,也有的称为BCK、SCLK等。对应数字音频的每一位数据,SCK都有1个脉冲。 SCK的频率 = 声道数 * 采样频率 * 采样位数。 - WS: (word select) 字段(声道)选择
字段选择信号WS,也叫LRCLK(LRCK),用于切换左右声道的数据。 WS的频率 = 采样频率 WS为0,表示正在传输的是左声道的数据; WS为1,表示正在传输的是右声道的数据。 - SDATA:串行数据,在全双工的模式下分为DATAin和DATAout,主从之间的in/out要交叉接线
- MCLK:主时钟频率。一般为采样频率的256倍
esp32从ES8311分析i2s驱动如何去写
原理图
┌─────────────────┐ ┌──────────────────────────┐
│ ESP │ │ ES8311 │
│ │ │ │
│ MCLK-GPIO 0 ├──────────?│PIN2-MCLK │
│ │ │ │ ┌─────────┐
│ BCLK-GPIO 4 ├──────────?│PIN6-BCLK PIN12-OUTP├───────────┤ │
│ │ │ │ │ EARPHONE│
│ WS-GPIO 5 ├──────────?│PIN8-LRCK PIN13-OUTN├───────────┤ │
│ │ │ │ └─────────┘
│ SDOUT-GPIO 18├──────────?│PIN9-SDIN │
│ │ │ │
│ SDIN-GPIO 19│?──────────┤PIN7-SDOUT │
│ │ │ │ ┌─────────┐
│ │ │ PIN18-MIC1P├───────────┤ │
│ SCL-GPIO 16├──────────?│PIN1 -CCLK │ │ MIC │
│ (GPIO 7)│ │ PIN17-MIC1N├───────────┤ │
│ SDA-GPIO 17│?─────────?│PIN19-CDATA │ └─────────┘
│ (GPIO 8)│ │ │
│ VCC 3.3├───────────┤VCC │
│ │ │ │
│ GND├───────────┤GND │
└─────────────────┘ └──────────────────────────┘
注意:这里的SDOUT和SDIN要交叉接线。而且除了i2s给数据流之外,我们还需要一个i2c用来初始化codec芯片
例程代码分析
主函数
void app_main(void)
{
if (i2s_driver_init() != ESP_OK) {
ESP_LOGE(TAG, "i2s driver init failed");
abort();
}
if (es8311_codec_init() != ESP_OK) {
ESP_LOGE(TAG, "es8311 codec init failed");
abort();
}
#if CONFIG_EXAMPLE_MODE_MUSIC
xTaskCreate(i2s_music, "i2s_music", 4096, NULL, 5, NULL);
#else
xTaskCreate(i2s_echo, "i2s_echo", 8192, NULL, 5, NULL);
#endif
}
主函数整体比较简单初始化i2s后初始化i2c并且给i2c初始化,然后就是通过i2s输入音频数据。
i2s初始化
static esp_err_t i2s_driver_init(void)
{
i2s_config_t i2s_cfg = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX,
.sample_rate = EXAMPLE_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.tx_desc_auto_clear = true,
#if SOC_I2S_SUPPORTS_TDM
.total_chan = 2,
.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1,
.left_align = false,
.big_edin = false,
.bit_order_msb = false,
.skip_msk = false,
#endif
.dma_desc_num = 8,
.dma_frame_num = 64,
.use_apll = false,
.mclk_multiple = EXAMPLE_MCLK_MULTIPLE,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
};
ESP_RETURN_ON_ERROR(i2s_driver_install(I2S_NUM, &i2s_cfg, 0, NULL), TAG, "install i2s failed");
i2s_pin_config_t i2s_pin_cfg = {
.mck_io_num = I2S_MCK_IO,
.bck_io_num = I2S_BCK_IO,
.ws_io_num = I2S_WS_IO,
.data_out_num = I2S_DO_IO,
.data_in_num = I2S_DI_IO
};
ESP_RETURN_ON_ERROR(i2s_set_pin(I2S_NUM, &i2s_pin_cfg), TAG, "set i2s pins failed");
return ESP_OK;
}
关于TDM相关的这里有比较详细的说明,大致就是多声道输出。本人理解有限 ,这里不做过多解释。
es8311初始化
static esp_err_t es8311_codec_init(void)
{
i2c_config_t es_i2c_cfg = {
.sda_io_num = I2C_SDA_IO,
.scl_io_num = I2C_SCL_IO,
.mode = I2C_MODE_MASTER,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
ESP_RETURN_ON_ERROR(i2c_param_config(I2C_NUM, &es_i2c_cfg), TAG, "config i2c failed");
ESP_RETURN_ON_ERROR(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0), TAG, "install i2c driver failed");
es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
ESP_RETURN_ON_FALSE(es_handle, ESP_FAIL, TAG, "es8311 create failed");
es8311_clock_config_t es_clk = {
.mclk_from_mclk_pin = true,
.sample_frequency = EXAMPLE_SAMPLE_RATE
};
es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16);
ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE), TAG, "set es8311 sample frequency failed");
ESP_RETURN_ON_ERROR(es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL), TAG, "set es8311 volume failed");
ESP_RETURN_ON_ERROR(es8311_microphone_config(es_handle, false), TAG, "set es8311 microphone failed");
#if CONFIG_EXAMPLE_MODE_ECHO
ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(es_handle, EXAMPLE_MIC_GAIN), TAG, "set es8311 microphone gain faield");
#endif
return ESP_OK;
}
前面部分是在初始化i2c,后面部分主要是在调用es8311库里的东西,主要是调用i2c去配置一些es8311的寄存器。
播放音频
static void i2s_music(void *args)
{
esp_err_t ret = ESP_OK;
size_t bytes_write = 0;
while (1) {
ret = i2s_write(I2S_NUM, music_pcm_start, music_pcm_end - music_pcm_start, &bytes_write, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[music] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
abort();
}
i2s_zero_dma_buffer(I2S_NUM);
if (bytes_write > 0) {
ESP_LOGI(TAG, "[music] i2s music played, %d bytes are written.", bytes_write);
} else {
ESP_LOGE(TAG, "[music] i2s music play falied.");
abort();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
|