From 6f7d1e1d98f5b4ba827c8ce97c1f21f40ae95e4d Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Mon, 27 Jul 2026 16:49:29 +0800 Subject: [PATCH] fix(rmt): fix loop tx can trigger done event --- components/esp_driver_rmt/src/rmt_tx.c | 10 +- .../test_apps/rmt/main/test_rmt_tx.c | 92 ++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/components/esp_driver_rmt/src/rmt_tx.c b/components/esp_driver_rmt/src/rmt_tx.c index f021be76090..37bccd0ce6c 100644 --- a/components/esp_driver_rmt/src/rmt_tx.c +++ b/components/esp_driver_rmt/src/rmt_tx.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -727,11 +727,13 @@ static void rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t // don't enable threshold interrupt with loop mode on // threshold interrupt will be disabled in `rmt_encode_eof()` rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_THRES(channel_id), t->loop_count == 0); - // Threshold interrupt will be generated by accident, clear it before starting new transmission - rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_THRES(channel_id)); } // don't generate trans done event for loop transmission rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), t->loop_count == 0); + // Clear stale TX events before the engine starts. + // Some targets can keep TX_DONE pending after a loop transaction. + // And Threshold interrupt may be generated by accident + rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_MASK(channel_id)); portEXIT_CRITICAL_SAFE(&group->spinlock); // at the beginning of a new transaction, encoding memory offset should start from zero. @@ -915,6 +917,8 @@ bool rmt_isr_handle_tx_threshold(rmt_tx_channel_t *tx_chan) { // continue ping-pong transmission rmt_tx_trans_desc_t *t = tx_chan->cur_trans; + // sanity check + assert(t); size_t encoded_symbols = t->transmitted_symbol_num; // encoding finished, only need to send the EOF symbol if (t->flags.encoding_done) { diff --git a/components/esp_driver_rmt/test_apps/rmt/main/test_rmt_tx.c b/components/esp_driver_rmt/test_apps/rmt/main/test_rmt_tx.c index cb15805d605..df2b94e2b5f 100644 --- a/components/esp_driver_rmt/test_apps/rmt/main/test_rmt_tx.c +++ b/components/esp_driver_rmt/test_apps/rmt/main/test_rmt_tx.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -389,6 +389,96 @@ TEST_CASE("rmt finite loop transaction", "[rmt]") #endif } +typedef struct { + uint32_t expected_symbols[3]; + uint32_t done_count; +} test_rmt_sequential_loop_context_t; + +TEST_RMT_CALLBACK_ATTR +static bool test_rmt_sequential_loop_done_cb(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data) +{ + test_rmt_sequential_loop_context_t *ctx = (test_rmt_sequential_loop_context_t *)user_data; + TEST_ASSERT_LESS_THAN(3, ctx->done_count); + esp_rom_printf("test_rmt_sequential_loop_done_cb: done_count = %d, expected_symbols = %d, num_symbols = %d\r\n", ctx->done_count, ctx->expected_symbols[ctx->done_count], edata->num_symbols); + TEST_ASSERT_EQUAL(ctx->expected_symbols[ctx->done_count], edata->num_symbols); + ctx->done_count++; + return false; +} + +TEST_CASE("rmt loop transaction followed by ping-pong transaction", "[rmt]") +{ + rmt_tx_channel_config_t tx_channel_cfg = { + .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * 2, + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = 1000000, // 1MHz, 1 tick = 1us + .trans_queue_depth = 4, + .gpio_num = TEST_RMT_GPIO_NUM_A, + .intr_priority = 2, + }; + printf("install tx channel\r\n"); + rmt_channel_handle_t tx_channel = NULL; + TEST_ESP_OK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel)); + + printf("install copy encoder\r\n"); + rmt_encoder_handle_t copy_encoder = NULL; + rmt_copy_encoder_config_t copy_encoder_config = {}; + TEST_ESP_OK(rmt_new_copy_encoder(©_encoder_config, ©_encoder)); + + uint32_t ping_pong_symbol_num = SOC_RMT_MEM_WORDS_PER_CHANNEL * 3 + 5; + test_rmt_sequential_loop_context_t cb_ctx = { + // +1 for the eof marker + .expected_symbols = { + ping_pong_symbol_num + 1, + 2, + ping_pong_symbol_num + 1, + }, + }; + rmt_tx_event_callbacks_t cbs = { + .on_trans_done = test_rmt_sequential_loop_done_cb, + }; + TEST_ESP_OK(rmt_tx_register_event_callbacks(tx_channel, &cbs, &cb_ctx)); + + printf("enable tx channel\r\n"); + TEST_ESP_OK(rmt_enable(tx_channel)); + + rmt_symbol_word_t ping_pong_symbols[ping_pong_symbol_num]; + memset(ping_pong_symbols, 0, sizeof(ping_pong_symbols)); + for (int i = 0; i < ping_pong_symbol_num; i++) { + ping_pong_symbols[i] = (rmt_symbol_word_t) { + .level0 = 0, + .duration0 = 5, + .level1 = 1, + .duration1 = 5, + }; + } + rmt_symbol_word_t loop_symbol = { + .level0 = 0, + .duration0 = 5, + .level1 = 1, + .duration1 = 5, + }; + + printf("queue ping-pong, loop, ping-pong transactions\r\n"); + rmt_transmit_config_t transmit_config = { + .loop_count = 0, + }; + TEST_ESP_OK(rmt_transmit(tx_channel, copy_encoder, ping_pong_symbols, sizeof(ping_pong_symbols), &transmit_config)); + transmit_config.loop_count = 50; + TEST_ESP_OK(rmt_transmit(tx_channel, copy_encoder, &loop_symbol, sizeof(loop_symbol), &transmit_config)); + transmit_config.loop_count = 0; + TEST_ESP_OK(rmt_transmit(tx_channel, copy_encoder, ping_pong_symbols, sizeof(ping_pong_symbols), &transmit_config)); + + printf("wait for queued transactions done\r\n"); + TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1)); + TEST_ASSERT_EQUAL(3, cb_ctx.done_count); + + printf("disable tx channel\r\n"); + TEST_ESP_OK(rmt_disable(tx_channel)); + printf("remove tx channel and copy encoder\r\n"); + TEST_ESP_OK(rmt_del_channel(tx_channel)); + TEST_ESP_OK(rmt_del_encoder(copy_encoder)); +} + #endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT TEST_CASE("rmt infinite loop transaction", "[rmt]")