Appearance
I2S
使用es8311芯片
这一个芯片ESP-IDF自带的示例里面就有
可以直接使用
在example_config.h文件里面改一下使用的GPIO, 在main函数里面加入使能引脚的定义, 设置为高电平即可
使用的GPIO11需要使用uFuse进行设置
回声模式可以设置麦克风的增益
c
void app_main(void)
{
printf("i2s es8311 codec example start\n-----------------------------\n");
printf("ESP_EFUSE_VDD_SPI_AS_GPIO start\n-----------------------------\n");
//这一个引脚熔断只需要一次
//esp_efuse_write_field_bit(ESP_EFUSE_VDD_SPI_AS_GPIO);
/* 初始化PA芯片NS4150B控制引脚 低电平关闭音频输出 高电平允许音频输出 */
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE, //disable interrupt
.mode = GPIO_MODE_OUTPUT, //set as output mode
.pin_bit_mask = 1<<13, //bit mask of the pins
.pull_down_en = 0, //disable pull-down mode
.pull_up_en = 1, //enable pull-up mode
};
//configure GPIO with the given settings
gpio_config(&io_conf);
gpio_set_level(GPIO_NUM_13, 1); // 输出高电平
/* Initialize i2s peripheral */
if (i2s_driver_init() != ESP_OK) {
ESP_LOGE(TAG, "i2s driver init failed");
abort();
} else {
ESP_LOGI(TAG, "i2s driver init success");
}
/* Initialize i2c peripheral and config es8311 codec by i2c */
if (es8311_codec_init() != ESP_OK) {
ESP_LOGE(TAG, "es8311 codec init failed");
abort();
} else {
ESP_LOGI(TAG, "es8311 codec init success");
}
#if CONFIG_EXAMPLE_MODE_MUSIC
/* Play a piece of music in music mode */
xTaskCreate(i2s_music, "i2s_music", 4096, NULL, 5, NULL);
#else
/* Echo the sound from MIC in echo mode */
xTaskCreate(i2s_echo, "i2s_echo", 8192, NULL, 5, NULL);
#endif
}
初始化I2S
c
static esp_err_t i2s_driver_init(void)
{
#if !defined(CONFIG_EXAMPLE_BSP)
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
//配置引脚
.mclk = I2S_MCK_IO,
.bclk = I2S_BCK_IO,
.ws = I2S_WS_IO,
.dout = I2S_DO_IO,
.din = I2S_DI_IO,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
//设置模式
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
//使能通道
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
#else
//这一个是乐鑫自己的开发板执行的函数
ESP_LOGI(TAG, "Using BSP for HW configuration");
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = BSP_I2S_GPIO_CFG,
};
std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;
ESP_ERROR_CHECK(bsp_audio_init(&std_cfg, &tx_handle, &rx_handle));
ESP_ERROR_CHECK(bsp_audio_poweramp_enable(true));
#endif
return ESP_OK;
}
esc8311初始化
c
static esp_err_t es8311_codec_init(void)
{
/* Initialize I2C peripheral */
#if !defined(CONFIG_EXAMPLE_BSP)
//初始化I2C驱动
const 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");
#else
ESP_ERROR_CHECK(bsp_i2c_init());
#endif
//初始化esc8311
/* Initialize es8311 codec */
es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);
ESP_RETURN_ON_FALSE(es_handle, ESP_FAIL, TAG, "es8311 create failed");
const es8311_clock_config_t es_clk = {
.mclk_inverted = false,
.sclk_inverted = false,
.mclk_from_mclk_pin = true,
.mclk_frequency = EXAMPLE_MCLK_FREQ_HZ,
.sample_frequency = EXAMPLE_SAMPLE_RATE
};
ESP_ERROR_CHECK(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");//设置麦克风false: 模拟麦克风 true数字麦克风
#if CONFIG_EXAMPLE_MODE_ECHO
ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(es_handle, EXAMPLE_MIC_GAIN), TAG, "set es8311 microphone gain failed");//配置麦克风的增益
#endif
return ESP_OK;
}
c
typedef struct {
i2c_port_t port;
uint16_t dev_addr;
} es8311_dev_t;
es8311_handle_t es8311_create(const i2c_port_t port, const uint16_t dev_addr)
{
es8311_dev_t *sensor = (es8311_dev_t *) calloc(1, sizeof(es8311_dev_t));
sensor->port = port;
sensor->dev_addr = dev_addr;
return (es8311_handle_t) sensor;
}
执行任务
c
static void i2s_music(void *args)
{
esp_err_t ret = ESP_OK;
size_t bytes_write = 0;
uint8_t *data_ptr = (uint8_t *)music_pcm_start;
/* (Optional) Disable TX channel and preload the data before enabling the TX channel,
* so that the valid data can be transmitted immediately */
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));//输出通道禁止
ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write));//预加载输出的数据
data_ptr += bytes_write; // Move forward the data pointer
/* Enable the TX channel */
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));//开启
while (1) {
/* Write music to earphone */
ret = i2s_channel_write(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write, portMAX_DELAY);//输出音乐, 使用的是canon.pcm这一个文件
if (ret != ESP_OK) {
/* Since we set timeout to 'portMAX_DELAY' in 'i2s_channel_write'
so you won't reach here unless you set other timeout value,
if timeout detected, it means write operation failed. */
ESP_LOGE(TAG, "[music] i2s write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
abort();
}
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 failed.");
abort();
}
data_ptr = (uint8_t *)music_pcm_start;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
#else
static void i2s_echo(void *args)
{
int *mic_data = malloc(EXAMPLE_RECV_BUF_SIZE);
if (!mic_data) {
ESP_LOGE(TAG, "[echo] No memory for read data buffer");
abort();
}
esp_err_t ret = ESP_OK;
size_t bytes_read = 0;
size_t bytes_write = 0;
ESP_LOGI(TAG, "[echo] Echo start");
while (1) {
memset(mic_data, 0, EXAMPLE_RECV_BUF_SIZE);
/* Read sample data from mic */
ret = i2s_channel_read(rx_handle, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_read, 1000);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[echo] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
abort();
}
/* Write sample data to earphone */
ret = i2s_channel_write(tx_handle, mic_data, EXAMPLE_RECV_BUF_SIZE, &bytes_write, 1000);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[echo] i2s write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
abort();
}
if (bytes_read != bytes_write) {
ESP_LOGW(TAG, "[echo] %d bytes read but only %d bytes are written", bytes_read, bytes_write);
}
}
vTaskDelete(NULL);
}
#endif
配置自己的音乐
- Choose the music in any format you want to play (e.g. a.mp3)
- Install 'ffmpeg' tool 安装这一个工具
- Check your music format using
ffprobe a.mp3
, you can get the stream format (e.g. Stream #0.0: Audio: mp3, 44100Hz, stereo, s16p, 64kb/s) 剪切 - Cut your music since there is no enough space for the whole piece of music.
ffmpeg -i a.mp3 -ss 00:00:00 -t 00:00:20 a_cut.mp3
- Transfer the music format into .pcm.
ffmpeg -i a_cut.mp3 -f s16ls -ar 16000 -ac -1 -acodec pcm_s16le a.pcm
转换格式 - Move 'a.pcm' under 'main' directory
- Replace 'canon.pcm' with 'a.pcm' in 'CMakeLists.txt' under 'main' directory 改配置文件
- Replace '_binary_canon_pcm_start' and '_binary_canon_pcm_end' with '_binary_a_pcm_start' and '_binary_a_pcm_end' in
i2s_es8311_example.c
- Download the example and enjoy your own music