mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/rmt_bit_encoder' into 'master'
feat(rmt): add bit encoder for rmt tx Closes IDFGH-16044, IDFGH-13969, and IDFGH-13698 See merge request espressif/esp-idf!41090
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
181
components/esp_driver_rmt/src/rmt_encoder_bits.c
Normal file
181
components/esp_driver_rmt/src/rmt_encoder_bits.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -518,9 +518,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");
|
||||
@@ -547,7 +547,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;
|
||||
@@ -650,7 +650,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;
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user