feat(rmt): support bitscrambler as a RMT encoder

This commit is contained in:
morris
2025-03-26 10:29:09 +08:00
parent a06dfd5f19
commit c3f422348d
16 changed files with 388 additions and 59 deletions

View File

@@ -11,4 +11,5 @@ endif()
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs}
PRIV_REQUIRES "esp_mm" PRIV_REQUIRES "esp_mm"
INCLUDE_DIRS "include") INCLUDE_DIRS "include"
LDFRAGMENTS "linker.lf")

View File

@@ -0,0 +1,16 @@
menu "BitScrambler Configurations"
depends on SOC_BITSCRAMBLER_SUPPORTED
config BITSCRAMBLER_CTRL_FUNC_IN_IRAM
bool "Place BitScrambler control functions in IRAM"
default n
select BITSCRAMBLER_OBJ_CACHE_SAFE
help
Place BitScrambler control functions into IRAM for better performance and fewer cache misses.
config BITSCRAMBLER_OBJ_CACHE_SAFE
bool
default n
help
This will ensure the BitScrambler object will not be allocated from a memory region
where its cache can be disabled.
endmenu # BitScrambler Configurations

View File

@@ -0,0 +1,6 @@
[mapping:bitscrambler_driver]
archive: libesp_driver_bitscrambler.a
entries:
if BITSCRAMBLER_CTRL_FUNC_IN_IRAM = y:
bitscrambler: bitscrambler_reset (noflash)
bitscrambler: bitscrambler_start (noflash)

View File

@@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <stdatomic.h> #include <stdatomic.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_heap_caps.h"
#include "driver/bitscrambler.h" #include "driver/bitscrambler.h"
#include "bitscrambler_private.h" #include "bitscrambler_private.h"
#include "hal/bitscrambler_ll.h" #include "hal/bitscrambler_ll.h"
@@ -13,6 +14,12 @@
static const char *TAG = "bitscrambler"; static const char *TAG = "bitscrambler";
#if CONFIG_BITSCRAMBLER_OBJ_CACHE_SAFE
#define BITSCRAMBLER_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define BITSCRAMBLER_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with #define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with
#define BITSCRAMBLER_HW_REV 0 #define BITSCRAMBLER_HW_REV 0
@@ -153,8 +160,8 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
if (!handle) { if (!handle) {
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
// Allocate memory for private data // Allocate memory for the BitScrambler object from internal memory
bitscrambler_t *bs = calloc(1, sizeof(bitscrambler_t)); bitscrambler_t *bs = heap_caps_calloc(1, sizeof(bitscrambler_t), BITSCRAMBLER_MEM_ALLOC_CAPS);
if (!bs) { if (!bs) {
return ESP_ERR_NO_MEM; return ESP_ERR_NO_MEM;
} }
@@ -173,7 +180,7 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
return r; return r;
} }
// Done. // Return the handle
*handle = bs; *handle = bs;
return ESP_OK; return ESP_OK;
} }

View File

