简介

I2S(Inter-IC Sound,集成电路内置音频总线)是一种同步串行通信协议,通常用于在两个数字音频设备之间传输音频数据。

ESP32-S3 包含 2 个 I2S 外设。通过配置这些外设,可以借助 I2S 驱动来输入和输出采样数据。

标准和 TDM 模式下的 I2S 总线包含以下几条线路:

  • MCLK:主时钟线。该信号线可选,具体取决于从机,主要用于向 I2S 从机提供参考时钟。

  • BCLK:位时钟线。用于数据线的位时钟。

  • WS:字(声道)选择线。通常用于识别声道(除 PDM 模式外)。

  • DIN/DOUT:串行数据输入/输出线。如果 DIN 和 DOUT 被配置到相同的 GPIO,数据将在内部回环

I2S通信模式 - 标准模式

标准模式中有且仅有左右两个声道,驱动中将声道称为 slot。这些声道可以支持 8/16/24/32 位宽的采样数据。

代码

初始化

这段代码主要用于初始化NS4168模块的I2S传输通道。首先,通过调用宏 I2S_CHANNEL_DEFAULT_CONFIG 自动生成一个I2S通道配置结构,此处传入 I2S_NUM_AUTO 表示自动分配通道编号,并设置角色为主模式(I2S_ROLE_MASTER)。接着,调用 i2s_new_channel 函数创建新的I2S通道,并使用 ESP_ERROR_CHECK 宏进行错误检查,确保通道创建成功。

随后,代码构造了一个标准的I2S配置结构 std_cfg。在该结构中,clk_cfg 字段利用 I2S_STD_CLK_DEFAULT_CONFIG(16000) 设置了采样率为16kHz的时钟配置;而 slot_cfg 则通过 I2S_STD_MSB_SLOT_DEFAULT_CONFIG 来指定数据传输格式,这里采用32位数据宽度(I2S_DATA_BIT_WIDTH_32BIT)以及单声道模式(I2S_SLOT_MODE_MONO)。这决定了数据传输时每个样本的比特数和通道数。

接下来的 gpio_cfg 字段则为I2S信号分配具体的GPIO引脚。代码中将 mclk(主时钟)标记为未使用(I2S_GPIO_UNUSED),说明此示例中不需要使用主时钟信号,而 bclk(位时钟)、ws(字选择信号)和 dout(数据输出)分别对应预定义的 NS4168_IIS_BCK、NS4168_IIS_WS 和 NS4168_IIS_DATA。din(数据输入)同样未被使用。对于invert_flags结构,所有相关时钟信号的反转均设置为false,表示信号不做反转处理。

最后,通过调用 i2s_channel_init_std_mode 函数,将先前创建的I2S通道与定义好的标准配置结构 std_cfg 进行绑定,并再次通过 ESP_ERROR_CHECK 检查初始化是否成功。如果所有配置和初始化过程都顺利完成,该函数最终返回 ESP_OK,表明NS4168模块的I2S传输通道已经成功初始化,可以进行后续的音频数据传输。

esp_err_t ns4168_init() {
  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 std_cfg = {
      .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
      .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
                                                  I2S_SLOT_MODE_STEREO),
      .gpio_cfg =
          {
              .mclk = I2S_GPIO_UNUSED,  // some codecs may require mclk signal,
                                        // this example doesn't need it
              .bclk = NS4168_IIS_BCK,
              .ws = NS4168_IIS_WS,
              .dout = NS4168_IIS_DATA,
              .din = I2S_GPIO_UNUSED,
              .invert_flags =
                  {
                      .mclk_inv = false,
                      .bclk_inv = false,
                      .ws_inv = false,
                  },
          },
  };
  ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
  return ESP_OK;
}

播放wav音频

函数参数是一个指向字符数组的指针,代表要播放的音频文件的路径。函数首先检查传入的文件路径是否为空,如果为空则打印错误信息并终止任务。

接下来,函数为写入数据分配了一个2048字节的缓冲区。如果内存分配失败,同样会打印错误信息并删除任务。然后函数尝试以二进制读模式打开指定的文件,如果文件打开失败,则释放之前分配的缓冲区并终止任务。

一旦文件成功打开,函数使用fseek将文件指针移动到偏移量为44的位置,这通常用于跳过WAV文件的头部信息。之后,函数启用I2S传输通道,并进入一个无限循环。在每次循环中,通过fread从文件中读取最多2048字节的数据到缓冲区。如果读到的数据字节数为0,则说明文件已到末尾,循环退出。

在读取到数据后,函数先调用i2s_channel_preload_data将数据预加载到I2S通道,然后调用i2s_channel_write将数据写入I2S通道,并根据写入结果打印相应信息。如果本次读取的数据少于2048字节,也会认为文件播放完毕,从而退出循环。最后,函数关闭文件,释放缓冲区,并调用vTaskDelete删除当前任务。

static void i2s_play_task(void *args) {
  const char *file_path = (const char *)args;
  if (file_path == NULL) {
    printf("File path is NULL\n");
    vTaskDelete(NULL);
    return;
  }
  uint8_t *w_buf = (uint8_t *)calloc(1, 2048);
  if (w_buf == NULL) {
    printf("Failed to allocate memory for write buffer\n");
    vTaskDelete(NULL);
    return;
  }
  FILE *fp = fopen(file_path, "rb");
  if (fp == NULL) {
    printf("Failed to open file: %s\n", file_path);
    free(w_buf);
    vTaskDelete(NULL);
    return;
  }
  fseek(fp, 44, SEEK_SET);
  size_t w_bytes = 0;
  size_t read_bytes = 0;
  ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
  while (1) {
    read_bytes = fread(w_buf, 1, 2048, fp);
    if (read_bytes == 0) {
      printf("End of file reached\n");
      break;
    }
    i2s_channel_preload_data(tx_chan, w_buf, read_bytes, &w_bytes);
    if (i2s_channel_write(tx_chan, w_buf, read_bytes, &w_bytes,
                          portMAX_DELAY) == ESP_OK) {
      printf("I2S write %d bytes\n", (int)w_bytes);
    } else {
      printf("I2S write failed\n");
    }
    if (read_bytes < 2048) {
      printf("Partial buffer written, file playback complete\n");
      break;
    }
  }
  fclose(fp);
  free(w_buf);
  vTaskDelete(NULL);
}

格式化音频文件

不是任何一个wav音频文件都能够正确播放,需要根据I2S的配置,格式化音频文件;使用GoldWave编辑文件
如下图所示,格式为:
Wave PCM signed 16 bit, 16000Hz, 512kbps, stereo
音频属性

修改sampling

在这里插入图片描述
在这里插入图片描述

格式化

格式化是在保存的时候
Format

破音问题

遇到破音的问题,可能与喇叭有关系,可能需要将音频的音量调低
调整音量

Reference

esp 5.4.1 i2s std example
esp i2s 编程指南

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