mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Merge branch 'feat/i2s_tx_sync_esp31s31_v6.1' into 'release/v6.1'
feat(i2s): support TX FIFO sync (v6.1) See merge request espressif/esp-idf!50338
This commit is contained in:
@@ -12,5 +12,5 @@ endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity esp_driver_pcnt spi_flash
|
||||
esp_driver_gpio esp_driver_i2s esp_driver_uart esp_psram
|
||||
esp_driver_gpio esp_driver_i2s esp_driver_uart esp_psram esp_driver_gptimer
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in I2S driver, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-360)
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-450)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_common.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC
|
||||
#include "driver/i2s_etm.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "esp_etm.h"
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTS_PDM
|
||||
#include "driver/i2s_pdm.h"
|
||||
#endif
|
||||
@@ -1364,6 +1369,178 @@ TEST_CASE("I2S_rate_tunning", "[i2s]")
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC
|
||||
#define I2S_TX_SYNC_TEST_TIMER_RES_HZ (1000 * 1000)
|
||||
#define I2S_TX_SYNC_TEST_ALARM_COUNT (5000)
|
||||
#define I2S_TX_SYNC_TEST_IDEAL_CNT (1000)
|
||||
#define I2S_TX_SYNC_TEST_MANUAL_THRESH (64)
|
||||
#define I2S_TX_SYNC_TEST_BUF_SIZE (4096)
|
||||
|
||||
typedef struct {
|
||||
SemaphoreHandle_t sem;
|
||||
int32_t diff_count;
|
||||
} i2s_tx_sync_test_ctx_t;
|
||||
|
||||
static IRAM_ATTR bool i2s_tx_sync_test_callback(i2s_chan_handle_t handle, const i2s_sync_event_data_t *event, void *user_ctx)
|
||||
{
|
||||
(void)handle;
|
||||
i2s_tx_sync_test_ctx_t *ctx = (i2s_tx_sync_test_ctx_t *)user_ctx;
|
||||
ctx->diff_count = event->diff_count;
|
||||
|
||||
BaseType_t need_yield = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->sem, &need_yield);
|
||||
return need_yield == pdTRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("I2S TX sync callback can be registered while running", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle = NULL;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
std_cfg.gpio_cfg.mclk = -1;
|
||||
#if CONFIG_IDF_TARGET_ESP32S31
|
||||
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
|
||||
#endif
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
|
||||
i2s_event_callbacks_t dma_cbs = {
|
||||
.on_sent = i2s_tx_on_sent_callback,
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_channel_register_event_callback(tx_handle, &dma_cbs, NULL));
|
||||
|
||||
i2s_event_callbacks_t sync_cbs = {
|
||||
.on_tx_sync_evt = i2s_tx_sync_test_callback,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &sync_cbs, NULL));
|
||||
sync_cbs.on_tx_sync_evt = NULL;
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &sync_cbs, NULL));
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("I2S TX sync callback is triggered by GPTimer ETM alarm", "[i2s][etm]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle = NULL;
|
||||
gptimer_handle_t timer = NULL;
|
||||
esp_etm_event_handle_t timer_event = NULL;
|
||||
esp_etm_task_handle_t i2s_sync_task = NULL;
|
||||
esp_etm_channel_handle_t etm_channel = NULL;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
i2s_tx_sync_test_ctx_t cb_ctx = {
|
||||
.sem = xSemaphoreCreateBinary(),
|
||||
};
|
||||
TEST_ASSERT_NOT_NULL(cb_ctx.sem);
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.dma_desc_num = 4;
|
||||
chan_cfg.dma_frame_num = 256;
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
std_cfg.gpio_cfg.mclk = -1;
|
||||
#if CONFIG_IDF_TARGET_ESP32S31
|
||||
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
|
||||
#endif
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
|
||||
buf = (uint8_t *)calloc(1, I2S_TX_SYNC_TEST_BUF_SIZE);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
size_t bytes_loaded = 0;
|
||||
TEST_ESP_OK(i2s_channel_preload_data(tx_handle, buf, I2S_TX_SYNC_TEST_BUF_SIZE, &bytes_loaded));
|
||||
TEST_ASSERT_GREATER_THAN(0, bytes_loaded);
|
||||
|
||||
i2s_tx_fifo_sync_config_t sync_cfg = {
|
||||
.auto_suppl_thresh = 0,
|
||||
.manual_suppl_thresh = I2S_TX_SYNC_TEST_MANUAL_THRESH,
|
||||
.ideal_cnt = I2S_TX_SYNC_TEST_IDEAL_CNT,
|
||||
.suppl_mode = I2S_TX_FIFO_SYNC_SUPPL_MODE_LAST_DATA,
|
||||
};
|
||||
i2s_event_callbacks_t cbs = {
|
||||
.on_tx_sync_evt = i2s_tx_sync_test_callback,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &cbs, &cb_ctx));
|
||||
|
||||
i2s_sync_count_t sync_count = {};
|
||||
TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, &sync_count, true));
|
||||
TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, &sync_count, false));
|
||||
printf("TX sync API before start: diff=%"PRId32", fifo=%"PRIu32", bclk=%"PRIu32"\n",
|
||||
sync_count.diff_count, sync_count.fifo_count, sync_count.bclk_count);
|
||||
|
||||
i2s_etm_task_config_t i2s_task_cfg = {
|
||||
.task_type = I2S_ETM_TASK_SYNC_FIFO,
|
||||
};
|
||||
TEST_ESP_OK(i2s_new_etm_task(tx_handle, &i2s_task_cfg, &i2s_sync_task));
|
||||
|
||||
gptimer_config_t timer_cfg = {
|
||||
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = I2S_TX_SYNC_TEST_TIMER_RES_HZ,
|
||||
};
|
||||
TEST_ESP_OK(gptimer_new_timer(&timer_cfg, &timer));
|
||||
gptimer_alarm_config_t alarm_cfg = {
|
||||
.alarm_count = I2S_TX_SYNC_TEST_ALARM_COUNT,
|
||||
};
|
||||
TEST_ESP_OK(gptimer_set_alarm_action(timer, &alarm_cfg));
|
||||
|
||||
gptimer_etm_event_config_t timer_event_cfg = {
|
||||
.event_type = GPTIMER_ETM_EVENT_ALARM_MATCH,
|
||||
};
|
||||
TEST_ESP_OK(gptimer_new_etm_event(timer, &timer_event_cfg, &timer_event));
|
||||
|
||||
esp_etm_channel_config_t etm_cfg = {};
|
||||
TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_channel));
|
||||
TEST_ESP_OK(esp_etm_channel_connect(etm_channel, timer_event, i2s_sync_task));
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
// TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_channel_enable_tx_fifo_sync(tx_handle, true));
|
||||
TEST_ESP_OK(i2s_channel_config_tx_fifo_sync(tx_handle, &sync_cfg));
|
||||
TEST_ESP_OK(i2s_channel_enable_tx_fifo_sync(tx_handle, true));
|
||||
TEST_ESP_OK(esp_etm_channel_enable(etm_channel));
|
||||
TEST_ESP_OK(gptimer_enable(timer));
|
||||
TEST_ESP_OK(gptimer_start(timer));
|
||||
|
||||
bool callback_triggered = xSemaphoreTake(cb_ctx.sem, pdMS_TO_TICKS(100)) == pdTRUE;
|
||||
printf("TX sync callback: triggered=%d, diff=%"PRId32"\n", callback_triggered, cb_ctx.diff_count);
|
||||
|
||||
TEST_ESP_OK(gptimer_stop(timer));
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable_tx_fifo_sync(tx_handle, false));
|
||||
cbs.on_tx_sync_evt = NULL;
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &cbs, NULL));
|
||||
TEST_ESP_OK(gptimer_disable(timer));
|
||||
TEST_ESP_OK(esp_etm_channel_disable(etm_channel));
|
||||
|
||||
TEST_ESP_OK(esp_etm_del_event(timer_event));
|
||||
TEST_ESP_OK(esp_etm_del_task(i2s_sync_task));
|
||||
TEST_ESP_OK(esp_etm_del_channel(etm_channel));
|
||||
TEST_ESP_OK(gptimer_del_timer(timer));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
vSemaphoreDelete(cb_ctx.sem);
|
||||
free(buf);
|
||||
|
||||
TEST_ASSERT_TRUE(callback_triggered);
|
||||
|
||||
/* SYNC_CHECK fires at 0.005 s. About 48000 * 2 * 0.005 = 480 samples
|
||||
* are sent, so ideal_cnt 1000 should produce diff_count around -520.
|
||||
*/
|
||||
TEST_ASSERT_INT32_WITHIN(52, -520, cb_ctx.diff_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("i2s_destination_test", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx = NULL;
|
||||
@@ -1403,7 +1580,14 @@ TEST_CASE("i2s_destination_test", "[i2s]")
|
||||
chan_cfg.tx_destination = I2S_DESTINATION_BT;
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx, NULL));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx, &std_cfg));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_register_event_callback(tx, &cbs, NULL));
|
||||
// DMA event callbacks rely on the DMA data path and are not available on the Bluetooth path.
|
||||
i2s_event_callbacks_t tx_dma_cbs = { .on_sent = i2s_tx_on_sent_callback };
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_register_event_callback(tx, &tx_dma_cbs, NULL));
|
||||
#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC
|
||||
// TX FIFO sync callback is a peripheral interrupt event and is available regardless of the data path.
|
||||
i2s_event_callbacks_t tx_sync_cbs = { .on_tx_sync_evt = i2s_tx_sync_test_callback };
|
||||
TEST_ESP_OK(i2s_channel_register_event_callback(tx, &tx_sync_cbs, NULL));
|
||||
#endif
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_preload_data(tx, buf, sizeof(buf), &loaded));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_write(tx, buf, sizeof(buf), &written, 0));
|
||||
TEST_ESP_OK(i2s_del_channel(tx));
|
||||
@@ -1414,7 +1598,9 @@ TEST_CASE("i2s_destination_test", "[i2s]")
|
||||
chan_cfg.rx_destination = I2S_DESTINATION_BT;
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, NULL, &rx));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx, &std_cfg));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_register_event_callback(rx, &cbs, NULL));
|
||||
// DMA event callbacks rely on the DMA data path and are not available on the Bluetooth path.
|
||||
i2s_event_callbacks_t rx_dma_cbs = { .on_recv = i2s_rx_on_recv_callback };
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_register_event_callback(rx, &rx_dma_cbs, NULL));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, i2s_channel_read(rx, buf, sizeof(buf), &read_bytes, 0));
|
||||
TEST_ESP_OK(i2s_del_channel(rx));
|
||||
rx = NULL;
|
||||
|
||||
@@ -412,12 +412,18 @@ static void test_i2s_external_clk_src(bool is_master, bool is_external)
|
||||
} else {
|
||||
unity_wait_for_signal("Master Finished");
|
||||
}
|
||||
if (!is_external) {
|
||||
unity_wait_for_signal("External Clock User Finished");
|
||||
}
|
||||
// Disable and free the resources
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
free(recv_buff);
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
if (is_external) {
|
||||
unity_send_signal("External Clock User Finished");
|
||||
}
|
||||
// Assert whether the test success
|
||||
TEST_ASSERT(is_success);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user