Chen Jichang
2025-08-07 17:22:28 +08:00
parent 8c007f0e03
commit 86df5630e8
7 changed files with 308 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ if(CONFIG_SOC_RMT_SUPPORTED)
list(APPEND srcs "src/rmt_common.c"
"src/rmt_encoder.c"
"src/rmt_encoder_bytes.c"
"src/rmt_encoder_bits.c"
"src/rmt_encoder_copy.c"
"src/rmt_encoder_simple.c"
"src/rmt_rx.c"

View File

@@ -44,7 +44,7 @@ struct rmt_encoder_t {
* @param[in] encoder Encoder handle
* @param[in] tx_channel RMT TX channel handle, returned from `rmt_new_tx_channel()`
* @param[in] primary_data App data to be encoded into RMT symbols
* @param[in] data_size Size of primary_data, in bytes
* @param[in] data_size Size of primary_data, in bytes (especially for bits encoder, it is in bits)
* @param[out] ret_state Returned current encoder's state
* @return Number of RMT symbols that the primary data has been encoded into
*/
@@ -129,6 +129,17 @@ typedef struct {
} flags; /*!< Encoder config flag */
} rmt_bytes_encoder_config_t;
/**
* @brief Bits encoder configuration
*/
typedef struct {
rmt_symbol_word_t bit0; /*!< How to represent BIT0 in RMT symbol */
rmt_symbol_word_t bit1; /*!< How to represent BIT1 in RMT symbol */
struct {
uint32_t msb_first: 1; /*!< Whether to encode MSB bit first */
} flags; /*!< Encoder config flag */
} rmt_bits_encoder_config_t;
/**
* @brief Copy encoder configuration
*/
@@ -182,6 +193,38 @@ esp_err_t rmt_new_bytes_encoder(const rmt_bytes_encoder_config_t *config, rmt_en
*/
esp_err_t rmt_bytes_encoder_update_config(rmt_encoder_handle_t bytes_encoder, const rmt_bytes_encoder_config_t *config);
/**
* @brief Create RMT bits encoder, which can encode bit stream into RMT symbols
*
* @note The bits encoder is similar to bytes encoder, but it can handle arbitrary bit lengths,
* not just multiples of 8 bits. The data_size parameter in (*encode)() represents
* the number of bits to encode, not bytes.
*
* @param[in] config Bits encoder configuration
* @param[out] ret_encoder Returned encoder handle
* @return
* - ESP_OK: Create RMT bits encoder successfully
* - ESP_ERR_INVALID_ARG: Create RMT bits encoder failed because of invalid argument
* - ESP_ERR_NO_MEM: Create RMT bits encoder failed because out of memory
* - ESP_FAIL: Create RMT bits encoder failed because of other error
*/
esp_err_t rmt_new_bits_encoder(const rmt_bits_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
/**
* @brief Update the configuration of the bits encoder
*
* @note The configurations of the bits encoder is also set up by `rmt_new_bits_encoder()`.
* This function is used to update the configuration of the bits encoder at runtime.
*
* @param[in] bits_encoder Bits encoder handle, created by e.g `rmt_new_bits_encoder()`
* @param[in] config Bits encoder configuration
* @return
* - ESP_OK: Update RMT bits encoder successfully
* - ESP_ERR_INVALID_ARG: Update RMT bits encoder failed because of invalid argument
* - ESP_FAIL: Update RMT bits encoder failed because of other error
*/
esp_err_t rmt_bits_encoder_update_config(rmt_encoder_handle_t bits_encoder, const rmt_bits_encoder_config_t *config);
/**
* @brief Create RMT copy encoder, which copies the given RMT symbols into RMT memory
*

View File

@@ -96,7 +96,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
* @param[in] tx_channel RMT TX channel that created by `rmt_new_tx_channel()`
* @param[in] encoder RMT encoder that created by various factory APIs like `rmt_new_bytes_encoder()`
* @param[in] payload The raw data to be encoded into RMT symbols
* @param[in] payload_bytes Size of the `payload` in bytes
* @param[in] payload_size Size of the `payload` in bytes (specially for bits encoder, it is in bits)
* @param[in] config Transmission specific configuration
* @return
* - ESP_OK: Transmit data successfully
@@ -105,7 +105,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
* - ESP_ERR_NOT_SUPPORTED: Transmit data failed because some feature is not supported by hardware, e.g. unsupported loop count
* - ESP_FAIL: Transmit data failed because of other error
*/
esp_err_t rmt_transmit(rmt_channel_handle_t tx_channel, rmt_encoder_handle_t encoder, const void *payload, size_t payload_bytes, const rmt_transmit_config_t *config);
esp_err_t rmt_transmit(rmt_channel_handle_t tx_channel, rmt_encoder_handle_t encoder, const void *payload, size_t payload_size, const rmt_transmit_config_t *config);
/**
* @brief Wait for all pending TX transactions done

View File

@@ -0,0 +1,181 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "driver/rmt_encoder.h"
#include "rmt_private.h"
typedef struct rmt_bits_encoder_t {
rmt_encoder_t base; // encoder base class
size_t last_bit_index; // index of the current bit position within the current byte (0-7)
size_t last_byte_index; // index of the current byte in the data stream
rmt_symbol_word_t bit0; // bit zero representing
rmt_symbol_word_t bit1; // bit one representing
struct {
uint32_t msb_first: 1; // encode MSB firstly
} flags;
} rmt_bits_encoder_t;
RMT_ENCODER_FUNC_ATTR
static esp_err_t rmt_bits_encoder_reset(rmt_encoder_t *encoder)
{
rmt_bits_encoder_t *bits_encoder = __containerof(encoder, rmt_bits_encoder_t, base);
// reset index to zero
bits_encoder->last_bit_index = 0;
bits_encoder->last_byte_index = 0;
return ESP_OK;
}
RMT_ENCODER_FUNC_ATTR
static size_t rmt_encode_bits(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_bits_encoder_t *bits_encoder = __containerof(encoder, rmt_bits_encoder_t, base);
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
const uint8_t *raw_data = (const uint8_t *)primary_data;
rmt_encode_state_t state = RMT_ENCODING_RESET;
size_t byte_index = bits_encoder->last_byte_index;
size_t bit_index = bits_encoder->last_bit_index;
// calculate total bit position for remaining bits calculation
size_t total_bit_pos = byte_index * 8 + bit_index;
// how many symbols will be generated by the encoder (data_size is in bits)
size_t mem_want = data_size - total_bit_pos;
// how many symbols we can save for this round
size_t symbol_off = tx_chan->mem_off_bytes / sizeof(rmt_symbol_word_t);
size_t mem_have = tx_chan->mem_end - symbol_off;
// where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to_nc = NULL;
if (channel->dma_chan) {
mem_to_nc = tx_chan->dma_mem_base_nc;
} else {
mem_to_nc = channel->hw_mem_base;
}
// how many symbols will be encoded in this round
size_t encode_len = MIN(mem_want, mem_have);
bool encoding_truncated = mem_have < mem_want;
bool encoding_space_free = mem_have > mem_want;
#if SOC_RMT_SUPPORT_DMA
uint8_t dma_lli0_index = 0;
uint8_t dma_lli1_index = 0;
if (channel->dma_chan) {
// mark the start descriptor
if (symbol_off < tx_chan->ping_pong_symbols) {
dma_lli0_index = 0;
} else {
dma_lli0_index = 1;
}
}
#endif // SOC_RMT_SUPPORT_DMA
size_t len = encode_len;
while (len > 0) {
// start from last time truncated encoding
uint8_t cur_byte = raw_data[byte_index];
// bit-wise reverse
if (bits_encoder->flags.msb_first) {
cur_byte = hal_utils_bitwise_reverse8(cur_byte);
}
while ((len > 0) && (bit_index < 8)) {
if (cur_byte & (1 << bit_index)) {
mem_to_nc[symbol_off++] = bits_encoder->bit1;
} else {
mem_to_nc[symbol_off++] = bits_encoder->bit0;
}
len--;
bit_index++;
}
if (bit_index >= 8) {
byte_index++;
bit_index = 0;
}
}
#if SOC_RMT_SUPPORT_DMA
if (channel->dma_chan) {
// mark the end descriptor
if (symbol_off < tx_chan->ping_pong_symbols) {
dma_lli1_index = 0;
} else {
dma_lli1_index = 1;
}
// cross line, means desc0 has prepared with sufficient data buffer
if (dma_lli0_index != dma_lli1_index) {
gdma_link_set_owner(tx_chan->dma_link, dma_lli0_index, GDMA_LLI_OWNER_DMA);
gdma_link_set_length(tx_chan->dma_link, dma_lli0_index, tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t));
}
}
#endif // SOC_RMT_SUPPORT_DMA
if (encoding_truncated) {
// this encoding has not finished yet, save the truncated position
bits_encoder->last_bit_index = bit_index;
bits_encoder->last_byte_index = byte_index;
} else {
// reset internal index if encoding session has finished
bits_encoder->last_bit_index = 0;
bits_encoder->last_byte_index = 0;
state |= RMT_ENCODING_COMPLETE;
}
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 (symbol_off >= tx_chan->ping_pong_symbols * 2) {
#if SOC_RMT_SUPPORT_DMA
if (channel->dma_chan) {
gdma_link_set_owner(tx_chan->dma_link, dma_lli1_index, GDMA_LLI_OWNER_DMA);
gdma_link_set_length(tx_chan->dma_link, dma_lli1_index, tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t));
}
#endif // SOC_RMT_SUPPORT_DMA
tx_chan->mem_off_bytes = 0;
} else {
tx_chan->mem_off_bytes = symbol_off * sizeof(rmt_symbol_word_t);
}
*ret_state = state;
return encode_len;
}
static esp_err_t rmt_del_bits_encoder(rmt_encoder_t *encoder)
{
rmt_bits_encoder_t *bits_encoder = __containerof(encoder, rmt_bits_encoder_t, base);
free(bits_encoder);
return ESP_OK;
}
esp_err_t rmt_new_bits_encoder(const rmt_bits_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
rmt_bits_encoder_t *encoder = rmt_alloc_encoder_mem(sizeof(rmt_bits_encoder_t));
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for bits encoder");
encoder->base.encode = rmt_encode_bits;
encoder->base.del = rmt_del_bits_encoder;
encoder->base.reset = rmt_bits_encoder_reset;
encoder->bit0 = config->bit0;
encoder->bit1 = config->bit1;
encoder->flags.msb_first = config->flags.msb_first;
// return general encoder handle
*ret_encoder = &encoder->base;
ESP_LOGD(TAG, "new bits encoder @%p", encoder);
err:
return ret;
}
esp_err_t rmt_bits_encoder_update_config(rmt_encoder_handle_t bits_encoder, const rmt_bits_encoder_config_t *config)
{
ESP_RETURN_ON_FALSE(bits_encoder && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
rmt_bits_encoder_t *encoder = __containerof(bits_encoder, rmt_bits_encoder_t, base);
encoder->bit0 = config->bit0;
encoder->bit1 = config->bit1;
encoder->flags.msb_first = config->flags.msb_first;
return ESP_OK;
}

View File

@@ -181,7 +181,7 @@ struct rmt_channel_t {
typedef struct {
rmt_encoder_handle_t encoder; // encode user payload into RMT symbols
const void *payload; // encoder payload
size_t payload_bytes; // payload size
size_t payload_size; // payload size, in bytes (specially for bits encoder, it is in bits)
int loop_count; // transaction can be continued in a loop for specific times
int remain_loop_count; // user required loop count may exceed hardware limitation, the driver will transfer them in batches
size_t transmitted_symbol_num; // track the number of transmitted symbols

View File

@@ -517,9 +517,9 @@ esp_err_t rmt_tx_register_event_callbacks(rmt_channel_handle_t channel, const rm
return ESP_OK;
}
esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, const void *payload, size_t payload_bytes, const rmt_transmit_config_t *config)
esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, const void *payload, size_t payload_size, const rmt_transmit_config_t *config)
{
ESP_RETURN_ON_FALSE(channel && encoder && payload && payload_bytes && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(channel && encoder && payload && payload_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
#if !SOC_RMT_SUPPORT_TX_LOOP_COUNT
ESP_RETURN_ON_FALSE(config->loop_count <= 1, ESP_ERR_NOT_SUPPORTED, TAG, "loop count is not supported");
@@ -546,7 +546,7 @@ esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, con
memset(t, 0, sizeof(rmt_tx_trans_desc_t));
t->encoder = encoder;
t->payload = payload;
t->payload_bytes = payload_bytes;
t->payload_size = payload_size;
// treat loop_count == 1 as no loop
t->loop_count = config->loop_count == 1 ? 0 : config->loop_count;
t->remain_loop_count = t->loop_count;
@@ -649,7 +649,7 @@ size_t rmt_encode_check_result(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t *t
{
rmt_encode_state_t encode_state = RMT_ENCODING_RESET;
rmt_encoder_handle_t encoder = t->encoder;
size_t encoded_symbols = encoder->encode(encoder, &tx_chan->base, t->payload, t->payload_bytes, &encode_state);
size_t encoded_symbols = encoder->encode(encoder, &tx_chan->base, t->payload, t->payload_size, &encode_state);
bool is_mem_full = encode_state & RMT_ENCODING_MEM_FULL;
bool need_eof_mark = (encode_state & RMT_ENCODING_WITH_EOF) == 0;

View File

@@ -114,6 +114,81 @@ TEST_CASE("rmt bytes encoder", "[rmt]")
TEST_ESP_OK(rmt_del_encoder(bytes_encoder));
}
TEST_CASE("rmt bits encoder", "[rmt]")
{
rmt_tx_channel_config_t tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz resolution
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.trans_queue_depth = 4,
.gpio_num = TEST_RMT_GPIO_NUM_B,
};
rmt_channel_handle_t tx_channel = NULL;
TEST_ESP_OK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));
TEST_ESP_OK(rmt_enable(tx_channel));
printf("install bits encoder\r\n");
rmt_encoder_handle_t bits_encoder = NULL;
rmt_bits_encoder_config_t bits_enc_config = {
.bit0 = {
.level0 = 1,
.duration0 = 3, // T0H=3us
.level1 = 0,
.duration1 = 9, // T0L=9us
},
.bit1 = {
.level0 = 1,
.duration0 = 9, // T1H=9us
.level1 = 0,
.duration1 = 3, // T1L=3us
},
.flags.msb_first = 1,
};
TEST_ESP_OK(rmt_new_bits_encoder(&bits_enc_config, &bits_encoder));
uint8_t test_data[] = {0xAB, 0xDA}; // 10101011, 11011010
size_t num_bits1 = 8;
size_t num_bits2 = 5;
size_t num_bits3 = 13;
rmt_transmit_config_t transmit_config = {
.loop_count = 0, // no loop
};
printf("transmit bits pattern with MSB first(%zu bits)\r\n", num_bits1);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits1, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
printf("transmit bits pattern with MSB first(%zu bits)\r\n", num_bits2);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits2, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
printf("transmit bits pattern with MSB first(%zu bits)\r\n", num_bits3);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits3, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
// change to LSB first
printf("update bits encoder configuration to LSB first\r\n");
bits_enc_config.flags.msb_first = 0;
TEST_ESP_OK(rmt_bits_encoder_update_config(bits_encoder, &bits_enc_config));
printf("transmit bits pattern with LSB first(%zu bits)\r\n", num_bits1);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits1, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
printf("transmit bits pattern with LSB first(%zu bits)\r\n", num_bits2);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits2, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
printf("transmit bits pattern with LSB first(%zu bits)\r\n", num_bits3);
TEST_ESP_OK(rmt_transmit(tx_channel, bits_encoder, test_data, num_bits3, &transmit_config));
TEST_ESP_OK(rmt_tx_wait_all_done(tx_channel, -1));
TEST_ESP_OK(rmt_del_encoder(bits_encoder));
TEST_ESP_OK(rmt_disable(tx_channel));
TEST_ESP_OK(rmt_del_channel(tx_channel));
}
static void test_rmt_channel_single_trans(size_t mem_block_symbols, bool with_dma)
{
rmt_tx_channel_config_t tx_channel_cfg = {