ESP32-S3 ADF音频播放器player应用开发学习记录_esp32音乐播放器

ESP32-S3 ADF音频播放器player应用开发学习记录

使用的开发板是 ESP32-S3-Korvo-2 V3.0,项目主要应用 播放SD卡音频 功能,所以开发板上其他组件功能未使用,主要学习开发创建播放器player播放音频功能。

开发板上使用的音频解码芯片是 ES8311和 音频功率放大器 NS4150。

组件介绍
音频编解码芯片 (Audio Codec Chip)音频编解码器芯片 ES8311 是一种低功耗单声道音频编解码器,包含单通道 ADC、单通道 DAC、低噪声前置放大器、耳机驱动器、数字音效、模拟混音和增益功能。它通过 I2S 和 I2C 总线与 ESP32-S3-WROOM-1 模组连接,以提供独立于音频应用程序的硬件音频处理。
音频功率放大器 (Audio PA Chip)NS4150 是一款低 EMI、3 W 单声道 D 类音频功率放大器,用于放大来自音频编解码芯片的音频信号,以驱动扬声器。

硬件部分

开发板使用引脚如下
管脚名称ES8311NS4150
IO16I2S0_MCLK
IO17I2C_SDA
IO18I2C_CLK
IO8I2S0_DSDIN
IO9I2S0_SCLK
IO45I2S0_LRCK
IO48PA_CTRL
开发板硬件原理图如下

请添加图片描述
请添加图片描述

程序部分

ESP32提供了几个简单的高级 API。它旨在基于标准化音频元素的典型互连快速实现音频应用。所以此次开发使用了高等级的接口 API (esp_audio_) 来构建 player。

可以参考例程: /examples/advanced_examples/esp_dispatcher_dueros/main/audio_setup.c

创建播放器

创建播放器具体分为六步:
Ⅰ 初始化音频解码芯片
Ⅱ 创建esp_audio实例
Ⅲ 启动编解码驱动程序
Ⅳ 添加音频输入流到特定的esp_audio实例
Ⅴ 添加解码器和编码器到esp_audio实例
Ⅵ 添加音频输出流到特定的esp_audio实例

void Setup\_player(void)
{	
    esp\_audio\_cfg\_t cfg = DEFAULT\_ESP\_AUDIO\_CONFIG();
    //硬件编解码器初始化
    board_handle = audio\_board\_init();
    cfg.vol_handle = board_handle->audio_hal;
    cfg.vol_set = (audio_volume_set)audio_hal_set_volume;
    cfg.vol_get = (audio_volume_get)audio_hal_get_volume;
    //esp\_audio适用于sepcific类型
    //ESP\_AUDIO\_PREFER\_MEM 在新管道启动之前停止了先前链接的元素,但out流元素除外
    cfg.prefer_type = ESP_AUDIO_PREFER_MEM;
    //目标采样率
    cfg.resample_rate = 48000;
    //根据 cfg 参数创建 esp\_audio实例
	player = esp\_audio\_create(&cfg);
	audio\_hal\_ctrl\_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);//启动/停止编解码驱动程序

    //添加音频输入流到特定的esp\_audio实例
	fatfs\_stream\_cfg\_t fatfs_cfg = FATFS\_STREAM\_CFG\_DEFAULT();
	fatfs_cfg.type = AUDIO_STREAM_READER;
	audio\_element\_handle\_t fatfs_stream_reader = fatfs\_stream\_init(&fatfs_cfg);
	esp\_audio\_input\_stream\_add(player, fatfs_stream_reader);

    //添加一个新的编解码器库,可以解码或编码音乐
	mp3\_decoder\_cfg\_t mp3_cfg = DEFAULT\_MP3\_DECODER\_CONFIG();
	audio\_element\_handle\_t mp3_decoder = mp3\_decoder\_init(&mp3_cfg);
    esp\_audio\_codec\_lib\_add(player, AUDIO_CODEC_TYPE_DECODER, mp3_decoder);
	
    //添加音频输出流到特定的esp\_audio实例
	i2s\_stream\_cfg\_t i2s_cfg = I2S\_STREAM\_CFG\_DEFAULT();
    i2s_cfg.i2s_config.sample_rate = 48000;//和编解码的采样率保持一致
	i2s_cfg.type = AUDIO_STREAM_WRITER;
	audio\_element\_handle\_t i2s_stream_writer = i2s\_stream\_init(&i2s_cfg);
	esp\_audio\_output\_stream\_add(player, i2s_stream_writer);	
}

