mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-27 03:55:01 +00:00
Merge branch 'feat/bitscrambler_rmt' into 'master'
feat(rmt): play the BitScrambler as an RMT encoder Closes IDF-12018 See merge request espressif/esp-idf!37542
This commit is contained in:
@@ -11,4 +11,5 @@ endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES "esp_mm"
|
||||
INCLUDE_DIRS "include")
|
||||
INCLUDE_DIRS "include"
|
||||
LDFRAGMENTS "linker.lf")
|
||||
|
||||
16
components/esp_driver_bitscrambler/Kconfig
Normal file
16
components/esp_driver_bitscrambler/Kconfig
Normal 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
|
||||
6
components/esp_driver_bitscrambler/linker.lf
Normal file
6
components/esp_driver_bitscrambler/linker.lf
Normal 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)
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "bitscrambler_private.h"
|
||||
#include "hal/bitscrambler_ll.h"
|
||||
@@ -13,6 +14,12 @@
|
||||
|
||||
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_HW_REV 0
|
||||
|
||||
@@ -153,8 +160,8 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
|
||||
if (!handle) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// Allocate memory for private data
|
||||
bitscrambler_t *bs = calloc(1, sizeof(bitscrambler_t));
|
||||
// Allocate memory for the BitScrambler object from internal memory
|
||||
bitscrambler_t *bs = heap_caps_calloc(1, sizeof(bitscrambler_t), BITSCRAMBLER_MEM_ALLOC_CAPS);
|
||||
if (!bs) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
@@ -173,7 +180,7 @@ esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_han
|
||||
return r;
|
||||
}
|
||||
|
||||
// Done.
|
||||
// Return the handle
|
||||
*handle = bs;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,14 @@ if(CONFIG_SOC_RMT_SUPPORTED)
|
||||
"src/rmt_tx.c")
|
||||
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")
|
||||
set(priv_requires "")
|
||||
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()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
|
||||
@@ -5,6 +5,8 @@ menu "ESP-Driver:RMT Configurations"
|
||||
bool "Place RMT TX ISR handler in IRAM to reduce latency"
|
||||
default y
|
||||
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
|
||||
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"
|
||||
default n
|
||||
select RMT_OBJ_CACHE_SAFE
|
||||
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA
|
||||
help
|
||||
Place RMT receive function into IRAM for better performance and fewer cache misses.
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -126,6 +127,13 @@ typedef struct {
|
||||
typedef struct {
|
||||
} 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
|
||||
*/
|
||||
@@ -169,6 +177,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
|
||||
@@ -179,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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* data into RMT symbols.
|
||||
|
||||
@@ -8,9 +8,13 @@ entries:
|
||||
rmt_tx: rmt_tx_do_transaction (noflash)
|
||||
rmt_tx: rmt_encode_check_result (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_bytes_encoder_reset (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_simple_encoder_reset (noflash)
|
||||
|
||||
if SOC_RMT_SUPPORT_TX_LOOP_COUNT = y:
|
||||
rmt_tx: rmt_isr_handle_tx_loop_end (noflash)
|
||||
@@ -18,6 +22,10 @@ entries:
|
||||
if SOC_RMT_SUPPORT_DMA = y:
|
||||
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:
|
||||
rmt_rx: rmt_rx_default_isr (noflash)
|
||||
rmt_rx: rmt_isr_handle_rx_done (noflash)
|
||||
@@ -30,51 +38,3 @@ entries:
|
||||
|
||||
if RMT_RECV_FUNC_IN_IRAM = y:
|
||||
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)
|
||||
|
||||
143
components/esp_driver_rmt/src/rmt_encoder_bs.c
Normal file
143
components/esp_driver_rmt/src/rmt_encoder_bs.c
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -667,6 +674,9 @@ static void rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t
|
||||
// update current transaction
|
||||
tx_chan->cur_trans = t;
|
||||
|
||||
// reset RMT encoder before starting a new transaction
|
||||
rmt_encoder_reset(t->encoder);
|
||||
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
if (channel->dma_chan) {
|
||||
gdma_reset(channel->dma_chan);
|
||||
@@ -717,7 +727,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
|
||||
@@ -838,8 +848,6 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
|
||||
// recycle the interrupted transaction
|
||||
if (tx_chan->cur_trans) {
|
||||
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;
|
||||
|
||||
@@ -899,8 +907,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 +1108,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);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@ if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE)
|
||||
list(APPEND srcs "test_rmt_sleep.c")
|
||||
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}"
|
||||
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)
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED AND CONFIG_SOC_RMT_SUPPORT_DMA)
|
||||
target_bitscrambler_add_src("test_tx.bsasm")
|
||||
endif()
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
22
components/esp_driver_rmt/test_apps/rmt/main/test_tx.bsasm
Normal file
22
components/esp_driver_rmt/test_apps/rmt/main/test_tx.bsasm
Normal 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
|
||||
@@ -62,7 +62,7 @@ static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
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_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;
|
||||
}
|
||||
|
||||
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_encoder_reset(nec_encoder->copy_encoder);
|
||||
|
||||
Reference in New Issue
Block a user