feat(rmt): allow the encoder to emit the EOF marker

This commit is contained in:
morris
2025-03-20 18:19:33 +08:00
parent 4f3e64331b
commit a06dfd5f19
6 changed files with 69 additions and 51 deletions

View File

@@ -26,6 +26,7 @@ typedef enum {
RMT_ENCODING_RESET = 0, /*!< The encoding session is in reset state */
RMT_ENCODING_COMPLETE = (1 << 0), /*!< The encoding session is finished, the caller can continue with subsequent encoding */
RMT_ENCODING_MEM_FULL = (1 << 1), /*!< The encoding artifact memory is full, the caller should return from current encoding session */
RMT_ENCODING_WITH_EOF = (1 << 2), /*!< The encoding session has inserted the EOF marker to the symbol stream */
} rmt_encode_state_t;
/**
@@ -169,6 +170,8 @@ esp_err_t rmt_bytes_encoder_update_config(rmt_encoder_handle_t bytes_encoder, co
/**
* @brief Create RMT copy encoder, which copies the given RMT symbols into RMT memory
*
* @note When transmitting using a copy encoder, ensure that the input data is already formatted as `rmt_symbol_word_t`.
*
* @param[in] config Copy encoder configuration
* @param[out] ret_encoder Returned encoder handle
* @return

View File

@@ -42,7 +42,8 @@ static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t chan
// how many symbols will be generated by the encoder
size_t mem_want = (data_size - byte_index - 1) * 8 + (8 - bit_index);
// how many symbols we can save for this round
size_t mem_have = tx_chan->mem_end - tx_chan->mem_off;
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) {
@@ -57,7 +58,7 @@ static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t chan
if (channel->dma_chan) {
// mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes_nc[0];
} else {
desc0 = &tx_chan->dma_nodes_nc[1];
@@ -74,9 +75,9 @@ static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t chan
}
while ((len > 0) && (bit_index < 8)) {
if (cur_byte & (1 << bit_index)) {
mem_to_nc[tx_chan->mem_off++] = bytes_encoder->bit1;
mem_to_nc[symbol_off++] = bytes_encoder->bit1;
} else {
mem_to_nc[tx_chan->mem_off++] = bytes_encoder->bit0;
mem_to_nc[symbol_off++] = bytes_encoder->bit0;
}
len--;
bit_index++;
@@ -89,7 +90,7 @@ static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t chan
if (channel->dma_chan) {
// mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes_nc[0];
} else {
desc1 = &tx_chan->dma_nodes_nc[1];
@@ -119,12 +120,14 @@ static size_t rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t chan
}
// reset offset pointer when exceeds maximum range
if (tx_chan->mem_off >= tx_chan->ping_pong_symbols * 2) {
if (symbol_off >= tx_chan->ping_pong_symbols * 2) {
if (channel->dma_chan) {
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 = 0;
tx_chan->mem_off_bytes = 0;
} else {
tx_chan->mem_off_bytes = symbol_off * sizeof(rmt_symbol_word_t);
}
*ret_state = state;

View File

@@ -20,11 +20,11 @@ static esp_err_t rmt_copy_encoder_reset(rmt_encoder_t *encoder)
}
static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
const void *input_symbols, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_copy_encoder_t *copy_encoder = __containerof(encoder, rmt_copy_encoder_t, base);
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
rmt_symbol_word_t *symbols = (rmt_symbol_word_t *)primary_data;
rmt_symbol_word_t *symbols = (rmt_symbol_word_t *)input_symbols;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_dma_descriptor_t *desc0 = NULL;
rmt_dma_descriptor_t *desc1 = NULL;
@@ -33,7 +33,8 @@ static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t chann
// how many symbols will be copied by the encoder
size_t mem_want = (data_size / 4 - symbol_index);
// how many symbols we can save for this round
size_t mem_have = tx_chan->mem_end - tx_chan->mem_off;
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) {
@@ -48,7 +49,7 @@ static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t chann
if (channel->dma_chan) {
// mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes_nc[0];
} else {
desc0 = &tx_chan->dma_nodes_nc[1];
@@ -57,13 +58,13 @@ static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t chann
size_t len = encode_len;
while (len > 0) {
mem_to_nc[tx_chan->mem_off++] = symbols[symbol_index++];
mem_to_nc[symbol_off++] = symbols[symbol_index++];
len--;
}
if (channel->dma_chan) {
// mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes_nc[0];
} else {
desc1 = &tx_chan->dma_nodes_nc[1];
@@ -91,12 +92,14 @@ static size_t rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_handle_t chann
}
// reset offset pointer when exceeds maximum range
if (tx_chan->mem_off >= tx_chan->ping_pong_symbols * 2) {
if (symbol_off >= tx_chan->ping_pong_symbols * 2) {
if (channel->dma_chan) {
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 = 0;
tx_chan->mem_off_bytes = 0;
} else {
tx_chan->mem_off_bytes = symbol_off * sizeof(rmt_symbol_word_t);
}
*ret_state = state;

View File

@@ -37,6 +37,7 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_dma_descriptor_t *desc0 = NULL;
rmt_dma_descriptor_t *desc1 = NULL;
size_t symbol_off = tx_chan->mem_off_bytes / sizeof(rmt_symbol_word_t);
// where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to_nc = NULL;
@@ -48,7 +49,7 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
if (channel->dma_chan) {
// mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes_nc[0];
} else {
desc0 = &tx_chan->dma_nodes_nc[1];
@@ -69,11 +70,11 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
// when then called with a larger buffer.
size_t encode_len = 0; //total amount of symbols written to rmt memory
bool is_done = false;
while (tx_chan->mem_off < tx_chan->mem_end) {
while (symbol_off < tx_chan->mem_end) {
if (simple_encoder->ovf_buf_parsed_pos < simple_encoder->ovf_buf_fill_len) {
// Overflow buffer has data from the previous encoding call. Copy one entry
// from that.
mem_to_nc[tx_chan->mem_off++] = simple_encoder->ovf_buf[simple_encoder->ovf_buf_parsed_pos++];
mem_to_nc[symbol_off++] = simple_encoder->ovf_buf[simple_encoder->ovf_buf_parsed_pos++];
encode_len++;
} else {
// Overflow buffer is empty, so we don't need to empty that first.
@@ -87,11 +88,11 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
// Try to have the callback write the data directly into RMT memory.
size_t enc_size = simple_encoder->callback(data, data_size,
simple_encoder->last_symbol_index,
tx_chan->mem_end - tx_chan->mem_off,
&mem_to_nc[tx_chan->mem_off],
tx_chan->mem_end - symbol_off,
&mem_to_nc[symbol_off],
&is_done, simple_encoder->arg);
encode_len += enc_size;
tx_chan->mem_off += enc_size;
symbol_off += enc_size;
simple_encoder->last_symbol_index += enc_size;
if (is_done) {
break; // we're done, no more data to write to RMT memory.
@@ -130,7 +131,7 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
if (channel->dma_chan) {
// mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
if (symbol_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes_nc[0];
} else {
desc1 = &tx_chan->dma_nodes_nc[1];
@@ -153,12 +154,14 @@ static size_t rmt_encode_simple(rmt_encoder_t *encoder, rmt_channel_handle_t cha
}
// reset offset pointer when exceeds maximum range
if (tx_chan->mem_off >= tx_chan->ping_pong_symbols * 2) {
if (symbol_off >= tx_chan->ping_pong_symbols * 2) {
if (channel->dma_chan) {
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 = 0;
tx_chan->mem_off_bytes = 0;
} else {
tx_chan->mem_off_bytes = symbol_off * sizeof(rmt_symbol_word_t);
}
*ret_state = state;

View File

@@ -183,6 +183,7 @@ typedef struct {
struct {
uint32_t eot_level : 1; // Set the output level for the "End Of Transmission"
uint32_t encoding_done: 1; // Indicate whether the encoding has finished (not the encoding of transmission)
uint32_t need_eof_mark: 1; // Indicate whether need to insert an EOF mark (a special RMT symbol)
} flags;
} rmt_tx_trans_desc_t;
@@ -191,7 +192,7 @@ struct rmt_tx_channel_t {
rmt_channel_t base; // channel base class
rmt_symbol_word_t *dma_mem_base; // base address of RMT channel DMA buffer
rmt_symbol_word_t *dma_mem_base_nc; // base address of RMT channel DMA buffer, accessed in non-cached way
size_t mem_off; // runtime argument, indicating the next writing position in the RMT hardware memory
size_t mem_off_bytes; // runtime argument, indicating the next writing position in the RMT hardware memory, the offset unit is in bytes
size_t mem_end; // runtime argument, indicating the end of current writing region
size_t ping_pong_symbols; // ping-pong size (half of the RMT channel memory)
size_t queue_size; // size of transaction queue

View File

@@ -589,39 +589,43 @@ esp_err_t rmt_tx_wait_all_done(rmt_channel_handle_t channel, int timeout_ms)
return ESP_OK;
}
static void rmt_tx_mark_eof(rmt_tx_channel_t *tx_chan)
static size_t rmt_tx_mark_eof(rmt_tx_channel_t *tx_chan, bool need_eof_marker)
{
rmt_channel_t *channel = &tx_chan->base;
rmt_group_t *group = channel->group;
int channel_id = channel->channel_id;
rmt_symbol_word_t *mem_to_nc = NULL;
rmt_tx_trans_desc_t *cur_trans = tx_chan->cur_trans;
rmt_dma_descriptor_t *desc_nc = NULL;
if (channel->dma_chan) {
mem_to_nc = tx_chan->dma_mem_base_nc;
} else {
mem_to_nc = channel->hw_mem_base;
}
// a RMT word whose duration is zero means a "stop" pattern
mem_to_nc[tx_chan->mem_off++] = (rmt_symbol_word_t) {
.duration0 = 0,
.level0 = cur_trans->flags.eot_level,
.duration1 = 0,
.level1 = cur_trans->flags.eot_level,
};
if (need_eof_marker) {
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;
}
size_t symbol_off = tx_chan->mem_off_bytes / sizeof(rmt_symbol_word_t);
// a RMT word whose duration is zero means a "stop" pattern
mem_to_nc[symbol_off] = (rmt_symbol_word_t) {
.duration0 = 0,
.level0 = cur_trans->flags.eot_level,
.duration1 = 0,
.level1 = cur_trans->flags.eot_level,
};
tx_chan->mem_off_bytes += sizeof(rmt_symbol_word_t);
}
size_t off = 0;
if (channel->dma_chan) {
if (tx_chan->mem_off <= tx_chan->ping_pong_symbols) {
if (tx_chan->mem_off_bytes <= tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
desc_nc = &tx_chan->dma_nodes_nc[0];
off = tx_chan->mem_off;
off = tx_chan->mem_off_bytes;
} else {
desc_nc = &tx_chan->dma_nodes_nc[1];
off = tx_chan->mem_off - tx_chan->ping_pong_symbols;
off = tx_chan->mem_off_bytes - tx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
}
desc_nc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
desc_nc->dw0.length = off * sizeof(rmt_symbol_word_t);
desc_nc->dw0.length = off;
// break down the DMA descriptor link
desc_nc->next = NULL;
} else {
@@ -630,6 +634,8 @@ static void rmt_tx_mark_eof(rmt_tx_channel_t *tx_chan)
rmt_ll_enable_interrupt(group->hal.regs, RMT_LL_EVENT_TX_THRES(channel_id), false);
portEXIT_CRITICAL_ISR(&group->spinlock);
}
return need_eof_marker ? 1 : 0;
}
size_t rmt_encode_check_result(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t *t)
@@ -639,12 +645,13 @@ size_t rmt_encode_check_result(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t *t
size_t encoded_symbols = encoder->encode(encoder, &tx_chan->base, t->payload, t->payload_bytes, &encode_state);
if (encode_state & RMT_ENCODING_COMPLETE) {
t->flags.encoding_done = true;
bool need_eof_mark = (encode_state & RMT_ENCODING_WITH_EOF) == 0;
// inserting EOF symbol if there's extra space
if (!(encode_state & RMT_ENCODING_MEM_FULL)) {
rmt_tx_mark_eof(tx_chan);
encoded_symbols += 1;
encoded_symbols += rmt_tx_mark_eof(tx_chan, need_eof_mark);
}
t->flags.encoding_done = true;
t->flags.need_eof_mark = need_eof_mark;
}
// for loop transaction, the memory block should accommodate all encoded RMT symbols
@@ -717,7 +724,7 @@ static void rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t
// at the beginning of a new transaction, encoding memory offset should start from zero.
// It will increase in the encode function e.g. `rmt_encode_copy()`
tx_chan->mem_off = 0;
tx_chan->mem_off_bytes = 0;
// use the full memory block for the beginning encoding session
tx_chan->mem_end = tx_chan->ping_pong_symbols * 2;
// perform the encoding session, return the number of encoded symbols
@@ -899,8 +906,7 @@ bool rmt_isr_handle_tx_threshold(rmt_tx_channel_t *tx_chan)
size_t encoded_symbols = t->transmitted_symbol_num;
// encoding finished, only need to send the EOF symbol
if (t->flags.encoding_done) {
rmt_tx_mark_eof(tx_chan);
encoded_symbols += 1;
encoded_symbols += rmt_tx_mark_eof(tx_chan, t->flags.need_eof_mark);
} else {
encoded_symbols += rmt_encode_check_result(tx_chan, t);
}
@@ -1101,8 +1107,7 @@ static bool rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t
rmt_tx_trans_desc_t *t = tx_chan->cur_trans;
size_t encoded_symbols = t->transmitted_symbol_num;
if (t->flags.encoding_done) {
rmt_tx_mark_eof(tx_chan);
encoded_symbols += 1;
encoded_symbols += rmt_tx_mark_eof(tx_chan, t->flags.need_eof_mark);
} else {
encoded_symbols += rmt_encode_check_result(tx_chan, t);
}