Ⅰ.初始化音频解码芯片
   audio\_board\_handle\_t board_handle = audio\_board\_init();

具体初始化流程

\esp-adf\components\audio_board\esp32_s3_korvo2_v3\board.c

audio_board_init --> audio_board_codec_init(音频芯片初始化) --> es8311_codec_init
				 --> audio_board_adc_init(音频ADC初始化)

audio\_board\_handle\_t audio\_board\_init(void)
{
    if (board_handle) {
        ESP\_LOGW(TAG, "The board has already been initialized!");
        return board_handle;
    }
    board_handle = (audio\_board\_handle\_t) audio\_calloc(1, sizeof(struct audio\_board\_handle));
    AUDIO\_MEM\_CHECK(TAG, board_handle, return NULL);
    board_handle->audio_hal = audio\_board\_codec\_init();
    board_handle->adc_hal = audio\_board\_adc\_init(); 
    return board_handle;
}

audio\_hal\_handle\_t audio\_board\_codec\_init(void)
{
    audio\_hal\_codec\_config\_t audio_codec_cfg = AUDIO\_CODEC\_DEFAULT\_CONFIG();
    audio\_hal\_handle\_t codec_hal = audio\_hal\_init(&audio_codec_cfg, &AUDIO_CODEC_ES8311_DEFAULT_HANDLE);
    AUDIO\_NULL\_CHECK(TAG, codec_hal, return NULL);
    return codec_hal;
}

//初始化音频解码芯片的结构体
struct {
audio\_hal\_adc\_input\_t adc_input;/ \* !<设置adc通道\*/
audio\_hal\_dac\_output\_t dac_output;/ \* !<设置dac通道\*/
audio\_hal\_codec\_mode\_t codec_mode;/ \* !<选择编解码器模式:adc, dac或两者\*/
audio\_hal\_codec\_i2s\_iface\_t i2s_iface;/ \* !< set I2S接口配置\*/
} audio\_hal\_codec\_config\_t;

//编解码器E8311操作功能
AUDIO_CODEC_ES8311_DEFAULT_HANDLE = {
.audio_codec_initialize = es8311_codec_init //ES8311初始化
.audio_codec_deinitialize = es8311_codec_deinit,//去初始化
.audio_codec_ctrl = es8311_codec_ctrl_state,//控制状态
.audio_codec_config_iface = es8311_codec_config_i2s,//i2s配置
.audio_codec_set_mute = es8311_set_voice_mute,//设置静音
.audio_codec_set_volume = es8311_codec_set_voice_volume,//设置音量大小
.audio_codec_get_volume = es8311_codec_get_voice_volume,//获取音量大小
.audio_hal_lock = NULL
.handle = NULL;
};

ES8311初始化

\esp-adf\components\audio_hal\driver\es8311\es8311.c

