forked from espressif/esp-idf
feat(rmt): callback function can support partial receive
This commit is contained in:
@@ -22,7 +22,8 @@ extern "C" {
|
||||
* The variables used in the function should be in the SRAM as well.
|
||||
*/
|
||||
typedef struct {
|
||||
rmt_rx_done_callback_t on_recv_done; /*!< Event callback, invoked when one RMT channel receiving transaction completes */
|
||||
rmt_rx_done_callback_t on_recv_done; /*!< Event callback, invoked when the RMT channel reception is finished
|
||||
or partial data is received */
|
||||
} rmt_rx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
@@ -35,13 +36,13 @@ typedef struct {
|
||||
size_t mem_block_symbols; /*!< Size of memory block, in number of `rmt_symbol_word_t`, must be an even.
|
||||
In the DMA mode, this field controls the DMA buffer size, it can be set to a large value (e.g. 1024);
|
||||
In the normal mode, this field controls the number of RMT memory block that will be used by the channel. */
|
||||
int intr_priority; /*!< RMT interrupt priority,
|
||||
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
|
||||
struct {
|
||||
uint32_t invert_in: 1; /*!< Whether to invert the incoming RMT channel signal */
|
||||
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
} flags; /*!< RX channel config flags */
|
||||
int intr_priority; /*!< RMT interrupt priority,
|
||||
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
|
||||
} rmt_rx_channel_config_t;
|
||||
|
||||
/**
|
||||
@@ -50,6 +51,12 @@ typedef struct {
|
||||
typedef struct {
|
||||
uint32_t signal_range_min_ns; /*!< A pulse whose width is smaller than this threshold will be treated as glitch and ignored */
|
||||
uint32_t signal_range_max_ns; /*!< RMT will stop receiving if one symbol level has kept more than `signal_range_max_ns` */
|
||||
|
||||
/// Receive specific flags
|
||||
struct extra_flags {
|
||||
uint32_t en_partial_rx: 1; /*!< Set this flag if the incoming data is very long, and the driver can only receive the data piece by piece,
|
||||
because the user buffer is not sufficient to save all the data. */
|
||||
} flags; /*!< Receive specific config flags */
|
||||
} rmt_receive_config_t;
|
||||
|
||||
/**
|
||||
|
@@ -56,6 +56,9 @@ typedef bool (*rmt_tx_done_callback_t)(rmt_channel_handle_t tx_chan, const rmt_t
|
||||
typedef struct {
|
||||
rmt_symbol_word_t *received_symbols; /*!< Point to the received RMT symbols */
|
||||
size_t num_symbols; /*!< The number of received RMT symbols */
|
||||
struct {
|
||||
uint32_t is_last: 1; /*!< Indicating if the current received data are the last part of the transaction */
|
||||
} flags; /*!< Extra flags */
|
||||
} rmt_rx_done_event_data_t;
|
||||
|
||||
/**
|
||||
|
@@ -176,6 +176,10 @@ typedef struct {
|
||||
size_t buffer_size; // size of the buffer, in bytes
|
||||
size_t received_symbol_num; // track the number of received symbols
|
||||
size_t copy_dest_off; // tracking offset in the copy destination
|
||||
int dma_desc_index; // tracking the DMA descriptor used by ping-pong
|
||||
struct {
|
||||
uint32_t en_partial_rx: 1; // packet is too long, we need to notify the user to process the data piece by piece, in a ping-pong approach
|
||||
} flags;
|
||||
} rmt_rx_trans_desc_t;
|
||||
|
||||
struct rmt_rx_channel_t {
|
||||
|
@@ -28,7 +28,8 @@
|
||||
#include "rmt_private.h"
|
||||
#include "rom/cache.h"
|
||||
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1))
|
||||
|
||||
static const char *TAG = "rmt";
|
||||
|
||||
@@ -39,36 +40,20 @@ static esp_err_t rmt_rx_disable(rmt_channel_handle_t channel);
|
||||
static void rmt_rx_default_isr(void *args);
|
||||
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
static bool rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
static bool rmt_dma_rx_one_block_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
|
||||
static void rmt_rx_mount_dma_buffer(rmt_dma_descriptor_t *desc_array, rmt_dma_descriptor_t *desc_array_nc, size_t array_size, const void *buffer, size_t buffer_size)
|
||||
static void rmt_rx_mount_dma_buffer(rmt_rx_channel_t *rx_chan, const void *buffer, size_t buffer_size, size_t per_block_size, size_t last_block_size)
|
||||
{
|
||||
size_t prepared_length = 0;
|
||||
uint8_t *data = (uint8_t *)buffer;
|
||||
int dma_node_i = 0;
|
||||
rmt_dma_descriptor_t *desc = NULL;
|
||||
while (buffer_size > RMT_DMA_DESC_BUF_MAX_SIZE) {
|
||||
desc = &desc_array_nc[dma_node_i];
|
||||
desc->dw0.suc_eof = 0;
|
||||
desc->dw0.size = RMT_DMA_DESC_BUF_MAX_SIZE;
|
||||
desc->dw0.length = 0;
|
||||
desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
|
||||
desc->buffer = &data[prepared_length];
|
||||
desc->next = &desc_array[dma_node_i + 1]; // note, we must use the cache address for the "next" pointer
|
||||
prepared_length += RMT_DMA_DESC_BUF_MAX_SIZE;
|
||||
buffer_size -= RMT_DMA_DESC_BUF_MAX_SIZE;
|
||||
dma_node_i++;
|
||||
for (int i = 0; i < rx_chan->num_dma_nodes; i++) {
|
||||
rmt_dma_descriptor_t *desc_nc = &rx_chan->dma_nodes_nc[i];
|
||||
desc_nc->buffer = data + i * per_block_size;
|
||||
desc_nc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
|
||||
desc_nc->dw0.suc_eof = 0;
|
||||
desc_nc->dw0.length = 0;
|
||||
desc_nc->dw0.size = per_block_size;
|
||||
}
|
||||
if (buffer_size) {
|
||||
desc = &desc_array_nc[dma_node_i];
|
||||
desc->dw0.suc_eof = 0;
|
||||
desc->dw0.size = buffer_size;
|
||||
desc->dw0.length = 0;
|
||||
desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
|
||||
desc->buffer = &data[prepared_length];
|
||||
prepared_length += buffer_size;
|
||||
}
|
||||
desc->next = NULL; // one-off DMA chain
|
||||
rx_chan->dma_nodes_nc[rx_chan->num_dma_nodes - 1].dw0.size = last_block_size;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx_channel_config_t *config)
|
||||
@@ -77,8 +62,16 @@ static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx
|
||||
.direction = GDMA_CHANNEL_DIRECTION_RX,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &rx_channel->base.dma_chan), TAG, "allocate RX DMA channel failed");
|
||||
|
||||
// circular DMA descriptor
|
||||
for (int i = 0; i < rx_channel->num_dma_nodes; i++) {
|
||||
rx_channel->dma_nodes_nc[i].next = &rx_channel->dma_nodes[i + 1];
|
||||
}
|
||||
rx_channel->dma_nodes_nc[rx_channel->num_dma_nodes - 1].next = &rx_channel->dma_nodes[0];
|
||||
|
||||
// register event callbacks
|
||||
gdma_rx_event_callbacks_t cbs = {
|
||||
.on_recv_eof = rmt_dma_rx_eof_cb,
|
||||
.on_recv_done = rmt_dma_rx_one_block_cb,
|
||||
};
|
||||
gdma_register_rx_event_callbacks(rx_channel->base.dma_chan, &cbs, rx_channel);
|
||||
return ESP_OK;
|
||||
@@ -204,6 +197,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
|
||||
if (config->flags.with_dma) {
|
||||
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||
num_dma_nodes = config->mem_block_symbols * sizeof(rmt_symbol_word_t) / RMT_DMA_DESC_BUF_MAX_SIZE + 1;
|
||||
num_dma_nodes = MAX(2, num_dma_nodes); // at least 2 DMA nodes for ping-pong
|
||||
// DMA descriptors must be placed in internal SRAM
|
||||
rx_channel->dma_nodes = heap_caps_aligned_calloc(RMT_DMA_DESC_ALIGN, num_dma_nodes, sizeof(rmt_dma_descriptor_t), mem_caps);
|
||||
ESP_GOTO_ON_FALSE(rx_channel->dma_nodes, ESP_ERR_NO_MEM, err, TAG, "no mem for rx channel DMA nodes");
|
||||
@@ -347,6 +341,8 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
||||
ESP_RETURN_ON_FALSE_ISR(channel && buffer && buffer_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(channel->direction == RMT_CHANNEL_DIRECTION_RX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
|
||||
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);
|
||||
size_t per_dma_block_size = 0;
|
||||
size_t last_dma_block_size = 0;
|
||||
|
||||
if (channel->dma_chan) {
|
||||
ESP_RETURN_ON_FALSE_ISR(esp_ptr_internal(buffer), ESP_ERR_INVALID_ARG, TAG, "buffer must locate in internal RAM for DMA use");
|
||||
@@ -356,11 +352,14 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
||||
ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buffer & data_cache_line_mask) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer must be aligned to cache line size");
|
||||
ESP_RETURN_ON_FALSE_ISR((buffer_size & data_cache_line_mask) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer size must be aligned to cache line size");
|
||||
#endif
|
||||
}
|
||||
if (channel->dma_chan) {
|
||||
ESP_RETURN_ON_FALSE_ISR(buffer_size <= rx_chan->num_dma_nodes * RMT_DMA_DESC_BUF_MAX_SIZE,
|
||||
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity");
|
||||
per_dma_block_size = buffer_size / rx_chan->num_dma_nodes;
|
||||
per_dma_block_size = ALIGN_DOWN(per_dma_block_size, sizeof(rmt_symbol_word_t));
|
||||
last_dma_block_size = buffer_size - per_dma_block_size * (rx_chan->num_dma_nodes - 1);
|
||||
ESP_RETURN_ON_FALSE_ISR(last_dma_block_size <= RMT_DMA_DESC_BUF_MAX_SIZE, ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity");
|
||||
}
|
||||
|
||||
rmt_group_t *group = channel->group;
|
||||
rmt_hal_context_t *hal = &group->hal;
|
||||
int channel_id = channel->channel_id;
|
||||
@@ -377,14 +376,17 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
||||
|
||||
// fill in the transaction descriptor
|
||||
rmt_rx_trans_desc_t *t = &rx_chan->trans_desc;
|
||||
memset(t, 0, sizeof(rmt_rx_trans_desc_t));
|
||||
t->buffer = buffer;
|
||||
t->buffer_size = buffer_size;
|
||||
t->received_symbol_num = 0;
|
||||
t->copy_dest_off = 0;
|
||||
t->dma_desc_index = 0;
|
||||
t->flags.en_partial_rx = config->flags.en_partial_rx;
|
||||
|
||||
if (channel->dma_chan) {
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
rmt_rx_mount_dma_buffer(rx_chan->dma_nodes, rx_chan->dma_nodes_nc, rx_chan->num_dma_nodes, buffer, buffer_size);
|
||||
rmt_rx_mount_dma_buffer(rx_chan, buffer, buffer_size, per_dma_block_size, last_dma_block_size);
|
||||
gdma_reset(channel->dma_chan);
|
||||
gdma_start(channel->dma_chan, (intptr_t)rx_chan->dma_nodes); // note, we must use the cached descriptor address to start the DMA
|
||||
#endif
|
||||
@@ -531,16 +533,6 @@ static esp_err_t rmt_rx_disable(rmt_channel_handle_t channel)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR rmt_copy_symbols(rmt_symbol_word_t *symbol_stream, size_t symbol_num, void *buffer, size_t offset, size_t buffer_size)
|
||||
{
|
||||
size_t mem_want = symbol_num * sizeof(rmt_symbol_word_t);
|
||||
size_t mem_have = buffer_size - offset;
|
||||
size_t copy_size = MIN(mem_want, mem_have);
|
||||
// do memory copy
|
||||
memcpy(buffer + offset, symbol_stream, copy_size);
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
|
||||
{
|
||||
rmt_channel_t *channel = &rx_chan->base;
|
||||
@@ -548,21 +540,55 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
|
||||
rmt_hal_context_t *hal = &group->hal;
|
||||
uint32_t channel_id = channel->channel_id;
|
||||
rmt_rx_trans_desc_t *trans_desc = &rx_chan->trans_desc;
|
||||
rmt_rx_done_callback_t cb = rx_chan->on_recv_done;
|
||||
bool need_yield = false;
|
||||
|
||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_RX_DONE(channel_id));
|
||||
|
||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||
// disable the RX engine, it will be enabled again when next time user calls `rmt_receive()`
|
||||
rmt_ll_rx_enable(hal->regs, channel_id, false);
|
||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||
|
||||
uint32_t offset = rmt_ll_rx_get_memory_writer_offset(hal->regs, channel_id);
|
||||
// sanity check
|
||||
assert(offset >= rx_chan->mem_off);
|
||||
size_t mem_want = (offset - rx_chan->mem_off) * sizeof(rmt_symbol_word_t);
|
||||
size_t mem_have = trans_desc->buffer_size - trans_desc->copy_dest_off;
|
||||
size_t copy_size = mem_want;
|
||||
if (mem_want > mem_have) {
|
||||
if (trans_desc->flags.en_partial_rx) { // check partial receive is enabled or not
|
||||
// notify the user to process the received symbols if the buffer is going to be full
|
||||
if (trans_desc->received_symbol_num) {
|
||||
if (cb) {
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = trans_desc->buffer,
|
||||
.num_symbols = trans_desc->received_symbol_num,
|
||||
.flags.is_last = false,
|
||||
};
|
||||
if (cb(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
trans_desc->copy_dest_off = 0;
|
||||
trans_desc->received_symbol_num = 0;
|
||||
mem_have = trans_desc->buffer_size;
|
||||
|
||||
// even user process the partial received data, the remain buffer may still be insufficient
|
||||
if (mem_want > mem_have) {
|
||||
ESP_DRAM_LOGE(TAG, "user buffer too small, received symbols truncated");
|
||||
copy_size = mem_have;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_DRAM_LOGE(TAG, "user buffer too small, received symbols truncated");
|
||||
copy_size = mem_have;
|
||||
}
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_SW);
|
||||
// copy the symbols to user space
|
||||
size_t stream_symbols = offset - rx_chan->mem_off;
|
||||
size_t copy_size = rmt_copy_symbols(channel->hw_mem_base + rx_chan->mem_off, stream_symbols,
|
||||
trans_desc->buffer, trans_desc->copy_dest_off, trans_desc->buffer_size);
|
||||
// copy the symbols to the user buffer
|
||||
memcpy((uint8_t *)trans_desc->buffer + trans_desc->copy_dest_off, channel->hw_mem_base + rx_chan->mem_off, copy_size);
|
||||
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_HW);
|
||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||
|
||||
@@ -579,22 +605,19 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
|
||||
}
|
||||
#endif // !SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
|
||||
// check whether all symbols are copied
|
||||
if (copy_size != stream_symbols * sizeof(rmt_symbol_word_t)) {
|
||||
ESP_DRAM_LOGE(TAG, "user buffer too small, received symbols truncated");
|
||||
}
|
||||
trans_desc->copy_dest_off += copy_size;
|
||||
trans_desc->received_symbol_num += copy_size / sizeof(rmt_symbol_word_t);
|
||||
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
|
||||
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||
|
||||
// notify the user with receive RMT symbols
|
||||
if (rx_chan->on_recv_done) {
|
||||
// notify the user that all RMT symbols are received done
|
||||
if (cb) {
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = trans_desc->buffer,
|
||||
.num_symbols = trans_desc->received_symbol_num,
|
||||
.flags.is_last = true,
|
||||
};
|
||||
if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) {
|
||||
if (cb(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
@@ -604,6 +627,7 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
|
||||
#if SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
static bool IRAM_ATTR rmt_isr_handle_rx_threshold(rmt_rx_channel_t *rx_chan)
|
||||
{
|
||||
bool need_yield = false;
|
||||
rmt_channel_t *channel = &rx_chan->base;
|
||||
rmt_group_t *group = channel->group;
|
||||
rmt_hal_context_t *hal = &group->hal;
|
||||
@@ -612,24 +636,54 @@ static bool IRAM_ATTR rmt_isr_handle_rx_threshold(rmt_rx_channel_t *rx_chan)
|
||||
|
||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_RX_THRES(channel_id));
|
||||
|
||||
size_t mem_want = rx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t);
|
||||
size_t mem_have = trans_desc->buffer_size - trans_desc->copy_dest_off;
|
||||
size_t copy_size = mem_want;
|
||||
if (mem_want > mem_have) {
|
||||
if (trans_desc->flags.en_partial_rx) {
|
||||
// notify the user to process the received symbols if the buffer is going to be full
|
||||
if (trans_desc->received_symbol_num) {
|
||||
rmt_rx_done_callback_t cb = rx_chan->on_recv_done;
|
||||
if (cb) {
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = trans_desc->buffer,
|
||||
.num_symbols = trans_desc->received_symbol_num,
|
||||
.flags.is_last = false,
|
||||
};
|
||||
if (cb(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
trans_desc->copy_dest_off = 0;
|
||||
trans_desc->received_symbol_num = 0;
|
||||
mem_have = trans_desc->buffer_size;
|
||||
|
||||
// even user process the partial received data, the remain buffer size still insufficient
|
||||
if (mem_want > mem_have) {
|
||||
ESP_DRAM_LOGE(TAG, "user buffer too small, received symbols truncated");
|
||||
copy_size = mem_have;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_DRAM_LOGE(TAG, "user buffer too small, received symbols truncated");
|
||||
copy_size = mem_have;
|
||||
}
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_SW);
|
||||
// copy the symbols to user space
|
||||
size_t copy_size = rmt_copy_symbols(channel->hw_mem_base + rx_chan->mem_off, rx_chan->ping_pong_symbols,
|
||||
trans_desc->buffer, trans_desc->copy_dest_off, trans_desc->buffer_size);
|
||||
// copy the symbols to the user buffer
|
||||
memcpy((uint8_t *)trans_desc->buffer + trans_desc->copy_dest_off, channel->hw_mem_base + rx_chan->mem_off, copy_size);
|
||||
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_HW);
|
||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||
|
||||
// check whether all symbols are copied
|
||||
if (copy_size != rx_chan->ping_pong_symbols * sizeof(rmt_symbol_word_t)) {
|
||||
ESP_DRAM_LOGE(TAG, "received symbols truncated");
|
||||
}
|
||||
trans_desc->copy_dest_off += copy_size;
|
||||
trans_desc->received_symbol_num += copy_size / sizeof(rmt_symbol_word_t);
|
||||
|
||||
// update the hw memory offset, where stores the next RMT symbols to copy
|
||||
rx_chan->mem_off = rx_chan->ping_pong_symbols - rx_chan->mem_off;
|
||||
|
||||
return false;
|
||||
return need_yield;
|
||||
}
|
||||
#endif // SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
|
||||
@@ -666,18 +720,29 @@ static void IRAM_ATTR rmt_rx_default_isr(void *args)
|
||||
}
|
||||
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
static size_t IRAM_ATTR rmt_rx_get_received_symbol_num_from_dma(rmt_dma_descriptor_t *desc_nc)
|
||||
static size_t IRAM_ATTR rmt_rx_count_symbols_until_eof(rmt_rx_channel_t *rx_chan, int start_index)
|
||||
{
|
||||
size_t received_bytes = 0;
|
||||
while (desc_nc) {
|
||||
received_bytes += desc_nc->dw0.length;
|
||||
desc_nc = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(desc_nc->next);
|
||||
for (int i = 0; i < rx_chan->num_dma_nodes; i++) {
|
||||
received_bytes += rx_chan->dma_nodes_nc[start_index].dw0.length;
|
||||
if (rx_chan->dma_nodes_nc[start_index].dw0.suc_eof) {
|
||||
break;
|
||||
}
|
||||
start_index++;
|
||||
start_index %= rx_chan->num_dma_nodes;
|
||||
}
|
||||
received_bytes = ALIGN_UP(received_bytes, sizeof(rmt_symbol_word_t));
|
||||
return received_bytes / sizeof(rmt_symbol_word_t);
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
static size_t IRAM_ATTR rmt_rx_count_symbols_for_single_block(rmt_rx_channel_t *rx_chan, int desc_index)
|
||||
{
|
||||
size_t received_bytes = rx_chan->dma_nodes_nc[desc_index].dw0.length;
|
||||
received_bytes = ALIGN_UP(received_bytes, sizeof(rmt_symbol_word_t));
|
||||
return received_bytes / sizeof(rmt_symbol_word_t);
|
||||
}
|
||||
|
||||
static bool IRAM_ATTR rmt_dma_rx_one_block_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
bool need_yield = false;
|
||||
rmt_rx_channel_t *rx_chan = (rmt_rx_channel_t *)user_data;
|
||||
@@ -687,11 +752,6 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
|
||||
rmt_rx_trans_desc_t *trans_desc = &rx_chan->trans_desc;
|
||||
uint32_t channel_id = channel->channel_id;
|
||||
|
||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||
// disable the RX engine, it will be enabled again in the next `rmt_receive()`
|
||||
rmt_ll_rx_enable(hal->regs, channel_id, false);
|
||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
int invalidate_map = CACHE_MAP_L1_DCACHE;
|
||||
if (esp_ptr_external_ram((const void *)trans_desc->buffer)) {
|
||||
@@ -700,19 +760,48 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
|
||||
Cache_Invalidate_Addr(invalidate_map, (uint32_t)trans_desc->buffer, trans_desc->buffer_size);
|
||||
#endif
|
||||
|
||||
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
|
||||
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||
if (event_data->flags.normal_eof) {
|
||||
// if the DMA received an EOF, it means the RMT peripheral has received an "end marker"
|
||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||
// disable the RX engine, it will be enabled again in the next `rmt_receive()`
|
||||
rmt_ll_rx_enable(hal->regs, channel_id, false);
|
||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||
|
||||
if (rx_chan->on_recv_done) {
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = trans_desc->buffer,
|
||||
.num_symbols = rmt_rx_get_received_symbol_num_from_dma(rx_chan->dma_nodes_nc),
|
||||
};
|
||||
if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
|
||||
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||
|
||||
if (rx_chan->on_recv_done) {
|
||||
int recycle_start_index = trans_desc->dma_desc_index;
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = rx_chan->dma_nodes_nc[recycle_start_index].buffer,
|
||||
.num_symbols = rmt_rx_count_symbols_until_eof(rx_chan, recycle_start_index),
|
||||
.flags.is_last = true,
|
||||
};
|
||||
if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it's a partial receive done event
|
||||
if (trans_desc->flags.en_partial_rx) {
|
||||
if (rx_chan->on_recv_done) {
|
||||
size_t dma_desc_index = trans_desc->dma_desc_index;
|
||||
rmt_rx_done_event_data_t edata = {
|
||||
.received_symbols = rx_chan->dma_nodes_nc[dma_desc_index].buffer,
|
||||
.num_symbols = rmt_rx_count_symbols_for_single_block(rx_chan, dma_desc_index),
|
||||
.flags.is_last = false,
|
||||
};
|
||||
if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
|
||||
dma_desc_index++;
|
||||
trans_desc->dma_desc_index = dma_desc_index % rx_chan->num_dma_nodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
#endif // SOC_RMT_SUPPORT_DMA
|
||||
|
@@ -108,13 +108,13 @@ typedef struct {
|
||||
size_t received_symbol_num;
|
||||
rmt_receive_config_t rx_config;
|
||||
rmt_symbol_word_t remote_codes[128];
|
||||
} test_nec_rx_user_data_t;
|
||||
} test_rx_user_data_t;
|
||||
|
||||
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;
|
||||
test_nec_rx_user_data_t *test_user_data = (test_nec_rx_user_data_t *)user_data;
|
||||
test_rx_user_data_t *test_user_data = (test_rx_user_data_t *)user_data;
|
||||
test_user_data->received_symbol_num += edata->num_symbols;
|
||||
// should receive one RMT symbol at a time
|
||||
if (edata->num_symbols == 1) {
|
||||
@@ -142,13 +142,13 @@ static void test_rmt_rx_iram_safe(size_t mem_block_symbols, bool with_dma, rmt_c
|
||||
TEST_ESP_OK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
|
||||
|
||||
// initialize the GPIO level to low
|
||||
TEST_ESP_OK(gpio_set_level(0, 0));
|
||||
TEST_ESP_OK(gpio_set_level(TEST_RMT_GPIO_NUM_A, 0));
|
||||
|
||||
printf("register rx event callbacks\r\n");
|
||||
rmt_rx_event_callbacks_t cbs = {
|
||||
.on_recv_done = test_rmt_rx_done_callback,
|
||||
};
|
||||
test_nec_rx_user_data_t test_user_data = {
|
||||
test_rx_user_data_t test_user_data = {
|
||||
.task_to_notify = xTaskGetCurrentTaskHandle(),
|
||||
.received_symbol_num = 0,
|
||||
.rx_config = {
|
||||
@@ -165,7 +165,7 @@ static void test_rmt_rx_iram_safe(size_t mem_block_symbols, bool with_dma, rmt_c
|
||||
TEST_ESP_OK(rmt_receive(rx_channel, test_user_data.remote_codes, sizeof(test_user_data.remote_codes), &test_user_data.rx_config));
|
||||
|
||||
// disable the flash cache, and simulate input signal by GPIO
|
||||
unity_utils_run_cache_disable_stub(test_simulate_input_post_cache_disable, 0);
|
||||
unity_utils_run_cache_disable_stub(test_simulate_input_post_cache_disable, TEST_RMT_GPIO_NUM_A);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_EQUAL(TEST_RMT_SYMBOLS, test_user_data.received_symbol_num);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "unity.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "driver/rmt_rx.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "test_util_rmt_encoders.h"
|
||||
#include "test_board.h"
|
||||
@@ -45,7 +46,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 128;
|
||||
rmt_symbol_word_t *remote_codes = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(remote_codes);
|
||||
|
||||
rmt_rx_channel_config_t rx_channel_cfg = {
|
||||
@@ -201,3 +202,108 @@ TEST_CASE("rmt rx nec with carrier", "[rmt]")
|
||||
test_rmt_rx_nec_carrier(128, true, RMT_CLK_SRC_DEFAULT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
#define TEST_RMT_SYMBOLS 10000 // a very long frame, contains 10000 symbols
|
||||
|
||||
static void pwm_bit_bang(int gpio_num)
|
||||
{
|
||||
for (int i = 0; i < TEST_RMT_SYMBOLS; i++) {
|
||||
gpio_set_level(gpio_num, 1);
|
||||
esp_rom_delay_us(50);
|
||||
gpio_set_level(gpio_num, 0);
|
||||
esp_rom_delay_us(50);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t task_to_notify;
|
||||
size_t received_symbol_num;
|
||||
} test_rx_user_data_t;
|
||||
|
||||
TEST_RMT_CALLBACK_ATTR
|
||||
static bool test_rmt_partial_receive_done(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
test_rx_user_data_t *test_user_data = (test_rx_user_data_t *)user_data;
|
||||
test_user_data->received_symbol_num += edata->num_symbols;
|
||||
// when receive done, notify the task to check the received data
|
||||
if (edata->flags.is_last) {
|
||||
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
|
||||
}
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static void test_rmt_partial_receive(size_t mem_block_symbols, bool with_dma, rmt_clock_source_t clk_src)
|
||||
{
|
||||
uint32_t const test_rx_buffer_symbols = 128; // the user buffer is small, it can't hold all the received symbols
|
||||
rmt_symbol_word_t *receive_user_buf = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
|
||||
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(receive_user_buf);
|
||||
|
||||
rmt_rx_channel_config_t rx_channel_cfg = {
|
||||
.clk_src = clk_src,
|
||||
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.gpio_num = TEST_RMT_GPIO_NUM_A,
|
||||
.flags.with_dma = with_dma,
|
||||
.flags.io_loop_back = true, // the GPIO will act like a loopback
|
||||
};
|
||||
printf("install rx channel\r\n");
|
||||
rmt_channel_handle_t rx_channel = NULL;
|
||||
TEST_ESP_OK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
|
||||
|
||||
// initialize the GPIO level to low
|
||||
TEST_ESP_OK(gpio_set_level(TEST_RMT_GPIO_NUM_A, 0));
|
||||
|
||||
printf("register rx event callbacks\r\n");
|
||||
rmt_rx_event_callbacks_t cbs = {
|
||||
.on_recv_done = test_rmt_partial_receive_done,
|
||||
};
|
||||
test_rx_user_data_t test_user_data = {
|
||||
.task_to_notify = xTaskGetCurrentTaskHandle(),
|
||||
.received_symbol_num = 0,
|
||||
};
|
||||
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, &test_user_data));
|
||||
|
||||
printf("enable rx channel\r\n");
|
||||
TEST_ESP_OK(rmt_enable(rx_channel));
|
||||
|
||||
rmt_receive_config_t rx_config = {
|
||||
.signal_range_min_ns = 1250,
|
||||
.signal_range_max_ns = 12000000,
|
||||
.flags.en_partial_rx = true, // enable partial receive
|
||||
};
|
||||
// ready to receive
|
||||
TEST_ESP_OK(rmt_receive(rx_channel, receive_user_buf, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &rx_config));
|
||||
|
||||
// simulate input signal by GPIO
|
||||
pwm_bit_bang(TEST_RMT_GPIO_NUM_A);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(2000)));
|
||||
printf("received %zu symbols\r\n", test_user_data.received_symbol_num);
|
||||
TEST_ASSERT_EQUAL(TEST_RMT_SYMBOLS, test_user_data.received_symbol_num);
|
||||
// verify the received data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
printf("{%d:%d},{%d:%d}\r\n", receive_user_buf[i].level0, receive_user_buf[i].duration0, receive_user_buf[i].level1, receive_user_buf[i].duration1);
|
||||
TEST_ASSERT_EQUAL(1, receive_user_buf[i].level0);
|
||||
TEST_ASSERT_INT_WITHIN(20, 50, receive_user_buf[i].duration0);
|
||||
TEST_ASSERT_EQUAL(0, receive_user_buf[i].level1);
|
||||
TEST_ASSERT_INT_WITHIN(20, 50, receive_user_buf[i].duration1);
|
||||
}
|
||||
|
||||
printf("disable rx channels\r\n");
|
||||
TEST_ESP_OK(rmt_disable(rx_channel));
|
||||
printf("delete channels and encoder\r\n");
|
||||
TEST_ESP_OK(rmt_del_channel(rx_channel));
|
||||
free(receive_user_buf);
|
||||
}
|
||||
|
||||
TEST_CASE("rmt rx long frame partially", "[rmt]")
|
||||
{
|
||||
test_rmt_partial_receive(SOC_RMT_MEM_WORDS_PER_CHANNEL, false, RMT_CLK_SRC_DEFAULT);
|
||||
#if SOC_RMT_SUPPORT_DMA
|
||||
test_rmt_partial_receive(256, true, RMT_CLK_SRC_DEFAULT);
|
||||
#endif
|
||||
}
|
||||
#endif // SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
|
@@ -203,9 +203,13 @@ The RX channel-supported event callbacks are listed in the :cpp:type:`rmt_rx_eve
|
||||
|
||||
- :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` sets a callback function for "receive-done" event. The function prototype is declared in :cpp:type:`rmt_rx_done_callback_t`.
|
||||
|
||||
.. note::
|
||||
|
||||
The "receive-done" is not equivalent to "receive-finished". This callback can also be called at a "partial-receive-done" time, for many times during one receive transaction.
|
||||
|
||||
Users can save their own context in :cpp:func:`rmt_tx_register_event_callbacks` and :cpp:func:`rmt_rx_register_event_callbacks` as well, via the parameter ``user_data``. The user data is directly passed to each callback function.
|
||||
|
||||
In the callback function, users can fetch the event-specific data that is filled by the driver in the ``edata``. Note that the ``edata`` pointer is only valid during the callback.
|
||||
In the callback function, users can fetch the event-specific data that is filled by the driver in the ``edata``. Note that the ``edata`` pointer is **only** valid during the callback, please do not try to save this pointer and use that outside of the callback function.
|
||||
|
||||
The TX-done event data is defined in :cpp:type:`rmt_tx_done_event_data_t`:
|
||||
|
||||
@@ -213,8 +217,9 @@ The TX-done event data is defined in :cpp:type:`rmt_tx_done_event_data_t`:
|
||||
|
||||
The RX-complete event data is defined in :cpp:type:`rmt_rx_done_event_data_t`:
|
||||
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::received_symbols` points to the received RMT symbols. These symbols are saved in the ``buffer`` parameter of the :cpp:func:`rmt_receive` function. Users should not free this receive buffer before the callback returns.
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::received_symbols` points to the received RMT symbols. These symbols are saved in the ``buffer`` parameter of the :cpp:func:`rmt_receive` function. Users should not free this receive buffer before the callback returns. If you also enabled the partial receive feature, then the user buffer will be used as a "second level buffer", where its content can be overwritten by data comes in afterwards. In this case, you should copy the received data to another place if you want to keep it or process it later.
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::num_symbols` indicates the number of received RMT symbols. This value is not larger than the ``buffer_size`` parameter of :cpp:func:`rmt_receive` function. If the ``buffer_size`` is not sufficient to accommodate all the received RMT symbols, the driver only keeps the maximum number of symbols that the buffer can hold, and excess symbols are discarded or ignored.
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::is_last` indicates whether the current received buffer is the last one in the transaction. This is useful when you enable the partial reception feature by :cpp:member:`rmt_receive_config_t::extra_flags::en_partial_rx`.
|
||||
|
||||
.. _rmt-enable-and-disable-channel:
|
||||
|
||||
@@ -326,6 +331,7 @@ As also discussed in the :ref:`rmt-enable-and-disable-channel`, calling :cpp:fun
|
||||
|
||||
- :cpp:member:`rmt_receive_config_t::signal_range_min_ns` specifies the minimal valid pulse duration in either high or low logic levels. A pulse width that is smaller than this value is treated as a glitch, and ignored by the hardware.
|
||||
- :cpp:member:`rmt_receive_config_t::signal_range_max_ns` specifies the maximum valid pulse duration in either high or low logic levels. A pulse width that is bigger than this value is treated as **Stop Signal**, and the receiver generates receive-complete event immediately.
|
||||
- If the incoming packet is long, that they cannot be stored in the user buffer at once, you can enable the partial reception feature by setting :cpp:member:`rmt_receive_config_t::extra_flags::en_partial_rx` to ``true``. In this case, the driver invokes :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` callback multiple times during one transaction, when the user buffer is **almost full**. You can check the value of :cpp:member::`rmt_rx_done_event_data_t::is_last` to know if the transaction is about to finish.
|
||||
|
||||
The RMT receiver starts the RX machine after the user calls :cpp:func:`rmt_receive` with the provided configuration above. Note that, this configuration is transaction specific, which means, to start a new round of reception, the user needs to set the :cpp:type:`rmt_receive_config_t` again. The receiver saves the incoming signals into its internal memory block or DMA buffer, in the format of :cpp:type:`rmt_symbol_word_t`.
|
||||
|
||||
@@ -337,7 +343,7 @@ The RMT receiver starts the RX machine after the user calls :cpp:func:`rmt_recei
|
||||
|
||||
Due to the limited size of the memory block, the RMT receiver can only save short frames whose length is not longer than the memory block capacity. Long frames are truncated by the hardware, and the driver reports an error message: ``hw buffer too small, received symbols truncated``.
|
||||
|
||||
The copy destination should be provided in the ``buffer`` parameter of :cpp:func:`rmt_receive` function. If this buffer overlfows due to an insufficient buffer size, the receiver can continue to work, but overflowed symbols are dropped and the following error message is reported: ``user buffer too small, received symbols truncated``. Please take care of the lifecycle of the ``buffer`` parameter, ensuring that the buffer is not recycled before the receiver is finished or stopped.
|
||||
The copy destination should be provided in the ``buffer`` parameter of :cpp:func:`rmt_receive` function. If this buffer overflows due to an insufficient buffer size, the receiver can continue to work, but overflowed symbols are dropped and the following error message is reported: ``user buffer too small, received symbols truncated``. Please take care of the lifecycle of the ``buffer`` parameter, ensuring that the buffer is not recycled before the receiver is finished or stopped.
|
||||
|
||||
The receiver is stopped by the driver when it finishes working, i.e., receive a signal whose duration is bigger than :cpp:member:`rmt_receive_config_t::signal_range_max_ns`. The user needs to call :cpp:func:`rmt_receive` again to restart the receiver, if necessary. The user can get the received data in the :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` callback. See also :ref:`rmt-register-event-callbacks` for more information.
|
||||
|
||||
|
@@ -205,6 +205,10 @@ RMT 发射器可以生成载波信号,并将其调制到消息信号上。载
|
||||
|
||||
也可使用参数 ``user_data``,在 :cpp:func:`rmt_tx_register_event_callbacks` 和 :cpp:func:`rmt_rx_register_event_callbacks` 中保存自定义上下文。用户数据将直接传递给每个回调函数。
|
||||
|
||||
.. note::
|
||||
|
||||
"receive-done" 不等同于 "receive-finished". 这个回调函数也可以在 "partial-receive-done" 时间发生的时候被调用。
|
||||
|
||||
在回调函数中可以获取驱动程序在 ``edata`` 中填充的特定事件数据。注意,``edata`` 指针仅在回调的持续时间内有效。
|
||||
|
||||
有关 TX 完成事件数据的定义,请参阅 :cpp:type:`rmt_tx_done_event_data_t`:
|
||||
@@ -213,8 +217,9 @@ RMT 发射器可以生成载波信号,并将其调制到消息信号上。载
|
||||
|
||||
有关 RX 完成事件数据的定义,请参阅 :cpp:type:`rmt_rx_done_event_data_t`:
|
||||
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::received_symbols` 指向接收到的 RMT 符号,这些符号存储在 :cpp:func:`rmt_receive` 函数的 ``buffer`` 参数中,在回调函数返回前不应释放此接收缓冲区。
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::received_symbols` 指向接收到的 RMT 符号,这些符号存储在 :cpp:func:`rmt_receive` 函数的 ``buffer`` 参数中,在回调函数返回前不应释放此接收缓冲区。如果你还启用了部分接收的功能,则这个用户缓冲区会被用作“二级缓冲区”,其中的内容可以被随后传入的数据覆盖。在这种情况下,如果你想要保存或者稍后处理一些数据,则需要将接收到的数据复制到其他位置。
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::num_symbols` 表示接收到的 RMT 符号数量,该值不会超过 :cpp:func:`rmt_receive` 函数的 ``buffer_size`` 参数。如果 ``buffer_size`` 不足以容纳所有接收到的 RMT 符号,驱动程序将只保存缓冲区能够容纳的最大数量的符号,并丢弃或忽略多余的符号。
|
||||
- :cpp:member:`rmt_rx_done_event_data_t::is_last` 指示收到的数据包是否是当前的接收任务中的最后一个。这个标志在你使能 :cpp:member:`rmt_receive_config_t::extra_flags::en_partial_rx` 部分接收功能时非常有用。
|
||||
|
||||
.. _rmt-enable-and-disable-channel:
|
||||
|
||||
@@ -326,6 +331,7 @@ RMT 是一种特殊的通信外设,无法像 SPI 和 I2C 那样发送原始字
|
||||
|
||||
- :cpp:member:`rmt_receive_config_t::signal_range_min_ns` 指定高电平或低电平有效脉冲的最小持续时间。如果脉冲宽度小于指定值,硬件会将其视作干扰信号并忽略。
|
||||
- :cpp:member:`rmt_receive_config_t::signal_range_max_ns` 指定高电平或低电平有效脉冲的最大持续时间。如果脉冲宽度大于指定值,接收器会将其视作 **停止信号**,并立即生成接收完成事件。
|
||||
- 如果传入的数据包很长,无法一次性保存在用户缓冲区中,可以通过将 :cpp:member:`rmt_receive_config_t::extra_flags::en_partial_rx` 设置为 ``true`` 来开启部分接收功能。在这种情况下,当用户缓冲区快满的时候,驱动会多次调用 :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` 回调函数来通知用户去处理已经收到的数据。你可以检查 :cpp:member::`rmt_rx_done_event_data_t::is_last` 的值来了解当前事务是否已经结束。
|
||||
|
||||
根据以上配置调用 :cpp:func:`rmt_receive` 后,RMT 接收器会启动 RX 机制。注意,以上配置均针对特定事务存在,也就是说,要开启新一轮的接收时,需要再次设置 :cpp:type:`rmt_receive_config_t` 选项。接收器会将传入信号以 :cpp:type:`rmt_symbol_word_t` 的格式保存在内部内存块或 DMA 缓冲区中。
|
||||
|
||||
@@ -337,7 +343,7 @@ RMT 是一种特殊的通信外设,无法像 SPI 和 I2C 那样发送原始字
|
||||
|
||||
由于内存块大小有限,RMT 接收器只能保存长度不超过内存块容量的短帧。硬件会将长帧截断,并由驱动程序报错:``hw buffer too small, received symbols truncated``。
|
||||
|
||||
应在 :cpp:func:`rmt_receive` 函数的 ``buffer`` 参数中提供复制目标。如果由于缓冲区大小不足而导致缓冲区溢出,接收器仍可继续工作,但会丢弃溢出的符号,并报告此错误信息:``user buffer too small, received symbols truncated``。请注意 ``buffer`` 参数的生命周期,确保在接收器完成或停止工作前不会回收缓冲区。
|
||||
应在 :cpp:func:`rmt_receive` 函数的 ``buffer`` 参数中提供复制目标。如果由于缓冲区大小不足而导致缓冲区溢出,接收器仍可继续工作,但会丢弃溢出的符号,并报告此错误信息: ``user buffer too small, received symbols truncated``。请注意 ``buffer`` 参数的生命周期,确保在接收器完成或停止工作前不会回收缓冲区。
|
||||
|
||||
当接收器完成工作,即接收到持续时间大于 :cpp:member:`rmt_receive_config_t::signal_range_max_ns` 的信号时,驱动程序将停止接收器。如有需要,应再次调用 :cpp:func:`rmt_receive` 重新启动接收器。在 :cpp:member:`rmt_rx_event_callbacks_t::on_recv_done` 的回调中可以获取接收到的数据。要获取更多有关详情,请参阅 :ref:`rmt-register-event-callbacks`。
|
||||
|
||||
@@ -573,7 +579,7 @@ Kconfig 选项
|
||||
|
||||
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。详情请参阅 :ref:`rmt-iram-safe`。
|
||||
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` 用于启用调试日志输出,启用此选项将增加固件的二进制文件大小。
|
||||
- :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` 用于控制 RMT 接收函数被链接到系统内存的哪个位置(IRAM 还是 Flash)。详情请参阅 :ref:`rmt-iram-safe`。
|
||||
- :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` 用于控制 RMT 接收函数被链接到系统存储的哪个位置(IRAM 还是 Flash)。详情请参阅 :ref:`rmt-iram-safe`。
|
||||
|
||||
应用示例
|
||||
--------------------
|
||||
|
Reference in New Issue
Block a user