ESP32的I2S同步时钟BCK最高输出时钟达不到手册标的40MHZ

mr_jing
Posts: 6
Joined: Fri Nov 19, 2021 3:16 am

ESP32的I2S同步时钟BCK最高输出时钟达不到手册标的40MHZ

Postby mr_jing » Mon Nov 22, 2021 9:51 am

在idf的例程中复制项目i2s_basic,我尝试修改采样率来提高i2s的频率,当我设置采样率到150000,使用逻辑分析仪测量bck_io的频率为9.7MHz,我接着往大的设置,尝试了250000,350000....625000。发现bck_io的频率最高只能到15MHZ,设置高了也没有效果。

Code: Select all

/* I2S Example

    This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
    Every 5 seconds, it will change bits_per_sample [16, 24, 32] for i2s data

    This example code is in the Public Domain (or CC0 licensed, at your option.)

    Unless required by applicable law or agreed to in writing, this
    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#include <math.h>


#define SAMPLE_RATE     (150000)
#define I2S_NUM         (0)
#define WAVE_FREQ_HZ    (100)
#define PI              (3.14159265)
#define I2S_BCK_IO      (GPIO_NUM_4)
#define I2S_WS_IO       (GPIO_NUM_5)
#define I2S_DO_IO       (-1)
#define I2S_DI_IO       (GPIO_NUM_18)

#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)

static const char* TAG = "i2s_example";
int16_t i2s_readraw_buff[400];
static void setup_triangle_sine_waves(int bits)
{
    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);
        }

    }
    ESP_LOGI(TAG, "set clock");
    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
    ESP_LOGI(TAG, "write data");
    i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100);

    free(samples_data);
}

void app_main(void)
{
    //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
    //depend on bits_per_sample
    //using 6 buffers, we need 60-samples per buffer
    //if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
    //if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX,
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
        .channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
        .communication_format = I2S_COMM_FORMAT_STAND_MSB,
        .dma_buf_count = 6,
        .dma_buf_len = 60,
        .use_apll = true,
        .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_set_clk(I2S_NUM, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, 2);
    size_t bytes_read; 
    while (1) {
        // setup_triangle_sine_waves(test_bits);
        i2s_read(I2S_NUM, i2s_readraw_buff, 200, &bytes_read, portMAX_DELAY);
        printf("read i2s %d:",bytes_read);
        for(int i = 0;i < 200; i++)
        {
            printf("%x ",i2s_readraw_buff[i]);
        }
        printf("\n");
        vTaskDelay(5000/portTICK_RATE_MS);
    }
}

L-KAYA
Posts: 22
Joined: Thu Aug 12, 2021 3:44 am

Re: ESP32的I2S同步时钟BCK最高输出时钟达不到手册标的40MHZ

Postby L-KAYA » Tue Nov 23, 2021 3:48 am

mr_jing 您好,

非常感谢您的反馈,请问能否提供一下这些,以便定位具体问题:

1. IDF版本
2. 文档关于BCK最高能到40MHz表述的具体段落
3. 使用的逻辑分析仪采样频率

-----------------------------------------

从提供的代码中发现i2s_config_t中的mclk_multiple字段并未设置,默认为256,由于BCLK是由MCLK分频得到,而MCLK由APLL或PLL160M分频得到,想要获得较大的BCLK,就需要设置较小的MCLK分频系数以及BCLK分频系数,MCLK的分频系数是自适应的,没法改变,BCLK的分频系数与mclk_multiple即MCLK对于采样频率sample rate的倍数决定,关系如下:

MCLK = sample_rate * mclk_multiple
BCLK = sample_rate * channel_num * bits_per_sample
bclk_div = MCLK / BCLK

channel_format = I2S_CHANNEL_FMT_ALL_LEFT ——> channel_num = 2
bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT ——> bits_per_sample = 32

因此可以尝试将mclk_multiple取一较小值,但必须是channel_num * bits_per_sample的倍数
Attachments
Image Pasted at 2021-11-23 11-01.png
Image Pasted at 2021-11-23 11-01.png (29.84 KiB) Viewed 7015 times

mr_jing
Posts: 6
Joined: Fri Nov 19, 2021 3:16 am

Re: ESP32的I2S同步时钟BCK最高输出时钟达不到手册标的40MHZ

Postby mr_jing » Tue Nov 23, 2021 8:57 am

L-KAYA wrote:
Tue Nov 23, 2021 3:48 am
mr_jing 您好,

非常感谢您的反馈,请问能否提供一下这些,以便定位具体问题:

1. IDF版本
2. 文档关于BCK最高能到40MHz表述的具体段落
3. 使用的逻辑分析仪采样频率

-----------------------------------------

从提供的代码中发现i2s_config_t中的mclk_multiple字段并未设置,默认为256,由于BCLK是由MCLK分频得到,而MCLK由APLL或PLL160M分频得到,想要获得较大的BCLK,就需要设置较小的MCLK分频系数以及BCLK分频系数,MCLK的分频系数是自适应的,没法改变,BCLK的分频系数与mclk_multiple即MCLK对于采样频率sample rate的倍数决定,关系如下:

MCLK = sample_rate * mclk_multiple
BCLK = sample_rate * channel_num * bits_per_sample
bclk_div = MCLK / BCLK

channel_format = I2S_CHANNEL_FMT_ALL_LEFT ——> channel_num = 2
bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT ——> bits_per_sample = 32

因此可以尝试将mclk_multiple取一较小值,但必须是channel_num * bits_per_sample的倍数
感谢您的指导,我尝试修改了mclk_multiple,最终BCLK的频率可以输出40MHZ。
BLCK时钟波形:
Snipaste_2021-11-23_16-46-04.jpg
Snipaste_2021-11-23_16-46-04.jpg (44.54 KiB) Viewed 6963 times
修改如下:
#define SAMPLE_RATE (620000)
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
.use_apll = false,
.mclk_multiple = 128,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
};
按照理论计算BCLK = sample_rate * channel_num * bits_per_sample --> 40MHZ = sample_rate * 2 * 32 --> sample_rate = 625000,
实际测试:
sample_rate 设置为 620000,就已经输出40MHZ的BCLK,
sample_rate 设置为625000,反而输出不了40MHZ。

L-KAYA
Posts: 22
Joined: Thu Aug 12, 2021 3:44 am

Re: ESP32的I2S同步时钟BCK最高输出时钟达不到手册标的40MHZ

Postby L-KAYA » Wed Nov 24, 2021 5:53 am

mr_jing 您好,

对于您提到的频率不准确的问题,是由时钟源造成的。使用较高采样频率时,PLL160M的时钟分频精度较差,而APLL时钟源能够产生一个更精确的频率,但由于驱动中APLL分频系数计算方式的局限性,目前对于高采样频率的支持不够好,我们会尽快修复该问题,感谢您的反馈!

Ps: 当使用APLL时,其最高支持128MHz,而从APLL分频得到MCLK至少为2分频,即MCLK <= 64MHz,一般MCLK到BCLK也至少是2分频,所以此时BCLK <= 32MHz

Who is online

Users browsing this forum: Bing [Bot] and 35 guests