esp\_err\_t es8311\_codec\_init(audio\_hal\_codec\_config\_t \*codec_cfg)
{
    uint8\_t datmp, regv;
    int coeff;
    esp\_err\_t ret = ESP_OK;
    i2c\_init(); // ESP32 in master mode ESP32主模式

    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, 0x30);
    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG02, 0x00);
    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG03, 0x10);
    ret |= es8311\_write\_reg(ES8311_ADC_REG16, 0x24);
    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG04, 0x10);
    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG05, 0x00);
    ret |= es8311\_write\_reg(ES8311_SYSTEM_REG0B, 0x00);
    ret |= es8311\_write\_reg(ES8311_SYSTEM_REG0C, 0x00);
    ret |= es8311\_write\_reg(ES8311_SYSTEM_REG10, 0x1F);
    ret |= es8311\_write\_reg(ES8311_SYSTEM_REG11, 0x7F);
    ret |= es8311\_write\_reg(ES8311_RESET_REG00, 0x80);
    /\*
 \* Set Codec into Master or Slave mode 设置编解码器为主或从模式
 \*/
    regv = es8311\_read\_reg(ES8311_RESET_REG00);
    /\*
 \* Set master/slave audio interface 设置主\从音频接口
 \*/
    audio\_hal\_codec\_i2s\_iface\_t \*i2s_cfg = &(codec_cfg->i2s_iface);
    switch (i2s_cfg->mode) {
        case AUDIO_HAL_MODE_MASTER:    /\* MASTER MODE 主模式\*/
            ESP\_LOGI(TAG, "ES8311 in Master mode");
            regv |= 0x40;
            break;
        case AUDIO_HAL_MODE_SLAVE:    /\* SLAVE MODE 从模式\*/
            ESP\_LOGI(TAG, "ES8311 in Slave mode");
            regv &= 0xBF;
            break;
        default:
            regv &= 0xBF;
    }
    ret |= es8311\_write\_reg(ES8311_RESET_REG00, regv);
    ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, 0x3F);
    /\*
 \* Select clock source for internal mclk 为内部时钟选择时钟源
 \*/
    switch (get\_es8311\_mclk\_src()) {
        case FROM_MCLK_PIN:
            regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG01);
            regv &= 0x7F;
            ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, regv);
            break;
        case FROM_SCLK_PIN:
            regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG01);
            regv |= 0x80;
            ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, regv);
            break;
        default:
            regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG01);
            regv &= 0x7F;
            ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, regv);
            break;
    }
    int sample_fre = 0;
    int mclk_fre = 0;
    switch (i2s_cfg->samples) {
        case AUDIO_HAL_08K_SAMPLES:
            sample_fre = 8000;
            break;
        case AUDIO_HAL_11K_SAMPLES:
            sample_fre = 11025;
            break;
        case AUDIO_HAL_16K_SAMPLES:
            sample_fre = 16000;
            break;
        case AUDIO_HAL_22K_SAMPLES:
            sample_fre = 22050;
            break;
        case AUDIO_HAL_24K_SAMPLES:
            sample_fre = 24000;
            break;
        case AUDIO_HAL_32K_SAMPLES:
            sample_fre = 32000;
            break;
        case AUDIO_HAL_44K_SAMPLES:
            sample_fre = 44100;
            break;
        case AUDIO_HAL_48K_SAMPLES:
            sample_fre = 48000;
            break;
        default:
            ESP\_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre);
            break;
    }
    mclk_fre = sample_fre \* MCLK_DIV_FRE;
    coeff = get\_coeff(mclk_fre, sample_fre);
    if (coeff < 0) {
        ESP\_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre);
        return ESP_FAIL;
    }
    /\*
 \* Set clock parammeters 设置时钟参数
 \*/
    if (coeff >= 0) {
        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG02) & 0x07;
        regv |= (coeff_div[coeff].pre_div - 1) << 5;
        datmp = 0;
        switch (coeff_div[coeff].pre_multi) {
            case 1:
                datmp = 0;
                break;
            case 2:
                datmp = 1;
                break;
            case 4:
                datmp = 2;
                break;
            case 8:
                datmp = 3;
                break;
            default:
                break;
        }

        if (get\_es8311\_mclk\_src() == FROM_SCLK_PIN) {
            datmp = 3;     /\* DIG\_MCLK = LRCK \* 256 = BCLK \* 8 \*/
        }
        regv |= (datmp) << 3;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG02, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG05) & 0x00;
        regv |= (coeff_div[coeff].adc_div - 1) << 4;
        regv |= (coeff_div[coeff].dac_div - 1) << 0;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG05, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG03) & 0x80;
        regv |= coeff_div[coeff].fs_mode << 6;
        regv |= coeff_div[coeff].adc_osr << 0;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG03, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG04) & 0x80;
        regv |= coeff_div[coeff].dac_osr << 0;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG04, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG07) & 0xC0;
        regv |= coeff_div[coeff].lrck_h << 0;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG07, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG08) & 0x00;
        regv |= coeff_div[coeff].lrck_l << 0;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG08, regv);

        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG06) & 0xE0;
        if (coeff_div[coeff].bclk_div < 19) {
            regv |= (coeff_div[coeff].bclk_div - 1) << 0;
        } else {
            regv |= (coeff_div[coeff].bclk_div) << 0;
        }
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG06, regv);
    }

    /\* 
 \* mclk inverted or not MCLK是否倒置
 \*/
    if (INVERT_MCLK) {
        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG01);
        regv |= 0x40;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, regv);
    } else {
        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG01);
        regv &= ~(0x40);
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG01, regv);
    }
    /\*
 \* sclk inverted or not SCLK是否倒置
 \*/
    if (INVERT_SCLK) {
        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG06);
        regv |= 0x20;
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG06, regv);
    } else {
        regv = es8311\_read\_reg(ES8311_CLK_MANAGER_REG06);
        regv &= ~(0x20);
        ret |= es8311\_write\_reg(ES8311_CLK_MANAGER_REG06, regv);
    }

    ret |= es8311\_write\_reg(ES8311_SYSTEM_REG13, 0x10);
    ret |= es8311\_write\_reg(ES8311_ADC_REG1B, 0x0A);
    ret |= es8311\_write\_reg(ES8311_ADC_REG1C, 0x6A);

    /\* pa power gpio init 初始化功放引脚\*/
    gpio\_config\_t  io_conf;
    memset(&io_conf, 0, sizeof(io_conf));
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = BIT64(get\_pa\_enable\_gpio());
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio\_config(&io_conf);
    /\* enable pa power 使能功放电源\*/
    es8311\_pa\_power(true);

    codec\_dac\_volume\_config\_t vol_cfg = ES8311\_DAC\_VOL\_CFG\_DEFAULT();
    dac_vol_handle = audio\_codec\_volume\_init(&vol_cfg);
    return ESP_OK;
}

