ESP32 快速入门(十二): i2s 外设驱动实践

此篇博客从 esp-idf 的 i2s 示例 出发来学习实践 i2s。

1. i2s 配置

直接查看 i2s 示例中配置 i2s 的部分。

    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX,                                  // Only TX
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = 16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
        .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
        .dma_buf_count = 6,
        .dma_buf_len = 60,
        .use_apll = false,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1                                //Interrupt level 1
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCK_IO,
        .ws_io_num = I2S_WS_IO,
        .data_out_num = I2S_DO_IO,
        .data_in_num = I2S_DI_IO                                               //Not used
    };
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM, &pin_config);

以上我们可以发现使用 i2s 前需要配置使用以下内容 & API:

  • 配置 i2s_config
    • mode 设置模式,目前只支持 TX
    • sample_rate 设置采样率
    • bits_per_sample 设置采样深度
    • channel_format 设置左右声道
    • communication_format 设置交流格式
    • dma_buf_count 设置 DMA Buffer 计数
    • dma_buf_len 设置 DMA Buffer 长度
    • use_apll 设置是否获得精确时钟
    • intr_alloc_flags 设置用来分配中断
  • 配置 pin_config
    • bck_io_num 设置串行时钟引脚
    • ws_io_num 设置左右声道的时钟引脚
    • data_out_num 设置数据输出引脚
    • data_in_num 此条一般设置为 -1
  • 使用 i2s_driver_install 来安装 i2s 驱动,函数定义为 esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue)
  • 使用 i2s_set_pin 来设置 i2s 引脚,函数定义为 esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin)

2. i2s 使用

直接查看 i2s 示例中使用 i2s 的部分。

    int *samples_data = malloc(((bits+8)/16)*SAMPLE_PER_CYCLE*4);
    unsigned int i, sample_val;
    double sin_float, triangle_float, triangle_step = (double) pow(2, bits) / SAMPLE_PER_CYCLE;
    size_t i2s_bytes_write = 0;

    printf("\r\nTest bits=%d free mem=%d, written data=%d\n", bits, esp_get_free_heap_size(), ((bits+8)/16)*SAMPLE_PER_CYCLE*4);

    triangle_float = -(pow(2, bits)/2 - 1);

    for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
        sin_float = sin(i * 2 * PI / SAMPLE_PER_CYCLE);
        if(sin_float >= 0)
            triangle_float += triangle_step;
        else
            triangle_float -= triangle_step;

        sin_float *= (pow(2, bits)/2 - 1);

        if (bits == 16) {
            sample_val = 0;
            sample_val += (short)triangle_float;
            sample_val = sample_val << 16;
            sample_val += (short) sin_float;
            samples_data[i] = sample_val;
        } else if (bits == 24) { //1-bytes unused
            samples_data[i*2] = ((int) triangle_float) << 8;
            samples_data[i*2 + 1] = ((int) sin_float) << 8;
        } else {
            samples_data[i*2] = ((int) triangle_float);
            samples_data[i*2 + 1] = ((int) sin_float);
        }

    }

    i2s_set_clk(I2S_NUM, SAMPLE_RATE, bits, 2);
    //Using push
    // for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
    //     if (bits == 16)
    //         i2s_push_sample(0, &samples_data[i], 100);
    //     else
    //         i2s_push_sample(0, &samples_data[i*2], 100);
    // }
    // or write
    i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100);

    free(samples_data);

以上我们可以发现使用 i2s 需要使用以下内容 & API:

  • 首先创建一个空间来储存要播放的音频,如上述代码中的 samples_data
  • samples_data 中放置需要播放的音频数据段
  • 使用 i2s_set_clk(I2S_NUM, SAMPLE_RATE, bits, 2) 进行时钟调整,函数定义为 esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, i2s_bits_per_sample_t bits, i2s_channel_t ch)
  • 使用 i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100) 进行音频播放,函数定义为 i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait)
  • 循环以上步骤来进行完整的音频播放

3. i2s 注意事项

  1. i2s 一次读取播放的字节选择 512 或 1024 字节比较合适,字节效果越大越好,但是越大越吃资源。
  2. i2s_set_clk API 如果较 i2s 初始配置有太多改动,考虑在此 API 后加个 1 ms 的延时。
  3. 如果提前知道音频的信息(采样率,单双声道等),可以在 i2s config 里提前配置。
  4. BCLK = sample_rate x sample_length x channel

你可能感兴趣的:(ESP32,PERIPHERALS)