@@ -12,10 +12,14 @@ if(CONFIG_SOC_RMT_SUPPORTED)
"src/rmt_tx.c") "src/rmt_tx.c")
endif() endif()
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED AND CONFIG_SOC_RMT_SUPPORT_DMA)
list(APPEND srcs "src/rmt_encoder_bs.c")
endif()
if(${target} STREQUAL "linux") if(${target} STREQUAL "linux")
set(priv_requires "") set(priv_requires "")
else() else()
set(priv_requires esp_pm esp_driver_gpio esp_mm) set(priv_requires esp_pm esp_driver_gpio esp_driver_bitscrambler esp_mm)
endif() endif()
idf_component_register(SRCS ${srcs} idf_component_register(SRCS ${srcs}

View File

@@ -5,6 +5,8 @@ menu "ESP-Driver:RMT Configurations"
bool "Place RMT TX ISR handler in IRAM to reduce latency" bool "Place RMT TX ISR handler in IRAM to reduce latency"
default y default y
select RMT_OBJ_CACHE_SAFE select RMT_OBJ_CACHE_SAFE
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
select BITSCRAMBLER_CTRL_FUNC_IN_IRAM if SOC_BITSCRAMBLER_SUPPORTED && SOC_RMT_SUPPORT_DMA
help help
Place RMT TX ISR handler in IRAM to reduce latency caused by cache miss. Place RMT TX ISR handler in IRAM to reduce latency caused by cache miss.
@@ -19,6 +21,7 @@ menu "ESP-Driver:RMT Configurations"
bool "Place RMT receive function in IRAM" bool "Place RMT receive function in IRAM"
default n default n
select RMT_OBJ_CACHE_SAFE select RMT_OBJ_CACHE_SAFE
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
help help
Place RMT receive function into IRAM for better performance and fewer cache misses. Place RMT receive function into IRAM for better performance and fewer cache misses.

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -127,6 +127,13 @@ typedef struct {
typedef struct { typedef struct {
} rmt_copy_encoder_config_t; } rmt_copy_encoder_config_t;
/**
* @brief BitScrambler encoder configuration
*/
typedef struct {
const void *program_bin; /*!< BitScrambler program */
} rmt_bs_encoder_config_t;
/** /**
* @brief Simple callback encoder configuration * @brief Simple callback encoder configuration
*/ */
@@ -182,6 +189,22 @@ esp_err_t rmt_bytes_encoder_update_config(rmt_encoder_handle_t bytes_encoder, co
*/ */
esp_err_t rmt_new_copy_encoder(const rmt_copy_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder); esp_err_t rmt_new_copy_encoder(const rmt_copy_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
/**
* @brief Create RMT BitScrambler encoder
*
* @note The BitScrambler encoder is used to encode the user data into RMT symbols by providing the BitScrambler assembly program.
* The BitScrambler program is a binary blob, it should take control of the whole encoding stuffs, including inserting the EOF marker.
*
* @param[in] config BitScrambler encoder configuration
* @param[out] ret_encoder Returned encoder handle
* @return
* - ESP_OK: Create RMT BitScrambler encoder successfully
* - ESP_ERR_INVALID_ARG: Create RMT BitScrambler encoder failed because of invalid argument
* - ESP_ERR_NO_MEM: Create RMT BitScrambler encoder failed because out of memory
* - ESP_FAIL: Create RMT BitScrambler encoder failed because of other error
*/
esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
/** /**
* @brief Create RMT simple callback encoder, which uses a callback to convert incoming * @brief Create RMT simple callback encoder, which uses a callback to convert incoming
* data into RMT symbols. * data into RMT symbols.

View File

@@ -8,9 +8,13 @@ entries:
rmt_tx: rmt_tx_do_transaction (noflash) rmt_tx: rmt_tx_do_transaction (noflash)
rmt_tx: rmt_encode_check_result (noflash) rmt_tx: rmt_encode_check_result (noflash)
rmt_tx: rmt_tx_mark_eof (noflash) rmt_tx: rmt_tx_mark_eof (noflash)
rmt_encoder: rmt_encoder_reset (noflash)
rmt_encoder_bytes: rmt_encode_bytes (noflash) rmt_encoder_bytes: rmt_encode_bytes (noflash)
rmt_encoder_bytes: rmt_bytes_encoder_reset (noflash)
rmt_encoder_copy: rmt_encode_copy (noflash) rmt_encoder_copy: rmt_encode_copy (noflash)
rmt_encoder_copy: rmt_copy_encoder_reset (noflash)
rmt_encoder_simple: rmt_encode_simple (noflash) rmt_encoder_simple: rmt_encode_simple (noflash)
rmt_encoder_simple: rmt_simple_encoder_reset (noflash)
if SOC_RMT_SUPPORT_TX_LOOP_COUNT = y: if SOC_RMT_SUPPORT_TX_LOOP_COUNT = y:
rmt_tx: rmt_isr_handle_tx_loop_end (noflash) rmt_tx: rmt_isr_handle_tx_loop_end (noflash)
@@ -18,6 +22,10 @@ entries:
if SOC_RMT_SUPPORT_DMA = y: if SOC_RMT_SUPPORT_DMA = y:
rmt_tx: rmt_dma_tx_eof_cb (noflash) rmt_tx: rmt_dma_tx_eof_cb (noflash)
if SOC_BITSCRAMBLER_SUPPORTED = y:
rmt_encoder_bs: rmt_encode_bs (noflash)
rmt_encoder_bs: rmt_bs_encoder_reset (noflash)
if RMT_RX_ISR_HANDLER_IN_IRAM = y: if RMT_RX_ISR_HANDLER_IN_IRAM = y:
rmt_rx: rmt_rx_default_isr (noflash) rmt_rx: rmt_rx_default_isr (noflash)
rmt_rx: rmt_isr_handle_rx_done (noflash) rmt_rx: rmt_isr_handle_rx_done (noflash)
@@ -30,51 +38,3 @@ entries:
if RMT_RECV_FUNC_IN_IRAM = y: if RMT_RECV_FUNC_IN_IRAM = y:
rmt_rx: rmt_receive (noflash) rmt_rx: rmt_receive (noflash)
[mapping:rmt_driver_gdma]
archive: libesp_hw_support.a
entries:
if RMT_TX_ISR_HANDLER_IN_IRAM = y && SOC_RMT_SUPPORT_DMA = y:
gdma: gdma_reset (noflash)
gdma: gdma_start (noflash)
gdma: gdma_append (noflash)
if RMT_RECV_FUNC_IN_IRAM = y && SOC_RMT_SUPPORT_DMA = y:
gdma: gdma_reset (noflash)
gdma: gdma_start (noflash)
[mapping:rmt_driver_hal]
archive: libhal.a
entries:
if RMT_TX_ISR_HANDLER_IN_IRAM = y:
if SOC_RMT_SUPPORT_DMA = y:
gdma_hal_top: gdma_hal_append (noflash)
gdma_hal_top: gdma_hal_reset (noflash)
gdma_hal_top: gdma_hal_start_with_desc (noflash)
# GDMA implementation layer for AHB-DMA version 1
if SOC_AHB_GDMA_VERSION = 1:
gdma_hal_ahb_v1: gdma_ahb_hal_append (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_reset (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_start_with_desc (noflash)
# GDMA implementation layer for AHB-DMA version 2
if SOC_AHB_GDMA_VERSION = 2:
gdma_hal_ahb_v2: gdma_ahb_hal_append (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_reset (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_start_with_desc (noflash)
if RMT_RECV_FUNC_IN_IRAM = y:
if SOC_RMT_SUPPORT_DMA = y:
gdma_hal_top: gdma_hal_reset (noflash)
gdma_hal_top: gdma_hal_start_with_desc (noflash)
# GDMA implementation layer for AHB-DMA version 1
if SOC_AHB_GDMA_VERSION = 1:
gdma_hal_ahb_v1: gdma_ahb_hal_reset (noflash)
gdma_hal_ahb_v1: gdma_ahb_hal_start_with_desc (noflash)
# GDMA implementation layer for AHB-DMA version 2
if SOC_AHB_GDMA_VERSION = 2:
gdma_hal_ahb_v2: gdma_ahb_hal_reset (noflash)
gdma_hal_ahb_v2: gdma_ahb_hal_start_with_desc (noflash)

View File

@@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "driver/rmt_encoder.h"
#include "driver/bitscrambler.h"
#include "rmt_private.h"
typedef struct rmt_bs_encoder_t {
rmt_encoder_t base; // the base "class", declares the standard encoder interface
size_t last_byte_index; // index of the encoding byte in the primary stream
bitscrambler_handle_t bs; // BitScrambler handle
} rmt_bs_encoder_t;
static esp_err_t rmt_bs_encoder_reset(rmt_encoder_t *encoder)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
bs_encoder->last_byte_index = 0;
bitscrambler_reset(bs_encoder->bs);
bitscrambler_start(bs_encoder->bs);
return ESP_OK;
}
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
bitscrambler_free(bs_encoder->bs);
free(bs_encoder);
return ESP_OK;
}
static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *input_raw,
size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
uint8_t *input_bytes = (uint8_t *)input_raw;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_dma_descriptor_t *desc0 = NULL;
rmt_dma_descriptor_t *desc1 = NULL;
// bitscrambler encoder must be used with a TX channel with DMA enabled
assert(tx_chan->base.dma_chan != NULL);
size_t byte_index = bs_encoder->last_byte_index;
// how many bytes will be copied by the encoder
size_t mem_want = (data_size - byte_index);
// how many bytes we can save for this round
size_t mem_have = tx_chan->mem_end * sizeof(rmt_symbol_word_t) - tx_chan->mem_off_bytes;
// target memory buffer to copy to
uint8_t *mem_to_nc = (uint8_t*)tx_chan->dma_mem_base_nc;
// how many bytes will be copied in this round
size_t copy_len = MIN(mem_want, mem_have);
bool encoding_truncated = mem_have < mem_want;
bool encoding_space_free = mem_have > mem_want;
// mark the start descriptor
if (tx_chan->mem_off_bytes < tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
desc0 = &tx_chan->dma_nodes_nc[0];
} else {
desc0 = &tx_chan->dma_nodes_nc[1];
}
size_t len = copy_len;
while (len > 0) {
mem_to_nc[tx_chan->mem_off_bytes++] = input_bytes[byte_index++];
len--;
}
// mark the end descriptor
if (tx_chan->mem_off_bytes < tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
desc1 = &tx_chan->dma_nodes_nc[0];
} else {
desc1 = &tx_chan->dma_nodes_nc[1];
}
// cross line, means desc0 has prepared with sufficient data buffer
if (desc0 != desc1) {
desc0->dw0.length = tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
desc0->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
}
if (encoding_truncated) {
// this encoding has not finished yet, save the truncated position
bs_encoder->last_byte_index = byte_index;
} else {
// reset internal index if encoding session has finished
bs_encoder->last_byte_index = 0;
// bitscrambler program will take care of the EOF marker by itself
// so we don't rely on the TX driver to inject an EOF marker
state |= RMT_ENCODING_COMPLETE | RMT_ENCODING_WITH_EOF;
}
if (!encoding_space_free) {
// no more free memory, the caller should yield
state |= RMT_ENCODING_MEM_FULL;
}
// reset offset pointer when exceeds maximum range
if (tx_chan->mem_off_bytes >= tx_chan->ping_pong_symbols * 2 * sizeof(rmt_symbol_word_t)) {
desc1->dw0.length = tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
desc1->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
tx_chan->mem_off_bytes = 0;
}
*ret_state = state;
return copy_len;
}
esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
esp_err_t ret = ESP_OK;
rmt_bs_encoder_t *encoder = NULL;
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
encoder = rmt_alloc_encoder_mem(sizeof(rmt_bs_encoder_t));
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for bitscrambler encoder");
bitscrambler_config_t bs_config = {
.dir = BITSCRAMBLER_DIR_TX,
.attach_to = SOC_BITSCRAMBLER_ATTACH_RMT,
};
ESP_GOTO_ON_ERROR(bitscrambler_new(&bs_config, &encoder->bs), err, TAG, "create bitscrambler failed");
// load the bitscrambler program
ESP_GOTO_ON_ERROR(bitscrambler_load_program(encoder->bs, config->program_bin), err, TAG, "load bitscrambler program failed");
encoder->base.encode = rmt_encode_bs;
encoder->base.del = rmt_del_bs_encoder;
encoder->base.reset = rmt_bs_encoder_reset;
// return general encoder handle
*ret_encoder = &encoder->base;
ESP_LOGD(TAG, "new bitscrambler encoder @%p", encoder);
return ESP_OK;
err:
if (encoder) {
if (encoder->bs) {
bitscrambler_free(encoder->bs);
}
}
return ret;
}

View File

@@ -674,6 +674,9 @@ static void rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t
// update current transaction // update current transaction
tx_chan->cur_trans = t; tx_chan->cur_trans = t;
// reset RMT encoder before starting a new transaction
rmt_encoder_reset(t->encoder);
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
if (channel->dma_chan) { if (channel->dma_chan) {
gdma_reset(channel->dma_chan); gdma_reset(channel->dma_chan);
@@ -845,8 +848,6 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
// recycle the interrupted transaction // recycle the interrupted transaction
if (tx_chan->cur_trans) { if (tx_chan->cur_trans) {
xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &tx_chan->cur_trans, 0); xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &tx_chan->cur_trans, 0);
// reset corresponding encoder
rmt_encoder_reset(tx_chan->cur_trans->encoder);
} }
tx_chan->cur_trans = NULL; tx_chan->cur_trans = NULL;

View File

@@ -12,6 +12,14 @@ if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE)
list(APPEND srcs "test_rmt_sleep.c") list(APPEND srcs "test_rmt_sleep.c")
endif() endif()
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED AND CONFIG_SOC_RMT_SUPPORT_DMA)
list(APPEND srcs "test_rmt_bitscrambler.c")
endif()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES unity esp_driver_rmt esp_driver_gpio esp_timer esp_psram PRIV_REQUIRES unity esp_driver_rmt esp_driver_gpio esp_driver_bitscrambler esp_timer esp_psram
WHOLE_ARCHIVE) WHOLE_ARCHIVE)
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED AND CONFIG_SOC_RMT_SUPPORT_DMA)
target_bitscrambler_add_src("test_tx.bsasm")
endif()

View File

@@ -0,0 +1,107 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/gpio.h"
#include "driver/bitscrambler.h"
#include "test_board.h"
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx, "test_tx");
IRAM_ATTR static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t high_task_wakeup = pdFALSE;
TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
const rmt_symbol_word_t expected_symbols[] = {
{ .level0 = 1, .duration0 = 273, .level1 = 0, .duration1 = 1 },
{ .level0 = 1, .duration0 = 546, .level1 = 0, .duration1 = 2 },
{ .level0 = 1, .duration0 = 819, .level1 = 0, .duration1 = 3 },
{ .level0 = 1, .duration0 = 1092, .level1 = 0, .duration1 = 4 },
{ .level0 = 1, .duration0 = 1, .level1 = 0, .duration1 = 0 },
};
rmt_symbol_word_t *remote_codes = edata->received_symbols;
esp_rom_printf("%u symbols received:\r\n", edata->num_symbols);
for (int i = 0; i < edata->num_symbols; i++) {
esp_rom_printf("{%d:%d},{%d:%d}\r\n", remote_codes[i].level0, remote_codes[i].duration0, remote_codes[i].level1, remote_codes[i].duration1);
}
TEST_ASSERT_EQUAL_INT_ARRAY(expected_symbols, remote_codes, sizeof(expected_symbols) / sizeof(rmt_symbol_word_t));
vTaskNotifyGiveFromISR(task_to_notify, &high_task_wakeup);
return high_task_wakeup == pdTRUE;
}
TEST_CASE("rmt TX with bitscrambler", "[rmt]")
{
rmt_tx_channel_config_t tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = 48, // no need a large DMA memory to save the primary data
.gpio_num = TEST_RMT_GPIO_NUM_B,
.trans_queue_depth = 4,
.flags.with_dma = true, // bitscrambler has to work with DMA
};
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));
rmt_rx_channel_config_t rx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = TEST_RMT_GPIO_NUM_B,
};
printf("install rx channel to the same GPIO\r\n");
rmt_channel_handle_t rx_channel = NULL;
TEST_ESP_OK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
printf("register rx event callbacks\r\n");
rmt_rx_event_callbacks_t cbs = {
.on_recv_done = test_rmt_rx_done_callback,
};
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, xTaskGetCurrentTaskHandle()));
printf("install bitscrambler encoder\r\n");
rmt_encoder_handle_t bs_encoder = NULL;
rmt_bs_encoder_config_t bs_encoder_config = {
.program_bin = bitscrambler_program_test_tx,
};
TEST_ESP_OK(rmt_new_bitscrambler_encoder(&bs_encoder_config, &bs_encoder));
printf("enable tx+rx channel\r\n");
TEST_ESP_OK(rmt_enable(tx_channel));
TEST_ESP_OK(rmt_enable(rx_channel));
rmt_receive_config_t receive_config = {
.signal_range_min_ns = 500,
.signal_range_max_ns = 2000000,
};
rmt_symbol_word_t symbols[8];
TEST_ESP_OK(rmt_receive(rx_channel, symbols, sizeof(symbols), &receive_config));
printf("transmit!\r\n");
rmt_transmit_config_t transmit_config = {
.loop_count = 0, // no loop
};
TEST_ESP_OK(rmt_transmit(tx_channel, bs_encoder, (uint8_t[]) {
0x12, 0x34, 0x56, 0x78, 0x9a, // dummy test values, will be further processed by bitscrambler program
}, 5, &transmit_config));
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
printf("disable tx+rx channel\r\n");
TEST_ESP_OK(rmt_disable(tx_channel));
TEST_ESP_OK(rmt_disable(rx_channel));
printf("remove tx+rx channel and bs encoder\r\n");
TEST_ESP_OK(rmt_del_channel(tx_channel));
TEST_ESP_OK(rmt_del_channel(rx_channel));
TEST_ESP_OK(rmt_del_encoder(bs_encoder));
}

View File

@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
cfg prefetch false # disable data prefetch
cfg eof_on downstream # set EOF on downstream
cfg trailing_bytes 4
cfg lut_width_bits 32
# Define contents that stored in the lookup table
lut 0x00018111 # index 0, 273us high level, 1us low level
lut 0x00028222 # index 1, 546us high level, 2us low level
lut 0x00038333 # index 2, 819us high level, 3us low level
lut 0x00048444 # index 3, 1092us high level, 4us low level
lut 0x00008001 # index 4, saves the RMT end marker (any level with duration equals to 0)
set 16..18 L # init the LUT index: 0 (0b000)
loop:
read 8,
set 31..0 L31..L0,
write 32,
jmp loop

View File

@@ -62,7 +62,7 @@ static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
return ESP_OK; return ESP_OK;
} }
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) IRAM_ATTR static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
{ {
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_reset(led_encoder->bytes_encoder); rmt_encoder_reset(led_encoder->bytes_encoder);
@@ -177,7 +177,7 @@ static esp_err_t rmt_del_nec_protocol_encoder(rmt_encoder_t *encoder)
return ESP_OK; return ESP_OK;
} }
static esp_err_t rmt_nec_protocol_encoder_reset(rmt_encoder_t *encoder) IRAM_ATTR static esp_err_t rmt_nec_protocol_encoder_reset(rmt_encoder_t *encoder)
{ {
rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base); rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base);
rmt_encoder_reset(nec_encoder->copy_encoder); rmt_encoder_reset(nec_encoder->copy_encoder);

View File

@@ -443,6 +443,20 @@ A configuration structure :cpp:type:`rmt_simple_encoder_config_t` should be prov
While the functionality of an encoding process using the simple callback encoder can usually also realized by chaining other encoders, the simple callback can be more easy to understand and maintain than an encoder chain. While the functionality of an encoding process using the simple callback encoder can usually also realized by chaining other encoders, the simple callback can be more easy to understand and maintain than an encoder chain.
.. only:: SOC_BITSCRAMBLER_SUPPORTED and SOC_RMT_SUPPORT_DMA
BitScrambler Encoder
~~~~~~~~~~~~~~~~~~~~
When the RMT transmit channel has DMA enabled, we can control the data on the DMA path by writing :doc:`BitScrambler </api-reference/peripherals/bitscrambler>` assembly code, which implements simple encoding operations. Compared to CPU-based encoding, the BitScrambler offers higher performance without consuming CPU resources. However, due to the limited instruction memory space of the BitScrambler, it cannot implement complex encoding operations. Additionally, the output data format from the BitScrambler program must conform to the :cpp:type:`rmt_symbol_word_t` structure.
A BitScrambler encoder can be created by calling :cpp:func:`rmt_new_bitscrambler_encoder`. This function takes a configuration parameter of type :cpp:type:`rmt_bs_encoder_config_t`, which includes the following configuration items:
- :cpp:member:`rmt_bs_encoder_config_t::program_bin` points to the binary file of the BitScrambler program. This binary file must comply with the BitScrambler assembly language specification and will be loaded into the BitScrambler's instruction memory at runtime. For information on how to write and compile BitScrambler programs, please refer to the :doc:`BitScrambler Programming Guide </api-reference/peripherals/bitscrambler>`.
.. note::
The BitScrambler encoder **must** be used with an RMT channel that has DMA enabled.
Customize RMT Encoder for NEC Protocol Customize RMT Encoder for NEC Protocol
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -443,6 +443,20 @@ RMT 编码器是 RMT TX 事务的一部分,用于在特定时间生成正确
简易回调编码器的功能通常可以通过链式组合其他编码器来实现,但相比编码器链,简易回调编码器更易于理解和维护。 简易回调编码器的功能通常可以通过链式组合其他编码器来实现,但相比编码器链,简易回调编码器更易于理解和维护。
.. only:: SOC_BITSCRAMBLER_SUPPORTED and SOC_RMT_SUPPORT_DMA
比特调节 (BitScrambler) 编码器
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
当 RMT 的发送通道开启了 DMA, 我们可以通过编写 :doc:`比特调节器 </api-reference/peripherals/bitscrambler>` 汇编代码来控制 DMA 通路上的数据,进而实现一些简单的编码工作。相较于使用 CPU 做编码工作,比特调节器的性能更高,且不会占用 CPU 资源,但是受限于 BitScrambler 有限的指令存储器空间,它无法实现复杂的编码工作。此外,比特调节器程序的输出流数据格式必须符合 :cpp:type:`rmt_symbol_word_t` 结构。
调用 :cpp:func:`rmt_new_bitscrambler_encoder` 可以创建一个比特调节器编码器。该函数的配置参数为 :cpp:type:`rmt_bs_encoder_config_t` 结构体,包含以下配置项:
- :cpp:member:`rmt_bs_encoder_config_t::program_bin` 指向比特调节器程序的二进制文件的指针。该二进制文件必须符合比特调节器的汇编语言规范,并且在运行时会被加载到比特调节器的指令存储器中。如何编写并编译比特调节器程序请参考 :doc:`比特调节器编程指南 </api-reference/peripherals/bitscrambler>`
.. note::
比特调节编码器**必须**配合开启了 DMA 的 RMT 通道使用。
自定义 NEC 协议的 RMT 编码器 自定义 NEC 协议的 RMT 编码器
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~