esp-idf/components/driver/rmt/rmt_encoder.c
morris 2fb43820c2 driver_ng: implement new rmt driver
The legacy driver can't handle the breaking change between esp chips

very well.

And it's not elegant to extend new feature like DMA, ETM.

The new driver can return a opaque handle for each RMT channel.

An obvious transaction concept was also introduced.

TX and RX functionalities are splited out.
2022-05-07 10:34:50 +00:00

302 lines
11 KiB
C

/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include "sdkconfig.h"
#if CONFIG_RMT_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "driver/rmt_encoder.h"
#include "rmt_private.h"
static const char *TAG = "rmt";
typedef struct rmt_bytes_encoder_t {
rmt_encoder_t base; // encoder base class
size_t last_bit_index; // index of the encoding bit position in the encoding byte
size_t last_byte_index; // index of the encoding byte in the primary 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_bytes_encoder_t;
typedef struct rmt_copy_encoder_t {
rmt_encoder_t base; // encoder base class
size_t last_symbol_index; // index of symbol position in the primary stream
} rmt_copy_encoder_t;
static esp_err_t rmt_bytes_encoder_reset(rmt_encoder_t *encoder)
{
rmt_bytes_encoder_t *bytes_encoder = __containerof(encoder, rmt_bytes_encoder_t, base);
// reset index to zero
bytes_encoder->last_bit_index = 0;
bytes_encoder->last_byte_index = 0;
return ESP_OK;
}
static inline uint8_t _bitwise_reverse(uint8_t n)
{
n = ((n & 0xf0) >> 4) | ((n & 0x0f) << 4);
n = ((n & 0xcc) >> 2) | ((n & 0x33) << 2);
n = ((n & 0xaa) >> 1) | ((n & 0x55) << 1);
return n;
}
static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_handle_t channel,
const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_bytes_encoder_t *bytes_encoder = __containerof(encoder, rmt_bytes_encoder_t, base);
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
const uint8_t *nd = (const uint8_t *)primary_data;
rmt_encode_state_t state = 0;
dma_descriptor_t *desc0 = NULL;
dma_descriptor_t *desc1 = NULL;
size_t byte_index = bytes_encoder->last_byte_index;
size_t bit_index = bytes_encoder->last_bit_index;
// 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;
// where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to = channel->dma_chan ? channel->dma_mem_base : 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 (channel->dma_chan) {
// mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes[0];
} else {
desc0 = &tx_chan->dma_nodes[1];
}
}
size_t len = encode_len;
while (len > 0) {
// start from last time truncated encoding
uint8_t cur_byte = nd[byte_index];
// bit-wise reverse
if (bytes_encoder->flags.msb_first) {
cur_byte = _bitwise_reverse(cur_byte);
}
while ((len > 0) && (bit_index < 8)) {
if (cur_byte & (1 << bit_index)) {
mem_to[tx_chan->mem_off++] = bytes_encoder->bit1;
} else {
mem_to[tx_chan->mem_off++] = bytes_encoder->bit0;
}
len--;
bit_index++;
}
if (bit_index >= 8) {
byte_index++;
bit_index = 0;
}
}
if (channel->dma_chan) {
// mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes[0];
} else {
desc1 = &tx_chan->dma_nodes[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
bytes_encoder->last_bit_index = bit_index;
bytes_encoder->last_byte_index = byte_index;
} else {
// reset internal index if encoding session has finished
bytes_encoder->last_bit_index = 0;
bytes_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 (tx_chan->mem_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;
}
*ret_state = state;
return encode_len;
}
static esp_err_t rmt_copy_encoder_reset(rmt_encoder_t *encoder)
{
rmt_copy_encoder_t *copy_encoder = __containerof(encoder, rmt_copy_encoder_t, base);
copy_encoder->last_symbol_index = 0;
return ESP_OK;
}
static size_t IRAM_ATTR 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)
{
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_encode_state_t state = 0;
dma_descriptor_t *desc0 = NULL;
dma_descriptor_t *desc1 = NULL;
size_t symbol_index = copy_encoder->last_symbol_index;
// 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;
// where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to = channel->dma_chan ? channel->dma_mem_base : 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 (channel->dma_chan) {
// mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes[0];
} else {
desc0 = &tx_chan->dma_nodes[1];
}
}
size_t len = encode_len;
while (len > 0) {
mem_to[tx_chan->mem_off++] = symbols[symbol_index++];
len--;
}
if (channel->dma_chan) {
// mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes[0];
} else {
desc1 = &tx_chan->dma_nodes[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
copy_encoder->last_symbol_index = symbol_index;
} else {
// reset internal index if encoding session has finished
copy_encoder->last_symbol_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 (tx_chan->mem_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;
}
*ret_state = state;
return encode_len;
}
static esp_err_t rmt_del_bytes_encoder(rmt_encoder_t *encoder)
{
rmt_bytes_encoder_t *bytes_encoder = __containerof(encoder, rmt_bytes_encoder_t, base);
free(bytes_encoder);
return ESP_OK;
}
static esp_err_t rmt_del_copy_encoder(rmt_encoder_t *encoder)
{
rmt_copy_encoder_t *copy_encoder = __containerof(encoder, rmt_copy_encoder_t, base);
free(copy_encoder);
return ESP_OK;
}
esp_err_t rmt_new_bytes_encoder(const rmt_bytes_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_bytes_encoder_t *encoder = heap_caps_calloc(1, sizeof(rmt_bytes_encoder_t), RMT_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for bytes encoder");
encoder->base.encode = rmt_encode_bytes;
encoder->base.del = rmt_del_bytes_encoder;
encoder->base.reset = rmt_bytes_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 bytes encoder @%p", encoder);
err:
return ret;
}
esp_err_t rmt_new_copy_encoder(const rmt_copy_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_copy_encoder_t *encoder = heap_caps_calloc(1, sizeof(rmt_copy_encoder_t), RMT_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for copy encoder");
encoder->base.encode = rmt_encode_copy;
encoder->base.del = rmt_del_copy_encoder;
encoder->base.reset = rmt_copy_encoder_reset;
// return general encoder handle
*ret_encoder = &encoder->base;
ESP_LOGD(TAG, "new copy encoder @%p", encoder);
err:
return ret;
}
esp_err_t rmt_del_encoder(rmt_encoder_handle_t encoder)
{
ESP_RETURN_ON_FALSE(encoder, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return encoder->del(encoder);
}
esp_err_t rmt_encoder_reset(rmt_encoder_handle_t encoder)
{
ESP_RETURN_ON_FALSE(encoder, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return encoder->reset(encoder);
}