【原创】ESP32-S3 Audio MAX98357A Audio

软件环境:Win10 + VSC & esp-idf插件 idf v5.2.1
硬件:waveshare微雪 ESP32-S3-Touch-LCD-1.28

  1. 通过menuconfig修改分区,使用自定义分区文件partitions.csv:
# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     ,  0x6000,
phy_init, data, phy,     ,  0x1000,
factory,  app,  factory,   , 13M,

  1. 修改CMakeLists.txt加入i2s_test.co.pcm
  2. i2s_test.c文件内容:
/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include <stdint.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/i2s_std.h"
#include "driver/i2s_pdm.h"
#include "driver/i2s_tdm.h"
#include "driver/gpio.h"
#include "esp_check.h"
#include "sdkconfig.h"

#include "i2s_test.h"

/* Set 1 to allocate rx & tx channels in duplex mode on a same I2S controller, they will share the BCLK and WS signal
 * Set 0 to allocate rx & tx channels in simplex mode, these two channels will be totally separated,
 * Specifically, due to the hardware limitation, the simplex rx & tx channels can't be registered on the same controllers on ESP32 and ESP32-S2,
 * and ESP32-S2 has only one I2S controller, so it can't allocate two simplex channels */
#define EXAMPLE_I2S_DUPLEX_MODE 0

#define EXAMPLE_STD_BCLK_IO2 GPIO_NUM_16 // I2S bit clock io number
#define EXAMPLE_STD_WS_IO2 GPIO_NUM_17   // I2S word select io number
#define EXAMPLE_STD_DOUT_IO2 GPIO_NUM_15 // I2S data out io number
#define EXAMPLE_STD_DIN_IO2 GPIO_NUM_NC  // I2S data in io number

#define EXAMPLE_BUFF_SIZE 2048

extern const uint8_t pcm_start[] asm("_binary_o_pcm_start");
extern const uint8_t pcm_end[] asm("_binary_o_pcm_end");

static i2s_chan_handle_t tx_chan; // I2S tx channel handler

void i2s_example_write_task(void *args)
{
#if 0
    uint8_t *w_buf = (uint8_t *)calloc(1, EXAMPLE_BUFF_SIZE);
    assert(w_buf); // Check if w_buf allocation success

    /* Assign w_buf */
    for (int i = 0; i < EXAMPLE_BUFF_SIZE; i += 8) {
        w_buf[i]     = 0x12;
        w_buf[i + 1] = 0x34;
        w_buf[i + 2] = 0x56;
        w_buf[i + 3] = 0x78;
        w_buf[i + 4] = 0x9A;
        w_buf[i + 5] = 0xBC;
        w_buf[i + 6] = 0xDE;
        w_buf[i + 7] = 0xF0;
    }

    size_t w_bytes = EXAMPLE_BUFF_SIZE;

    /* (Optional) Preload the data before enabling the TX channel, so that the valid data can be transmitted immediately */
    while (w_bytes == EXAMPLE_BUFF_SIZE) {
        /* Here we load the target buffer repeatedly, until all the DMA buffers are preloaded */
        ESP_ERROR_CHECK(i2s_channel_preload_data(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes));
    }
#endif

    uint16_t *buffer = calloc(1, EXAMPLE_BUFF_SIZE * 2);

    size_t w_bytes = 0;
    uint32_t offset = 0;

    /* Enable the TX channel */
    ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));

    while (1)
    {
#if 0
        /* Write i2s data */
        if (i2s_channel_write(tx_chan, w_buf, EXAMPLE_BUFF_SIZE, &w_bytes, 1000) == ESP_OK) {
            printf("Write Task: i2s write %d bytes\n", w_bytes);
        } else {
            printf("Write Task: i2s write failed\n");
        }
        vTaskDelay(pdMS_TO_TICKS(200));
#else
        /* Write i2s data */
        if (i2s_channel_write(tx_chan, buffer, EXAMPLE_BUFF_SIZE * 2, &w_bytes, 1000) != ESP_OK)
        {
            printf("Write Task: i2s write failed\n");
        }
        if (offset > (pcm_end - pcm_start))
        {
            break;
        }

        for (int i = 0; i < EXAMPLE_BUFF_SIZE; i++)
        {
            offset++;
            buffer[i] = pcm_start[offset] << 3;
        }
#endif
    }
    free(buffer);
    ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
    vTaskDelete(NULL);
}

void i2s_example_init_std_simplex(void)
{
    /* Setp 1: Determine the I2S channel configuration and allocate two channels one by one
     * The default configuration can be generated by the helper macro,
     * it only requires the I2S controller id and I2S role
     * The tx and rx channels here are registered on different I2S controller,
     * Except ESP32 and ESP32-S2, others allow to register two separate tx & rx channels on a same controller */

    i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
    ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_chan, NULL));

    i2s_std_config_t tx_std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED, // some codecs may require mclk signal, this example doesn't need it
            .bclk = EXAMPLE_STD_BCLK_IO2,
            .ws = EXAMPLE_STD_WS_IO2,
            .dout = EXAMPLE_STD_DOUT_IO2,
            .din = EXAMPLE_STD_DIN_IO2,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false,
            },
        },
    };

    /* Default is only receiving left slot in mono mode,
     * update to right here to show how to change the default configuration */
    // tx_std_cfg.slot_cfg.slot_mask = I2S_STD_SLOT_RIGHT;
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &tx_std_cfg));
}

  1. clean and rebuild

音乐文件转换:ffmpeg -i music.mp3 -f u8 -ar 44100 -ac 1 o.pcm

posted @ 2024-05-22 14:20  yinsua  阅读(145)  评论(0编辑  收藏  举报