#define PA\_ENABLE\_GPIO GPIO\_NUM\_48

int8\_t get\_pa\_enable\_gpio(void)
{
    return PA_ENABLE_GPIO;
}

void es8311\_pa\_power(bool enable)
{
    if (enable) {
        gpio\_set\_level(get\_pa\_enable\_gpio(), 1);
    } else {
        gpio\_set\_level(get\_pa\_enable\_gpio(), 0);
    }
}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

img

img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)

img

最后

资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!

&物联网开发知识点,真正体系化!**

[外链图片转存中…(img-5oElaQEq-1712239729999)]

[外链图片转存中…(img-rLHFyAFL-1712239729999)]

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)

img

最后

资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!

更多资料点击此处获qu!!

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用: 在Ubuntu 20.04.4 LTS操作系统中,我已经安装了ESP-idf开发环境并使用了一段时间。现在我想安装esp-adf音频开发框架,但遇到了问题。可以在https://***/index.html找到相关文档。 引用: LinkGUI™ Air E32是一个开发平台,基于ESP32 WROVER芯片组,可以实现完美的图形交互体验。它将GUI系统嵌入到乐鑫开源的ESP-ADF/ESP-IDF开发环境中,提供了丰富的UI交互功能。LinkGUI™ Air E32平台由ESP32 WROVER芯片、LCD、按键、T卡插槽、喇叭插槽、麦克风和扩展接口组成。它的SDK以基于ESP-ADF的LinkGUI图形系统为核心,与ESP-ADF开发环境无缝连接,实现了WIFI/BT/音频和高级图形交互的功能。 根据您的问题,您想了解在Ubuntu操作系统上使用esp-adf和lvgl。首先,esp-adf是一个音频开发框架,用于在ESP32芯片上开发音频应用程序。它提供了丰富的音频功能和示例,可以实现音频播放、录制、语音识别等功能。 而lvgl是一个开源的图形库,用于创建嵌入式设备上的用户界面。它提供了丰富的图形元素和交互功能,可以实现按钮、滑块、进度条、列表等常见的UI元素。 在Ubuntu操作系统上,您可以按照ESP-ADF文档中提供的步骤安装和配置esp-adf开发环境。然后,您可以使用lvgl库开发具有图形界面的应用程序。您可以参考lvgl的官方文档和示例来学习如何使用lvgl库进行UI设计和交互。 总结起来,esp-adf是一个音频开发框架,lvgl是一个图形库。您可以在Ubuntu操作系统上使用esp-adf开发音频应用程序,并结合lvgl库创建图形界面。希望这能帮助到您。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Ubuntn环境下安装ESP 音频框架时出现Failed to resolve component ‘jsmn‘.问题(已解决)](https://blog.csdn.net/qq_41601311/article/details/124293303)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [基于ESP-ADF的图形系统](https://blog.csdn.net/skdev/article/details/120765588)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值