mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/rmt_stop_issue_v5.1' into 'release/v5.1'
fix(rmt): a disabled channel may pick up a pending transaction (v5.1) See merge request espressif/esp-idf!26779
This commit is contained in:
commit
ceb0aec0cc
@ -352,6 +352,15 @@ menu "Driver Configurations"
|
|||||||
Ensure the RMT interrupt is IRAM-Safe by allowing the interrupt handler to be
|
Ensure the RMT interrupt is IRAM-Safe by allowing the interrupt handler to be
|
||||||
executable when the cache is disabled (e.g. SPI Flash write).
|
executable when the cache is disabled (e.g. SPI Flash write).
|
||||||
|
|
||||||
|
config RMT_RECV_FUNC_IN_IRAM
|
||||||
|
bool "Place RMT receive function into IRAM"
|
||||||
|
default n
|
||||||
|
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA # RMT needs to start the GDMA in the receive function
|
||||||
|
help
|
||||||
|
Place RMT receive function into IRAM,
|
||||||
|
so that the receive function can be IRAM-safe and able to be called when the flash cache is disabled.
|
||||||
|
Enabling this option can improve driver performance as well.
|
||||||
|
|
||||||
config RMT_SUPPRESS_DEPRECATE_WARN
|
config RMT_SUPPRESS_DEPRECATE_WARN
|
||||||
bool "Suppress legacy driver deprecated warning"
|
bool "Suppress legacy driver deprecated warning"
|
||||||
default n
|
default n
|
||||||
|
@ -21,3 +21,7 @@ entries:
|
|||||||
if MCPWM_CTRL_FUNC_IN_IRAM = y:
|
if MCPWM_CTRL_FUNC_IN_IRAM = y:
|
||||||
mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)
|
mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)
|
||||||
mcpwm_timer: mcpwm_timer_set_period (noflash)
|
mcpwm_timer: mcpwm_timer_set_period (noflash)
|
||||||
|
if RMT_RECV_FUNC_IN_IRAM = y:
|
||||||
|
rmt_rx: rmt_receive (noflash)
|
||||||
|
if SOC_RMT_SUPPORT_DMA = y:
|
||||||
|
rmt_rx: rmt_rx_mount_dma_buffer (noflash)
|
||||||
|
@ -72,6 +72,8 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
|
|||||||
*
|
*
|
||||||
* @note This function is non-blocking, it initiates a new receive job and then returns.
|
* @note This function is non-blocking, it initiates a new receive job and then returns.
|
||||||
* User should check the received data from the `on_recv_done` callback that registered by `rmt_rx_register_event_callbacks()`.
|
* User should check the received data from the `on_recv_done` callback that registered by `rmt_rx_register_event_callbacks()`.
|
||||||
|
* @note This function can also be called in ISR context.
|
||||||
|
* @note If you want this function to work even when the flash cache is disabled, please enable the `CONFIG_RMT_RECV_FUNC_IN_IRAM` option.
|
||||||
*
|
*
|
||||||
* @param[in] rx_channel RMT RX channel that created by `rmt_new_rx_channel()`
|
* @param[in] rx_channel RMT RX channel that created by `rmt_new_rx_channel()`
|
||||||
* @param[in] buffer The buffer to store the received RMT symbols
|
* @param[in] buffer The buffer to store the received RMT symbols
|
||||||
|
@ -37,14 +37,14 @@ typedef struct {
|
|||||||
In the DMA mode, this field controls the DMA buffer size, it can be set to a large value;
|
In the DMA mode, this field controls the DMA buffer size, it can be set to a large value;
|
||||||
In the normal mode, this field controls the number of RMT memory block that will be used by the channel. */
|
In the normal mode, this field controls the number of RMT memory block that will be used by the channel. */
|
||||||
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background */
|
size_t trans_queue_depth; /*!< Depth of internal transfer queue, increase this value can support more transfers pending in the background */
|
||||||
|
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 {
|
struct {
|
||||||
uint32_t invert_out: 1; /*!< Whether to invert the RMT channel signal before output to GPIO pad */
|
uint32_t invert_out: 1; /*!< Whether to invert the RMT channel signal before output to GPIO pad */
|
||||||
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
|
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
|
||||||
uint32_t io_loop_back: 1; /*!< The signal output from the GPIO will be fed to the input path as well */
|
uint32_t io_loop_back: 1; /*!< The signal output from the GPIO will be fed to the input path as well */
|
||||||
uint32_t io_od_mode: 1; /*!< Configure the GPIO as open-drain mode */
|
uint32_t io_od_mode: 1; /*!< Configure the GPIO as open-drain mode */
|
||||||
} flags; /*!< TX channel config flags */
|
} flags; /*!< TX 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_tx_channel_config_t;
|
} rmt_tx_channel_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +54,8 @@ typedef struct {
|
|||||||
int loop_count; /*!< Specify the times of transmission in a loop, -1 means transmitting in an infinite loop */
|
int loop_count; /*!< Specify the times of transmission in a loop, -1 means transmitting in an infinite loop */
|
||||||
struct {
|
struct {
|
||||||
uint32_t eot_level : 1; /*!< Set the output level for the "End Of Transmission" */
|
uint32_t eot_level : 1; /*!< Set the output level for the "End Of Transmission" */
|
||||||
} flags; /*!< Transmit config flags */
|
uint32_t queue_nonblocking : 1; /*!< If set, when the transaction queue is full, driver will not block the thread but return directly */
|
||||||
|
} flags; /*!< Transmit specific config flags */
|
||||||
} rmt_transmit_config_t;
|
} rmt_transmit_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,8 +84,11 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
|
|||||||
/**
|
/**
|
||||||
* @brief Transmit data by RMT TX channel
|
* @brief Transmit data by RMT TX channel
|
||||||
*
|
*
|
||||||
* @note This function will construct a transaction descriptor and push to a queue. The transaction will not start immediately until it's dispatched in the ISR.
|
* @note This function constructs a transaction descriptor then pushes to a queue.
|
||||||
* If there're too many transactions pending in the queue, this function will block until the queue has free space.
|
* The transaction will not start immediately if there's another one under processing.
|
||||||
|
* Based on the setting of `rmt_transmit_config_t::queue_nonblocking`,
|
||||||
|
* if there're too many transactions pending in the queue, this function can block until it has free slot,
|
||||||
|
* otherwise just return quickly.
|
||||||
* @note The data to be transmitted will be encoded into RMT symbols by the specific `encoder`.
|
* @note The data to be transmitted will be encoded into RMT symbols by the specific `encoder`.
|
||||||
*
|
*
|
||||||
* @param[in] tx_channel RMT TX channel that created by `rmt_new_tx_channel()`
|
* @param[in] tx_channel RMT TX channel that created by `rmt_new_tx_channel()`
|
||||||
|
@ -180,7 +180,6 @@ esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_conf
|
|||||||
esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
|
esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
|
||||||
gpio_reset_pin(channel->gpio_num);
|
gpio_reset_pin(channel->gpio_num);
|
||||||
return channel->del(channel);
|
return channel->del(channel);
|
||||||
}
|
}
|
||||||
@ -188,14 +187,12 @@ esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
|
|||||||
esp_err_t rmt_enable(rmt_channel_handle_t channel)
|
esp_err_t rmt_enable(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
|
||||||
return channel->enable(channel);
|
return channel->enable(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t rmt_disable(rmt_channel_handle_t channel)
|
esp_err_t rmt_disable(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
|
|
||||||
return channel->disable(channel);
|
return channel->disable(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@ -26,7 +27,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_RMT_ISR_IRAM_SAFE
|
#if CONFIG_RMT_ISR_IRAM_SAFE || CONFIG_RMT_RECV_FUNC_IN_IRAM
|
||||||
#define RMT_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
#define RMT_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||||
#else
|
#else
|
||||||
#define RMT_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
#define RMT_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||||
@ -69,8 +70,12 @@ typedef enum {
|
|||||||
} rmt_channel_direction_t;
|
} rmt_channel_direction_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
RMT_FSM_INIT_WAIT,
|
||||||
RMT_FSM_INIT,
|
RMT_FSM_INIT,
|
||||||
|
RMT_FSM_ENABLE_WAIT,
|
||||||
RMT_FSM_ENABLE,
|
RMT_FSM_ENABLE,
|
||||||
|
RMT_FSM_RUN_WAIT,
|
||||||
|
RMT_FSM_RUN,
|
||||||
} rmt_fsm_t;
|
} rmt_fsm_t;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -108,7 +113,7 @@ struct rmt_channel_t {
|
|||||||
portMUX_TYPE spinlock; // prevent channel resource accessing by user and interrupt concurrently
|
portMUX_TYPE spinlock; // prevent channel resource accessing by user and interrupt concurrently
|
||||||
uint32_t resolution_hz; // channel clock resolution
|
uint32_t resolution_hz; // channel clock resolution
|
||||||
intr_handle_t intr; // allocated interrupt handle for each channel
|
intr_handle_t intr; // allocated interrupt handle for each channel
|
||||||
rmt_fsm_t fsm; // channel life cycle specific FSM
|
_Atomic rmt_fsm_t fsm; // channel life cycle specific FSM
|
||||||
rmt_channel_direction_t direction; // channel direction
|
rmt_channel_direction_t direction; // channel direction
|
||||||
rmt_symbol_word_t *hw_mem_base; // base address of RMT channel hardware memory
|
rmt_symbol_word_t *hw_mem_base; // base address of RMT channel hardware memory
|
||||||
rmt_symbol_word_t *dma_mem_base; // base address of RMT channel DMA buffer
|
rmt_symbol_word_t *dma_mem_base; // base address of RMT channel DMA buffer
|
||||||
|
@ -283,10 +283,10 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
|
|||||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO);
|
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO);
|
||||||
|
|
||||||
// initialize other members of rx channel
|
// initialize other members of rx channel
|
||||||
|
portMUX_INITIALIZE(&rx_channel->base.spinlock);
|
||||||
|
atomic_init(&rx_channel->base.fsm, RMT_FSM_INIT);
|
||||||
rx_channel->base.direction = RMT_CHANNEL_DIRECTION_RX;
|
rx_channel->base.direction = RMT_CHANNEL_DIRECTION_RX;
|
||||||
rx_channel->base.fsm = RMT_FSM_INIT;
|
|
||||||
rx_channel->base.hw_mem_base = &RMTMEM.channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].symbols[0];
|
rx_channel->base.hw_mem_base = &RMTMEM.channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].symbols[0];
|
||||||
rx_channel->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
|
||||||
// polymorphic methods
|
// polymorphic methods
|
||||||
rx_channel->base.del = rmt_del_rx_channel;
|
rx_channel->base.del = rmt_del_rx_channel;
|
||||||
rx_channel->base.set_carrier_action = rmt_rx_demodulate_carrier;
|
rx_channel->base.set_carrier_action = rmt_rx_demodulate_carrier;
|
||||||
@ -308,6 +308,8 @@ err:
|
|||||||
|
|
||||||
static esp_err_t rmt_del_rx_channel(rmt_channel_handle_t channel)
|
static esp_err_t rmt_del_rx_channel(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(atomic_load(&channel->fsm) == RMT_FSM_INIT,
|
||||||
|
ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
||||||
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);
|
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
int group_id = group->group_id;
|
int group_id = group->group_id;
|
||||||
@ -340,16 +342,15 @@ esp_err_t rmt_rx_register_event_callbacks(rmt_channel_handle_t channel, const rm
|
|||||||
|
|
||||||
esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_size, const rmt_receive_config_t *config)
|
esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_size, const rmt_receive_config_t *config)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(channel && buffer && buffer_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE_ISR(channel && buffer && buffer_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
ESP_RETURN_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_RX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
|
ESP_RETURN_ON_FALSE_ISR(channel->direction == RMT_CHANNEL_DIRECTION_RX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
|
||||||
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
|
|
||||||
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);
|
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);
|
||||||
|
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(buffer), ESP_ERR_INVALID_ARG, TAG, "buffer must locate in internal RAM for DMA use");
|
ESP_RETURN_ON_FALSE_ISR(esp_ptr_internal(buffer), ESP_ERR_INVALID_ARG, TAG, "buffer must locate in internal RAM for DMA use");
|
||||||
}
|
}
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
ESP_RETURN_ON_FALSE(buffer_size <= rx_chan->num_dma_nodes * RMT_DMA_DESC_BUF_MAX_SIZE,
|
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");
|
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity");
|
||||||
}
|
}
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
@ -358,8 +359,13 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
|||||||
|
|
||||||
uint32_t filter_reg_value = ((uint64_t)group->resolution_hz * config->signal_range_min_ns) / 1000000000UL;
|
uint32_t filter_reg_value = ((uint64_t)group->resolution_hz * config->signal_range_min_ns) / 1000000000UL;
|
||||||
uint32_t idle_reg_value = ((uint64_t)channel->resolution_hz * config->signal_range_max_ns) / 1000000000UL;
|
uint32_t idle_reg_value = ((uint64_t)channel->resolution_hz * config->signal_range_max_ns) / 1000000000UL;
|
||||||
ESP_RETURN_ON_FALSE(filter_reg_value <= RMT_LL_MAX_FILTER_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_min_ns too big");
|
ESP_RETURN_ON_FALSE_ISR(filter_reg_value <= RMT_LL_MAX_FILTER_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_min_ns too big");
|
||||||
ESP_RETURN_ON_FALSE(idle_reg_value <= RMT_LL_MAX_IDLE_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_max_ns too big");
|
ESP_RETURN_ON_FALSE_ISR(idle_reg_value <= RMT_LL_MAX_IDLE_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_max_ns too big");
|
||||||
|
|
||||||
|
// check if we're in a proper state to start the receiver
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
ESP_RETURN_ON_FALSE_ISR(atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT),
|
||||||
|
ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
|
||||||
|
|
||||||
// fill in the transaction descriptor
|
// fill in the transaction descriptor
|
||||||
rmt_rx_trans_desc_t *t = &rx_chan->trans_desc;
|
rmt_rx_trans_desc_t *t = &rx_chan->trans_desc;
|
||||||
@ -377,7 +383,7 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
|||||||
}
|
}
|
||||||
|
|
||||||
rx_chan->mem_off = 0;
|
rx_chan->mem_off = 0;
|
||||||
portENTER_CRITICAL(&channel->spinlock);
|
portENTER_CRITICAL_SAFE(&channel->spinlock);
|
||||||
// reset memory writer offset
|
// reset memory writer offset
|
||||||
rmt_ll_rx_reset_pointer(hal->regs, channel_id);
|
rmt_ll_rx_reset_pointer(hal->regs, channel_id);
|
||||||
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_HW);
|
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_HW);
|
||||||
@ -387,7 +393,12 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
|
|||||||
rmt_ll_rx_set_idle_thres(hal->regs, channel_id, idle_reg_value);
|
rmt_ll_rx_set_idle_thres(hal->regs, channel_id, idle_reg_value);
|
||||||
// turn on RMT RX machine
|
// turn on RMT RX machine
|
||||||
rmt_ll_rx_enable(hal->regs, channel_id, true);
|
rmt_ll_rx_enable(hal->regs, channel_id, true);
|
||||||
portEXIT_CRITICAL(&channel->spinlock);
|
portEXIT_CRITICAL_SAFE(&channel->spinlock);
|
||||||
|
|
||||||
|
// saying we're in running state, this state will last until the receiving is done
|
||||||
|
// i.e., we will switch back to the enable state in the receive done interrupt handler
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_RUN);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,13 +443,18 @@ static esp_err_t rmt_rx_demodulate_carrier(rmt_channel_handle_t channel, const r
|
|||||||
|
|
||||||
static esp_err_t rmt_rx_enable(rmt_channel_handle_t channel)
|
static esp_err_t rmt_rx_enable(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
|
// can only enable the channel when it's in "init" state
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_INIT;
|
||||||
|
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_ENABLE_WAIT),
|
||||||
|
ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
||||||
|
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
rmt_hal_context_t *hal = &group->hal;
|
||||||
int channel_id = channel->channel_id;
|
int channel_id = channel->channel_id;
|
||||||
|
|
||||||
// acquire power manager lock
|
// acquire power manager lock
|
||||||
if (channel->pm_lock) {
|
if (channel->pm_lock) {
|
||||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(channel->pm_lock), TAG, "acquire pm_lock failed");
|
esp_pm_lock_acquire(channel->pm_lock);
|
||||||
}
|
}
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
#if SOC_RMT_SUPPORT_DMA
|
#if SOC_RMT_SUPPORT_DMA
|
||||||
@ -454,12 +470,26 @@ static esp_err_t rmt_rx_enable(rmt_channel_handle_t channel)
|
|||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_RX_MASK(channel_id), true);
|
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_RX_MASK(channel_id), true);
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
portEXIT_CRITICAL(&group->spinlock);
|
||||||
}
|
}
|
||||||
channel->fsm = RMT_FSM_ENABLE;
|
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t rmt_rx_disable(rmt_channel_handle_t channel)
|
static esp_err_t rmt_rx_disable(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
|
// can disable the channel when it's in `enable` or `run` state
|
||||||
|
bool valid_state = false;
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_INIT_WAIT)) {
|
||||||
|
valid_state = true;
|
||||||
|
}
|
||||||
|
expected_fsm = RMT_FSM_RUN;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_INIT_WAIT)) {
|
||||||
|
valid_state = true;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_FALSE(valid_state, ESP_ERR_INVALID_STATE, TAG, "channel not in enable or run state");
|
||||||
|
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
rmt_hal_context_t *hal = &group->hal;
|
||||||
int channel_id = channel->channel_id;
|
int channel_id = channel->channel_id;
|
||||||
@ -485,9 +515,11 @@ static esp_err_t rmt_rx_disable(rmt_channel_handle_t channel)
|
|||||||
|
|
||||||
// release power manager lock
|
// release power manager lock
|
||||||
if (channel->pm_lock) {
|
if (channel->pm_lock) {
|
||||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(channel->pm_lock), TAG, "release pm_lock failed");
|
esp_pm_lock_release(channel->pm_lock);
|
||||||
}
|
}
|
||||||
channel->fsm = RMT_FSM_INIT;
|
|
||||||
|
// now we can switch the state to init
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_INIT);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,6 +577,9 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
|
|||||||
}
|
}
|
||||||
trans_desc->copy_dest_off += copy_size;
|
trans_desc->copy_dest_off += copy_size;
|
||||||
trans_desc->received_symbol_num += copy_size / sizeof(rmt_symbol_word_t);
|
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
|
// notify the user with receive RMT symbols
|
||||||
if (rx_chan->on_recv_done) {
|
if (rx_chan->on_recv_done) {
|
||||||
rmt_rx_done_event_data_t edata = {
|
rmt_rx_done_event_data_t edata = {
|
||||||
@ -649,6 +684,9 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
|
|||||||
rmt_ll_rx_enable(hal->regs, channel_id, false);
|
rmt_ll_rx_enable(hal->regs, channel_id, false);
|
||||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||||
|
|
||||||
|
// 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) {
|
if (rx_chan->on_recv_done) {
|
||||||
rmt_rx_done_event_data_t edata = {
|
rmt_rx_done_event_data_t edata = {
|
||||||
.received_symbols = trans_desc->buffer,
|
.received_symbols = trans_desc->buffer,
|
||||||
@ -658,6 +696,7 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
|
|||||||
need_yield = true;
|
need_yield = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return need_yield;
|
return need_yield;
|
||||||
}
|
}
|
||||||
#endif // SOC_RMT_SUPPORT_DMA
|
#endif // SOC_RMT_SUPPORT_DMA
|
||||||
|
@ -40,6 +40,7 @@ static esp_err_t rmt_tx_modulate_carrier(rmt_channel_handle_t channel, const rmt
|
|||||||
static esp_err_t rmt_tx_enable(rmt_channel_handle_t channel);
|
static esp_err_t rmt_tx_enable(rmt_channel_handle_t channel);
|
||||||
static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel);
|
static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel);
|
||||||
static void rmt_tx_default_isr(void *args);
|
static void rmt_tx_default_isr(void *args);
|
||||||
|
static void rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_trans_desc_t *t);
|
||||||
|
|
||||||
#if SOC_RMT_SUPPORT_DMA
|
#if SOC_RMT_SUPPORT_DMA
|
||||||
static bool rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
static bool rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||||
@ -302,10 +303,10 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
|
|||||||
config->flags.invert_out, false);
|
config->flags.invert_out, false);
|
||||||
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO);
|
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO);
|
||||||
|
|
||||||
|
portMUX_INITIALIZE(&tx_channel->base.spinlock);
|
||||||
|
atomic_init(&tx_channel->base.fsm, RMT_FSM_INIT);
|
||||||
tx_channel->base.direction = RMT_CHANNEL_DIRECTION_TX;
|
tx_channel->base.direction = RMT_CHANNEL_DIRECTION_TX;
|
||||||
tx_channel->base.fsm = RMT_FSM_INIT;
|
|
||||||
tx_channel->base.hw_mem_base = &RMTMEM.channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].symbols[0];
|
tx_channel->base.hw_mem_base = &RMTMEM.channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].symbols[0];
|
||||||
tx_channel->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
|
||||||
// polymorphic methods
|
// polymorphic methods
|
||||||
tx_channel->base.del = rmt_del_tx_channel;
|
tx_channel->base.del = rmt_del_tx_channel;
|
||||||
tx_channel->base.set_carrier_action = rmt_tx_modulate_carrier;
|
tx_channel->base.set_carrier_action = rmt_tx_modulate_carrier;
|
||||||
@ -327,6 +328,8 @@ err:
|
|||||||
|
|
||||||
static esp_err_t rmt_del_tx_channel(rmt_channel_handle_t channel)
|
static esp_err_t rmt_del_tx_channel(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(atomic_load(&channel->fsm) == RMT_FSM_INIT,
|
||||||
|
ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
||||||
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
int group_id = group->group_id;
|
int group_id = group->group_id;
|
||||||
@ -365,7 +368,7 @@ esp_err_t rmt_new_sync_manager(const rmt_sync_manager_config_t *config, rmt_sync
|
|||||||
channel = config->tx_channel_array[i];
|
channel = config->tx_channel_array[i];
|
||||||
ESP_GOTO_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, err, TAG, "sync manager supports TX channel only");
|
ESP_GOTO_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, err, TAG, "sync manager supports TX channel only");
|
||||||
ESP_GOTO_ON_FALSE(channel->group == group, ESP_ERR_INVALID_ARG, err, TAG, "channels to be managed should locate in the same group");
|
ESP_GOTO_ON_FALSE(channel->group == group, ESP_ERR_INVALID_ARG, err, TAG, "channels to be managed should locate in the same group");
|
||||||
ESP_GOTO_ON_FALSE(channel->fsm == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "channel should be started before creating sync manager");
|
ESP_GOTO_ON_FALSE(atomic_load(&channel->fsm) == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "channel not in enable state");
|
||||||
channel_mask |= 1 << channel->channel_id;
|
channel_mask |= 1 << channel->channel_id;
|
||||||
}
|
}
|
||||||
synchro->channel_mask = channel_mask;
|
synchro->channel_mask = channel_mask;
|
||||||
@ -391,7 +394,6 @@ esp_err_t rmt_new_sync_manager(const rmt_sync_manager_config_t *config, rmt_sync
|
|||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
portEXIT_CRITICAL(&group->spinlock);
|
||||||
|
|
||||||
|
|
||||||
*ret_synchro = synchro;
|
*ret_synchro = synchro;
|
||||||
ESP_LOGD(TAG, "new sync manager at %p, with channel mask:%02"PRIx32, synchro, synchro->channel_mask);
|
ESP_LOGD(TAG, "new sync manager at %p, with channel mask:%02"PRIx32, synchro, synchro->channel_mask);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -473,7 +475,6 @@ esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, con
|
|||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(channel && encoder && payload && payload_bytes && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE(channel && encoder && payload && payload_bytes && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
ESP_RETURN_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
|
ESP_RETURN_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_TX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
|
||||||
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
|
|
||||||
#if !SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
#if !SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||||
ESP_RETURN_ON_FALSE(config->loop_count <= 0, ESP_ERR_NOT_SUPPORTED, TAG, "loop count is not supported");
|
ESP_RETURN_ON_FALSE(config->loop_count <= 0, ESP_ERR_NOT_SUPPORTED, TAG, "loop count is not supported");
|
||||||
#endif // !SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
#endif // !SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||||
@ -481,22 +482,20 @@ esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, con
|
|||||||
// payload is retrieved by the encoder, we should make sure it's still accessible even when the cache is disabled
|
// payload is retrieved by the encoder, we should make sure it's still accessible even when the cache is disabled
|
||||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
|
ESP_RETURN_ON_FALSE(esp_ptr_internal(payload), ESP_ERR_INVALID_ARG, TAG, "payload not in internal RAM");
|
||||||
#endif
|
#endif
|
||||||
rmt_group_t *group = channel->group;
|
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
if (config->flags.queue_nonblocking) {
|
||||||
int channel_id = channel->channel_id;
|
queue_wait_ticks = 0;
|
||||||
|
}
|
||||||
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
||||||
rmt_tx_trans_desc_t *t = NULL;
|
rmt_tx_trans_desc_t *t = NULL;
|
||||||
// acquire one transaction description from ready_queue or done_queue
|
// acquire one transaction description from ready queue or complete queue
|
||||||
if (tx_chan->num_trans_inflight < tx_chan->queue_size) {
|
if (xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_READY], &t, 0) != pdTRUE) {
|
||||||
ESP_RETURN_ON_FALSE(xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_READY], &t, portMAX_DELAY) == pdTRUE,
|
if (xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &t, queue_wait_ticks) == pdTRUE) {
|
||||||
ESP_FAIL, TAG, "no transaction in the ready queue");
|
|
||||||
} else {
|
|
||||||
ESP_RETURN_ON_FALSE(xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &t, portMAX_DELAY) == pdTRUE,
|
|
||||||
ESP_FAIL, TAG, "recycle transaction from done queue failed");
|
|
||||||
tx_chan->num_trans_inflight--;
|
tx_chan->num_trans_inflight--;
|
||||||
}
|
}
|
||||||
// sanity check
|
}
|
||||||
assert(t);
|
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
|
||||||
|
|
||||||
// fill in the transaction descriptor
|
// fill in the transaction descriptor
|
||||||
memset(t, 0, sizeof(rmt_tx_trans_desc_t));
|
memset(t, 0, sizeof(rmt_tx_trans_desc_t));
|
||||||
t->encoder = encoder;
|
t->encoder = encoder;
|
||||||
@ -507,7 +506,7 @@ esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, con
|
|||||||
t->flags.eot_level = config->flags.eot_level;
|
t->flags.eot_level = config->flags.eot_level;
|
||||||
|
|
||||||
// send the transaction descriptor to queue
|
// send the transaction descriptor to queue
|
||||||
if (xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &t, portMAX_DELAY) == pdTRUE) {
|
if (xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||||
tx_chan->num_trans_inflight++;
|
tx_chan->num_trans_inflight++;
|
||||||
} else {
|
} else {
|
||||||
// put the trans descriptor back to ready_queue
|
// put the trans descriptor back to ready_queue
|
||||||
@ -515,13 +514,18 @@ esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, con
|
|||||||
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
ESP_ERR_INVALID_STATE, TAG, "ready queue full");
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't know which "transmission complete" event will be triggered, but must be one of them: trans_done, loop_done
|
// check if we need to start one pending transaction
|
||||||
// when we run at here, the interrupt status bit for tx_done or loop_end should already up (ensured by `rmt_tx_enable()`)
|
rmt_fsm_t expected_fsm = RMT_FSM_ENABLE;
|
||||||
// that's why we can go into ISR as soon as we enable the interrupt bit
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT)) {
|
||||||
// in the ISR, we will fetch the transactions from trans_queue and start it
|
// check if we need to start one transaction
|
||||||
portENTER_CRITICAL(&group->spinlock);
|
if (xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id) | RMT_LL_EVENT_TX_LOOP_END(channel_id), true);
|
atomic_store(&channel->fsm, RMT_FSM_RUN);
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
rmt_tx_do_transaction(tx_chan, t);
|
||||||
|
} else {
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,6 +616,9 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
|
|||||||
rmt_hal_context_t *hal = &group->hal;
|
rmt_hal_context_t *hal = &group->hal;
|
||||||
int channel_id = channel->channel_id;
|
int channel_id = channel->channel_id;
|
||||||
|
|
||||||
|
// update current transaction
|
||||||
|
tx_chan->cur_trans = t;
|
||||||
|
|
||||||
#if SOC_RMT_SUPPORT_DMA
|
#if SOC_RMT_SUPPORT_DMA
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
gdma_reset(channel->dma_chan);
|
gdma_reset(channel->dma_chan);
|
||||||
@ -625,7 +632,7 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
|
|||||||
#endif // SOC_RMT_SUPPORT_DMA
|
#endif // SOC_RMT_SUPPORT_DMA
|
||||||
|
|
||||||
// set transaction specific parameters
|
// set transaction specific parameters
|
||||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
portENTER_CRITICAL_SAFE(&channel->spinlock);
|
||||||
rmt_ll_tx_reset_pointer(hal->regs, channel_id); // reset pointer for new transaction
|
rmt_ll_tx_reset_pointer(hal->regs, channel_id); // reset pointer for new transaction
|
||||||
rmt_ll_tx_enable_loop(hal->regs, channel_id, t->loop_count != 0);
|
rmt_ll_tx_enable_loop(hal->regs, channel_id, t->loop_count != 0);
|
||||||
#if SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
#if SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
||||||
@ -641,10 +648,10 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
|
|||||||
t->remain_loop_count -= this_loop_count;
|
t->remain_loop_count -= this_loop_count;
|
||||||
}
|
}
|
||||||
#endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
#endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
portEXIT_CRITICAL_SAFE(&channel->spinlock);
|
||||||
|
|
||||||
// enable/disable specific interrupts
|
// enable/disable specific interrupts
|
||||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
portENTER_CRITICAL_SAFE(&group->spinlock);
|
||||||
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id), t->loop_count > 0);
|
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id), t->loop_count > 0);
|
||||||
#endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
#endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||||
@ -658,7 +665,7 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
|
|||||||
}
|
}
|
||||||
// don't generate trans done event for loop transmission
|
// don't generate trans done event for loop transmission
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), t->loop_count == 0);
|
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), t->loop_count == 0);
|
||||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||||
|
|
||||||
// at the beginning of a new transaction, encoding memory offset should start from zero.
|
// 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()`
|
// It will increase in the encode function e.g. `rmt_encode_copy()`
|
||||||
@ -678,39 +685,29 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// turn on the TX machine
|
// turn on the TX machine
|
||||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
portENTER_CRITICAL_SAFE(&channel->spinlock);
|
||||||
rmt_ll_tx_fix_idle_level(hal->regs, channel_id, t->flags.eot_level, true);
|
rmt_ll_tx_fix_idle_level(hal->regs, channel_id, t->flags.eot_level, true);
|
||||||
rmt_ll_tx_start(hal->regs, channel_id);
|
rmt_ll_tx_start(hal->regs, channel_id);
|
||||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
portEXIT_CRITICAL_SAFE(&channel->spinlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t rmt_tx_enable(rmt_channel_handle_t channel)
|
static esp_err_t rmt_tx_enable(rmt_channel_handle_t channel)
|
||||||
{
|
{
|
||||||
|
// can enable the channel when it's in "init" state
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_INIT;
|
||||||
|
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_ENABLE_WAIT),
|
||||||
|
ESP_ERR_INVALID_STATE, TAG, "channel not in init state");
|
||||||
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
rmt_tx_channel_t *tx_chan = __containerof(channel, rmt_tx_channel_t, base);
|
||||||
|
|
||||||
|
// acquire power manager lock
|
||||||
|
if (channel->pm_lock) {
|
||||||
|
esp_pm_lock_acquire(channel->pm_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SOC_RMT_SUPPORT_DMA
|
||||||
rmt_group_t *group = channel->group;
|
rmt_group_t *group = channel->group;
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
rmt_hal_context_t *hal = &group->hal;
|
||||||
int channel_id = channel->channel_id;
|
int channel_id = channel->channel_id;
|
||||||
// acquire power manager lock
|
|
||||||
if (channel->pm_lock) {
|
|
||||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(channel->pm_lock), TAG, "acquire pm_lock failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
portENTER_CRITICAL(&channel->spinlock);
|
|
||||||
rmt_ll_tx_reset_pointer(hal->regs, channel_id);
|
|
||||||
rmt_ll_tx_enable_loop(hal->regs, channel_id, false);
|
|
||||||
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
|
||||||
rmt_ll_tx_reset_loop_count(hal->regs, channel_id);
|
|
||||||
rmt_ll_tx_enable_loop_count(hal->regs, channel_id, false);
|
|
||||||
#endif // SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
|
||||||
// trigger a quick trans done event by sending a EOF symbol, no signal should appear on the GPIO
|
|
||||||
tx_chan->cur_trans = NULL;
|
|
||||||
channel->hw_mem_base[0].val = 0;
|
|
||||||
rmt_ll_tx_start(hal->regs, channel_id);
|
|
||||||
portEXIT_CRITICAL(&channel->spinlock);
|
|
||||||
|
|
||||||
// wait the RMT interrupt line goes active, we won't go into the ISR handler until we enable the `RMT_LL_EVENT_TX_DONE` interrupt
|
|
||||||
while (!(rmt_ll_tx_get_interrupt_status_raw(hal->regs, channel_id) & RMT_LL_EVENT_TX_DONE(channel_id))) {}
|
|
||||||
#if SOC_RMT_SUPPORT_DMA
|
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
// enable the DMA access mode
|
// enable the DMA access mode
|
||||||
portENTER_CRITICAL(&channel->spinlock);
|
portENTER_CRITICAL(&channel->spinlock);
|
||||||
@ -721,12 +718,22 @@ static esp_err_t rmt_tx_enable(rmt_channel_handle_t channel)
|
|||||||
}
|
}
|
||||||
#endif // SOC_RMT_SUPPORT_DMA
|
#endif // SOC_RMT_SUPPORT_DMA
|
||||||
|
|
||||||
channel->fsm = RMT_FSM_ENABLE;
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
|
||||||
|
// check if we need to start one pending transaction
|
||||||
|
rmt_tx_trans_desc_t *t = NULL;
|
||||||
|
expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT)) {
|
||||||
|
if (xQueueReceive(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||||
|
// sanity check
|
||||||
|
assert(t);
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_RUN);
|
||||||
|
rmt_tx_do_transaction(tx_chan, t);
|
||||||
|
} else {
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// enable channel interrupt, dispatch transactions in ISR (in case there're transaction descriptors in the queue, then we should start them)
|
|
||||||
portENTER_CRITICAL(&group->spinlock);
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), true);
|
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,6 +744,16 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
|
|||||||
rmt_hal_context_t *hal = &group->hal;
|
rmt_hal_context_t *hal = &group->hal;
|
||||||
int channel_id = channel->channel_id;
|
int channel_id = channel->channel_id;
|
||||||
|
|
||||||
|
// can disable the channel when it's in `enable` or `run` state
|
||||||
|
bool valid_state = false;
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_INIT_WAIT)) {
|
||||||
|
valid_state = true;
|
||||||
|
}
|
||||||
|
expected_fsm = RMT_FSM_RUN;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_INIT_WAIT)) {
|
||||||
|
valid_state = true;
|
||||||
|
// disable the hardware
|
||||||
portENTER_CRITICAL(&channel->spinlock);
|
portENTER_CRITICAL(&channel->spinlock);
|
||||||
rmt_ll_tx_enable_loop(hal->regs, channel->channel_id, false);
|
rmt_ll_tx_enable_loop(hal->regs, channel->channel_id, false);
|
||||||
#if SOC_RMT_SUPPORT_TX_ASYNC_STOP
|
#if SOC_RMT_SUPPORT_TX_ASYNC_STOP
|
||||||
@ -754,7 +771,10 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
|
|||||||
#endif
|
#endif
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_MASK(channel_id));
|
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_MASK(channel_id));
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
portEXIT_CRITICAL(&group->spinlock);
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_FALSE(valid_state, ESP_ERR_INVALID_STATE, TAG, "channel can't be disabled in state %d", expected_fsm);
|
||||||
|
|
||||||
|
// disable the DMA
|
||||||
#if SOC_RMT_SUPPORT_DMA
|
#if SOC_RMT_SUPPORT_DMA
|
||||||
if (channel->dma_chan) {
|
if (channel->dma_chan) {
|
||||||
gdma_stop(channel->dma_chan);
|
gdma_stop(channel->dma_chan);
|
||||||
@ -766,9 +786,10 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
|
|||||||
portEXIT_CRITICAL(&channel->spinlock);
|
portEXIT_CRITICAL(&channel->spinlock);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// recycle the interrupted transaction
|
// recycle the interrupted transaction
|
||||||
if (tx_chan->cur_trans) {
|
if (tx_chan->cur_trans) {
|
||||||
xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &tx_chan->cur_trans, portMAX_DELAY);
|
xQueueSend(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &tx_chan->cur_trans, 0);
|
||||||
// reset corresponding encoder
|
// reset corresponding encoder
|
||||||
rmt_encoder_reset(tx_chan->cur_trans->encoder);
|
rmt_encoder_reset(tx_chan->cur_trans->encoder);
|
||||||
}
|
}
|
||||||
@ -779,7 +800,9 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
|
|||||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(channel->pm_lock), TAG, "release pm_lock failed");
|
ESP_RETURN_ON_ERROR(esp_pm_lock_release(channel->pm_lock), TAG, "release pm_lock failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
channel->fsm = RMT_FSM_INIT;
|
// finally we switch to the INIT state
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_INIT);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,12 +846,7 @@ static esp_err_t rmt_tx_modulate_carrier(rmt_channel_handle_t channel, const rmt
|
|||||||
|
|
||||||
static bool IRAM_ATTR rmt_isr_handle_tx_threshold(rmt_tx_channel_t *tx_chan)
|
static bool IRAM_ATTR rmt_isr_handle_tx_threshold(rmt_tx_channel_t *tx_chan)
|
||||||
{
|
{
|
||||||
rmt_channel_t *channel = &tx_chan->base;
|
// continue ping-pong transmission
|
||||||
rmt_group_t *group = channel->group;
|
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
|
||||||
uint32_t channel_id = channel->channel_id;
|
|
||||||
|
|
||||||
// continue pingpong transmission
|
|
||||||
rmt_tx_trans_desc_t *t = tx_chan->cur_trans;
|
rmt_tx_trans_desc_t *t = tx_chan->cur_trans;
|
||||||
size_t encoded_symbols = t->transmitted_symbol_num;
|
size_t encoded_symbols = t->transmitted_symbol_num;
|
||||||
// encoding finished, only need to send the EOF symbol
|
// encoding finished, only need to send the EOF symbol
|
||||||
@ -841,74 +859,55 @@ static bool IRAM_ATTR rmt_isr_handle_tx_threshold(rmt_tx_channel_t *tx_chan)
|
|||||||
t->transmitted_symbol_num = encoded_symbols;
|
t->transmitted_symbol_num = encoded_symbols;
|
||||||
tx_chan->mem_end = tx_chan->ping_pong_symbols * 3 - tx_chan->mem_end; // mem_end equals to either ping_pong_symbols or ping_pong_symbols*2
|
tx_chan->mem_end = tx_chan->ping_pong_symbols * 3 - tx_chan->mem_end; // mem_end equals to either ping_pong_symbols or ping_pong_symbols*2
|
||||||
|
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_THRES(channel_id));
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IRAM_ATTR rmt_isr_handle_tx_done(rmt_tx_channel_t *tx_chan)
|
static bool IRAM_ATTR rmt_isr_handle_tx_done(rmt_tx_channel_t *tx_chan)
|
||||||
{
|
{
|
||||||
rmt_channel_t *channel = &tx_chan->base;
|
rmt_channel_t *channel = &tx_chan->base;
|
||||||
rmt_group_t *group = channel->group;
|
|
||||||
rmt_hal_context_t *hal = &group->hal;
|
|
||||||
uint32_t channel_id = channel->channel_id;
|
|
||||||
BaseType_t awoken = pdFALSE;
|
BaseType_t awoken = pdFALSE;
|
||||||
rmt_tx_trans_desc_t *trans_desc = NULL;
|
rmt_tx_trans_desc_t *trans_desc = NULL;
|
||||||
bool need_yield = false;
|
bool need_yield = false;
|
||||||
|
|
||||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
rmt_fsm_t expected_fsm = RMT_FSM_RUN;
|
||||||
// disable interrupt temporarily, re-enable it when there is transaction unhandled in the queue
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_ENABLE_WAIT)) {
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), false);
|
|
||||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
|
||||||
|
|
||||||
trans_desc = tx_chan->cur_trans;
|
trans_desc = tx_chan->cur_trans;
|
||||||
// process finished transaction
|
// move current finished transaction to the complete queue
|
||||||
if (trans_desc) {
|
|
||||||
// don't care of the tx done event for any undergoing loop transaction
|
|
||||||
// mostly it's triggered when a loop transmission is undergoing and user calls `rmt_transmit()` where tx done interrupt is generated by accident
|
|
||||||
if (trans_desc->loop_count != 0) {
|
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id));
|
|
||||||
return need_yield;
|
|
||||||
}
|
|
||||||
if (tx_chan->on_trans_done) {
|
|
||||||
rmt_tx_done_event_data_t edata = {
|
|
||||||
.num_symbols = trans_desc->transmitted_symbol_num,
|
|
||||||
};
|
|
||||||
if (tx_chan->on_trans_done(channel, &edata, tx_chan->user_data)) {
|
|
||||||
need_yield = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// move transaction to done_queue
|
|
||||||
xQueueSendFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &trans_desc, &awoken);
|
xQueueSendFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &trans_desc, &awoken);
|
||||||
if (awoken == pdTRUE) {
|
if (awoken == pdTRUE) {
|
||||||
need_yield = true;
|
need_yield = true;
|
||||||
}
|
}
|
||||||
|
tx_chan->cur_trans = NULL;
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
|
||||||
|
// invoke callback
|
||||||
|
rmt_tx_done_callback_t done_cb = tx_chan->on_trans_done;
|
||||||
|
if (done_cb) {
|
||||||
|
rmt_tx_done_event_data_t edata = {
|
||||||
|
.num_symbols = trans_desc->transmitted_symbol_num,
|
||||||
|
};
|
||||||
|
if (done_cb(channel, &edata, tx_chan->user_data)) {
|
||||||
|
need_yield = true;
|
||||||
}
|
}
|
||||||
// fetch new transaction description from trans_queue
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's try start the next pending transaction
|
||||||
|
expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT)) {
|
||||||
if (xQueueReceiveFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &trans_desc, &awoken) == pdTRUE) {
|
if (xQueueReceiveFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &trans_desc, &awoken) == pdTRUE) {
|
||||||
// sanity check
|
// sanity check
|
||||||
assert(trans_desc);
|
assert(trans_desc);
|
||||||
// update current transaction
|
atomic_store(&channel->fsm, RMT_FSM_RUN);
|
||||||
tx_chan->cur_trans = trans_desc;
|
|
||||||
|
|
||||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
|
||||||
// only clear the trans done status when we're sure there still remains transaction to handle
|
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id));
|
|
||||||
// enable interrupt again, because the new transaction can trigger another trans done event
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), trans_desc->loop_count == 0);
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id), trans_desc->loop_count > 0);
|
|
||||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
|
||||||
|
|
||||||
// begin a new transaction
|
// begin a new transaction
|
||||||
rmt_tx_do_transaction(tx_chan, trans_desc);
|
rmt_tx_do_transaction(tx_chan, trans_desc);
|
||||||
} else { // No transactions left in the queue
|
|
||||||
// don't clear interrupt status, so when next time user push new transaction to the queue and call esp_intr_enable,
|
|
||||||
// we can go to this ISR handler again
|
|
||||||
tx_chan->cur_trans = NULL;
|
|
||||||
}
|
|
||||||
if (awoken == pdTRUE) {
|
if (awoken == pdTRUE) {
|
||||||
need_yield = true;
|
need_yield = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return need_yield;
|
return need_yield;
|
||||||
}
|
}
|
||||||
@ -928,16 +927,16 @@ static bool IRAM_ATTR rmt_isr_handle_tx_loop_end(rmt_tx_channel_t *tx_chan)
|
|||||||
if (trans_desc) {
|
if (trans_desc) {
|
||||||
#if !SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
#if !SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
||||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||||
// This is a workaround for chips that don't support auto stop
|
// This is a workaround for chips that don't support loop auto stop
|
||||||
// Although we stop the transaction immediately in ISR handler, it's still possible that some rmt symbols have sneaked out
|
// Although we stop the transaction immediately in ISR handler, it's still possible that some rmt symbols have sneaked out
|
||||||
rmt_ll_tx_stop(hal->regs, channel_id);
|
rmt_ll_tx_stop(hal->regs, channel_id);
|
||||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||||
#endif // SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
#endif // SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
|
||||||
|
|
||||||
// continue unfinished loop transaction
|
// continue unfinished loop transaction
|
||||||
if (trans_desc->remain_loop_count) {
|
if (trans_desc->remain_loop_count) {
|
||||||
uint32_t this_loop_count = MIN(trans_desc->remain_loop_count, RMT_LL_MAX_LOOP_COUNT_PER_BATCH);
|
uint32_t this_loop_count = MIN(trans_desc->remain_loop_count, RMT_LL_MAX_LOOP_COUNT_PER_BATCH);
|
||||||
trans_desc->remain_loop_count -= this_loop_count;
|
trans_desc->remain_loop_count -= this_loop_count;
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id));
|
|
||||||
portENTER_CRITICAL_ISR(&channel->spinlock);
|
portENTER_CRITICAL_ISR(&channel->spinlock);
|
||||||
rmt_ll_tx_set_loop_count(hal->regs, channel_id, this_loop_count);
|
rmt_ll_tx_set_loop_count(hal->regs, channel_id, this_loop_count);
|
||||||
rmt_ll_tx_reset_pointer(hal->regs, channel_id);
|
rmt_ll_tx_reset_pointer(hal->regs, channel_id);
|
||||||
@ -945,51 +944,49 @@ static bool IRAM_ATTR rmt_isr_handle_tx_loop_end(rmt_tx_channel_t *tx_chan)
|
|||||||
rmt_ll_tx_start(hal->regs, channel_id);
|
rmt_ll_tx_start(hal->regs, channel_id);
|
||||||
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
portEXIT_CRITICAL_ISR(&channel->spinlock);
|
||||||
return need_yield;
|
return need_yield;
|
||||||
} else {
|
|
||||||
if (tx_chan->on_trans_done) {
|
|
||||||
rmt_tx_done_event_data_t edata = {
|
|
||||||
.num_symbols = trans_desc->transmitted_symbol_num,
|
|
||||||
};
|
|
||||||
if (tx_chan->on_trans_done(channel, &edata, tx_chan->user_data)) {
|
|
||||||
need_yield = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// move transaction to done_queue
|
// loop transaction finished
|
||||||
|
rmt_fsm_t expected_fsm = RMT_FSM_RUN;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_ENABLE_WAIT)) {
|
||||||
|
// move current finished transaction to the complete queue
|
||||||
xQueueSendFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &trans_desc, &awoken);
|
xQueueSendFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_COMPLETE], &trans_desc, &awoken);
|
||||||
if (awoken == pdTRUE) {
|
if (awoken == pdTRUE) {
|
||||||
need_yield = true;
|
need_yield = true;
|
||||||
}
|
}
|
||||||
}
|
tx_chan->cur_trans = NULL;
|
||||||
}
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
// trans_done and loop_done should be considered as one "transmission complete"
|
|
||||||
// but sometimes the trans done event might also be triggered together with loop done event, by accident, so clear it first
|
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id));
|
|
||||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
|
||||||
// disable interrupt temporarily, re-enable it when there is transaction unhandled in the queue
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id), false);
|
|
||||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
|
||||||
|
|
||||||
// fetch new transaction description from trans_queue
|
// invoke callback
|
||||||
|
rmt_tx_done_callback_t done_cb = tx_chan->on_trans_done;
|
||||||
|
if (done_cb) {
|
||||||
|
rmt_tx_done_event_data_t edata = {
|
||||||
|
.num_symbols = trans_desc->transmitted_symbol_num,
|
||||||
|
};
|
||||||
|
if (done_cb(channel, &edata, tx_chan->user_data)) {
|
||||||
|
need_yield = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's try start the next pending transaction
|
||||||
|
expected_fsm = RMT_FSM_ENABLE;
|
||||||
|
if (atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT)) {
|
||||||
if (xQueueReceiveFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &trans_desc, &awoken) == pdTRUE) {
|
if (xQueueReceiveFromISR(tx_chan->trans_queues[RMT_TX_QUEUE_PROGRESS], &trans_desc, &awoken) == pdTRUE) {
|
||||||
// sanity check
|
// sanity check
|
||||||
assert(trans_desc);
|
assert(trans_desc);
|
||||||
tx_chan->cur_trans = trans_desc;
|
atomic_store(&channel->fsm, RMT_FSM_RUN);
|
||||||
// clear the loop end status when we're sure there still remains transaction to handle
|
|
||||||
rmt_ll_clear_interrupt_status(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id));
|
|
||||||
|
|
||||||
portENTER_CRITICAL_ISR(&group->spinlock);
|
|
||||||
// enable interrupt again, because the new transaction can trigger new trans done event
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_DONE(channel_id), trans_desc->loop_count == 0);
|
|
||||||
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_LOOP_END(channel_id), trans_desc->loop_count > 0);
|
|
||||||
portEXIT_CRITICAL_ISR(&group->spinlock);
|
|
||||||
|
|
||||||
// begin a new transaction
|
// begin a new transaction
|
||||||
rmt_tx_do_transaction(tx_chan, trans_desc);
|
rmt_tx_do_transaction(tx_chan, trans_desc);
|
||||||
} else { // No transactions left in the queue
|
if (awoken == pdTRUE) {
|
||||||
// don't clear interrupt status, so when next time user push new transaction to the queue and call esp_intr_enable,
|
need_yield = true;
|
||||||
// we can go into ISR handler again
|
|
||||||
tx_chan->cur_trans = NULL;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (awoken == pdTRUE) {
|
if (awoken == pdTRUE) {
|
||||||
need_yield = true;
|
need_yield = true;
|
||||||
}
|
}
|
||||||
@ -1007,6 +1004,7 @@ static void IRAM_ATTR rmt_tx_default_isr(void *args)
|
|||||||
bool need_yield = false;
|
bool need_yield = false;
|
||||||
|
|
||||||
uint32_t status = rmt_ll_tx_get_interrupt_status(hal->regs, channel_id);
|
uint32_t status = rmt_ll_tx_get_interrupt_status(hal->regs, channel_id);
|
||||||
|
rmt_ll_clear_interrupt_status(hal->regs, status);
|
||||||
|
|
||||||
// Tx threshold interrupt
|
// Tx threshold interrupt
|
||||||
if (status & RMT_LL_EVENT_TX_THRES(channel_id)) {
|
if (status & RMT_LL_EVENT_TX_THRES(channel_id)) {
|
||||||
|
@ -90,22 +90,27 @@ TEST_CASE("rmt_tx_iram_safe_with_dma", "[rmt]")
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define TEST_RMT_SYMBOLS 2
|
||||||
|
|
||||||
static void IRAM_ATTR test_simulate_input_post_cache_disable(void *args)
|
static void IRAM_ATTR test_simulate_input_post_cache_disable(void *args)
|
||||||
{
|
{
|
||||||
int gpio_num = (int)args;
|
int gpio_num = (int)args;
|
||||||
// simulate input signal, should only be recognized as one RMT symbol
|
// simulate input signal, should only be recognized as two RMT symbols
|
||||||
|
for (int i = 0; i < TEST_RMT_SYMBOLS; i++) {
|
||||||
gpio_set_level(gpio_num, 0);
|
gpio_set_level(gpio_num, 0);
|
||||||
esp_rom_delay_us(50);
|
esp_rom_delay_us(50);
|
||||||
gpio_set_level(gpio_num, 1);
|
gpio_set_level(gpio_num, 1);
|
||||||
esp_rom_delay_us(50);
|
esp_rom_delay_us(50);
|
||||||
gpio_set_level(gpio_num, 0);
|
gpio_set_level(gpio_num, 0);
|
||||||
esp_rom_delay_us(20000);
|
esp_rom_delay_us(20000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TaskHandle_t task_to_notify;
|
TaskHandle_t task_to_notify;
|
||||||
size_t received_symbol_num;
|
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_nec_rx_user_data_t;
|
||||||
|
|
||||||
IRAM_ATTR
|
IRAM_ATTR
|
||||||
@ -113,8 +118,15 @@ static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx
|
|||||||
{
|
{
|
||||||
BaseType_t high_task_wakeup = pdFALSE;
|
BaseType_t high_task_wakeup = pdFALSE;
|
||||||
test_nec_rx_user_data_t *test_user_data = (test_nec_rx_user_data_t *)user_data;
|
test_nec_rx_user_data_t *test_user_data = (test_nec_rx_user_data_t *)user_data;
|
||||||
test_user_data->received_symbol_num = edata->num_symbols;
|
test_user_data->received_symbol_num += edata->num_symbols;
|
||||||
|
// should receive one RMT symbol at a time
|
||||||
|
if (edata->num_symbols == 1) {
|
||||||
|
if (test_user_data->received_symbol_num == TEST_RMT_SYMBOLS) {
|
||||||
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
|
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
|
||||||
|
} else {
|
||||||
|
rmt_receive(channel, test_user_data->remote_codes, sizeof(test_user_data->remote_codes), &test_user_data->rx_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
return high_task_wakeup == pdTRUE;
|
return high_task_wakeup == pdTRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,27 +153,25 @@ static void test_rmt_rx_iram_safe(size_t mem_block_symbols, bool with_dma, rmt_c
|
|||||||
};
|
};
|
||||||
test_nec_rx_user_data_t test_user_data = {
|
test_nec_rx_user_data_t test_user_data = {
|
||||||
.task_to_notify = xTaskGetCurrentTaskHandle(),
|
.task_to_notify = xTaskGetCurrentTaskHandle(),
|
||||||
|
.received_symbol_num = 0,
|
||||||
|
.rx_config = {
|
||||||
|
.signal_range_min_ns = 1250,
|
||||||
|
.signal_range_max_ns = 12000000,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, &test_user_data));
|
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, &test_user_data));
|
||||||
|
|
||||||
printf("enable rx channel\r\n");
|
printf("enable rx channel\r\n");
|
||||||
TEST_ESP_OK(rmt_enable(rx_channel));
|
TEST_ESP_OK(rmt_enable(rx_channel));
|
||||||
|
|
||||||
rmt_symbol_word_t remote_codes[128];
|
|
||||||
|
|
||||||
rmt_receive_config_t receive_config = {
|
|
||||||
.signal_range_min_ns = 1250,
|
|
||||||
.signal_range_max_ns = 12000000,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ready to receive
|
// ready to receive
|
||||||
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config));
|
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
|
// 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, 0);
|
||||||
|
|
||||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
|
||||||
TEST_ASSERT_EQUAL(1, test_user_data.received_symbol_num);
|
TEST_ASSERT_EQUAL(TEST_RMT_SYMBOLS, test_user_data.received_symbol_num);
|
||||||
|
|
||||||
printf("disable rx channels\r\n");
|
printf("disable rx channels\r\n");
|
||||||
TEST_ESP_OK(rmt_disable(rx_channel));
|
TEST_ESP_OK(rmt_disable(rx_channel));
|
||||||
|
@ -381,7 +381,7 @@ TEST_CASE("rmt_infinite_loop_trans", "[rmt]")
|
|||||||
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
|
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
|
||||||
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
||||||
.gpio_num = 2,
|
.gpio_num = 2,
|
||||||
.trans_queue_depth = 3,
|
.trans_queue_depth = 5,
|
||||||
.intr_priority = 1
|
.intr_priority = 1
|
||||||
};
|
};
|
||||||
printf("install tx channel\r\n");
|
printf("install tx channel\r\n");
|
||||||
@ -398,6 +398,7 @@ TEST_CASE("rmt_infinite_loop_trans", "[rmt]")
|
|||||||
|
|
||||||
rmt_transmit_config_t transmit_config = {
|
rmt_transmit_config_t transmit_config = {
|
||||||
.loop_count = -1, // infinite loop transmission
|
.loop_count = -1, // infinite loop transmission
|
||||||
|
.flags.queue_nonblocking = true, // return immediately if the queue is full
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("infinite loop transmission: keep spinning stepper motor\r\n");
|
printf("infinite loop transmission: keep spinning stepper motor\r\n");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||||
CONFIG_RMT_ISR_IRAM_SAFE=y
|
CONFIG_RMT_ISR_IRAM_SAFE=y
|
||||||
|
CONFIG_RMT_RECV_FUNC_IN_IRAM=y
|
||||||
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||||
# place non-ISR FreeRTOS functions in Flash
|
# place non-ISR FreeRTOS functions in Flash
|
||||||
|
@ -368,6 +368,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf0, idle_thres, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf0, idle_thres, thres);
|
||||||
@ -393,6 +394,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->conf_ch[channel].conf1.rx_filter_en = enable;
|
dev->conf_ch[channel].conf1.rx_filter_en = enable;
|
||||||
@ -405,6 +407,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf1, rx_filter_thres, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf1, rx_filter_thres, thres);
|
||||||
|
@ -438,6 +438,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
|
|||||||
* @param dev Peripheral instance address
|
* @param dev Peripheral instance address
|
||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
||||||
{
|
{
|
||||||
dev->rx_conf[channel].conf1.mem_wr_rst = 1;
|
dev->rx_conf[channel].conf1.mem_wr_rst = 1;
|
||||||
@ -480,6 +481,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
dev->rx_conf[channel].conf0.idle_thres = thres;
|
dev->rx_conf[channel].conf0.idle_thres = thres;
|
||||||
@ -505,6 +507,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->rx_conf[channel].conf1.rx_filter_en = enable;
|
dev->rx_conf[channel].conf1.rx_filter_en = enable;
|
||||||
@ -517,6 +520,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_conf[channel].conf1, rx_filter_thres, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_conf[channel].conf1, rx_filter_thres, thres);
|
||||||
|
@ -453,6 +453,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
|
|||||||
* @param dev Peripheral instance address
|
* @param dev Peripheral instance address
|
||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
||||||
@ -495,6 +496,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
||||||
@ -520,6 +522,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
||||||
@ -532,6 +535,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
||||||
|
@ -450,6 +450,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
|
|||||||
* @param dev Peripheral instance address
|
* @param dev Peripheral instance address
|
||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
||||||
@ -492,6 +493,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
||||||
@ -517,6 +519,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
||||||
@ -529,6 +532,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
||||||
|
@ -473,6 +473,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf0, idle_thres_chn, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf0, idle_thres_chn, thres);
|
||||||
@ -498,6 +499,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->conf_ch[channel].conf1.rx_filter_en_chn = enable;
|
dev->conf_ch[channel].conf1.rx_filter_en_chn = enable;
|
||||||
@ -510,6 +512,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf1, rx_filter_thres_chn, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf1, rx_filter_thres_chn, thres);
|
||||||
|
@ -463,6 +463,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
|
|||||||
* @param dev Peripheral instance address
|
* @param dev Peripheral instance address
|
||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
|
||||||
@ -518,6 +519,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Time length threshold
|
* @param thres Time length threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
dev->chmconf[channel].conf0.idle_thres_chm = thres;
|
||||||
@ -543,6 +545,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
|
|||||||
* @param channel RMT RX chanenl number
|
* @param channel RMT RX chanenl number
|
||||||
* @param enable True to enable, False to disable
|
* @param enable True to enable, False to disable
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
|
||||||
{
|
{
|
||||||
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
dev->chmconf[channel].conf1.rx_filter_en_chm = enable;
|
||||||
@ -555,6 +558,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
|
|||||||
* @param channel RMT RX channel number
|
* @param channel RMT RX channel number
|
||||||
* @param thres Filter threshold
|
* @param thres Filter threshold
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
static inline void rmt_ll_rx_set_filter_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
|
||||||
{
|
{
|
||||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf1, rx_filter_thres_chm, thres);
|
||||||
|
@ -56,7 +56,7 @@ The description of the RMT functionality is divided into the following sections:
|
|||||||
|
|
||||||
- :ref:`rmt-resource-allocation` - covers how to allocate and properly configure RMT channels. It also covers how to recycle channels and other resources when they are no longer used.
|
- :ref:`rmt-resource-allocation` - covers how to allocate and properly configure RMT channels. It also covers how to recycle channels and other resources when they are no longer used.
|
||||||
- :ref:`rmt-carrier-modulation-and demodulation` - describes how to modulate and demodulate the carrier signals for TX and RX channels respectively.
|
- :ref:`rmt-carrier-modulation-and demodulation` - describes how to modulate and demodulate the carrier signals for TX and RX channels respectively.
|
||||||
- :ref:`rmt-register-event-callbacks` - covers how to register user-provided event callbacks in order to receive RMT channel events.
|
- :ref:`rmt-register-event-callbacks` - covers how to register user-provided event callbacks to receive RMT channel events.
|
||||||
- :ref:`rmt-enable-and-disable-channel` - shows how to enable and disable the RMT channel.
|
- :ref:`rmt-enable-and-disable-channel` - shows how to enable and disable the RMT channel.
|
||||||
- :ref:`rmt-initiate-tx-transaction` - describes the steps to initiate a transaction for a TX channel.
|
- :ref:`rmt-initiate-tx-transaction` - describes the steps to initiate a transaction for a TX channel.
|
||||||
- :ref:`rmt-initiate-rx-transaction` - describes the steps to initiate a transaction for an RX channel.
|
- :ref:`rmt-initiate-rx-transaction` - describes the steps to initiate a transaction for an RX channel.
|
||||||
@ -84,7 +84,7 @@ To install an RMT TX channel, there is a configuration structure that needs to b
|
|||||||
- :cpp:member:`rmt_tx_channel_config_t::resolution_hz` sets the resolution of the internal tick counter. The timing parameter of the RMT signal is calculated based on this **tick**.
|
- :cpp:member:`rmt_tx_channel_config_t::resolution_hz` sets the resolution of the internal tick counter. The timing parameter of the RMT signal is calculated based on this **tick**.
|
||||||
- :cpp:member:`rmt_tx_channel_config_t::mem_block_symbols` has a slightly different meaning based on if the DMA backend is enabled or not.
|
- :cpp:member:`rmt_tx_channel_config_t::mem_block_symbols` has a slightly different meaning based on if the DMA backend is enabled or not.
|
||||||
|
|
||||||
- If the DMA is enabled via :cpp:member:`rmt_tx_channel_config_t::with_dma`, then this field controls the size of the internal DMA buffer. To achieve a better throughput and smaller CPU overhead, we recommend you set a large value, e.g., ``1024``.
|
- If the DMA is enabled via :cpp:member:`rmt_tx_channel_config_t::with_dma`, then this field controls the size of the internal DMA buffer. To achieve a better throughput and smaller CPU overhead, you can set a larger value, e.g., ``1024``.
|
||||||
- If DMA is not used, this field controls the size of the dedicated memory block owned by the channel, which should be at least {IDF_TARGET_SOC_RMT_MEM_WORDS_PER_CHANNEL}.
|
- If DMA is not used, this field controls the size of the dedicated memory block owned by the channel, which should be at least {IDF_TARGET_SOC_RMT_MEM_WORDS_PER_CHANNEL}.
|
||||||
|
|
||||||
- :cpp:member:`rmt_tx_channel_config_t::trans_queue_depth` sets the depth of the internal transaction queue, the deeper the queue, the more transactions can be prepared in the backlog.
|
- :cpp:member:`rmt_tx_channel_config_t::trans_queue_depth` sets the depth of the internal transaction queue, the deeper the queue, the more transactions can be prepared in the backlog.
|
||||||
@ -205,7 +205,7 @@ The RX channel-supported event callbacks are listed in the :cpp:type:`rmt_rx_eve
|
|||||||
|
|
||||||
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.
|
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 for the duration of 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.
|
||||||
|
|
||||||
The TX-done event data is defined in :cpp:type:`rmt_tx_done_event_data_t`:
|
The TX-done event data is defined in :cpp:type:`rmt_tx_done_event_data_t`:
|
||||||
|
|
||||||
@ -237,7 +237,7 @@ Initiate TX Transaction
|
|||||||
|
|
||||||
RMT is a special communication peripheral, as it is unable to transmit raw byte streams like SPI and I2C. RMT can only send data in its own format :cpp:type:`rmt_symbol_word_t`. However, the hardware does not help to convert the user data into RMT symbols, this can only be done in software by the so-called **RMT Encoder**. The encoder is responsible for encoding user data into RMT symbols and then writing to the RMT memory block or the DMA buffer. For how to create an RMT encoder, please refer to :ref:`rmt-rmt-encoder`.
|
RMT is a special communication peripheral, as it is unable to transmit raw byte streams like SPI and I2C. RMT can only send data in its own format :cpp:type:`rmt_symbol_word_t`. However, the hardware does not help to convert the user data into RMT symbols, this can only be done in software by the so-called **RMT Encoder**. The encoder is responsible for encoding user data into RMT symbols and then writing to the RMT memory block or the DMA buffer. For how to create an RMT encoder, please refer to :ref:`rmt-rmt-encoder`.
|
||||||
|
|
||||||
Once we got an encoder, we can initiate a TX transaction by calling :cpp:func:`rmt_transmit`. This function takes several positional parameters like channel handle, encoder handle, and payload buffer. Besides, we also need to provide a transmission-specific configuration in :cpp:type:`rmt_transmit_config_t`:
|
Once you created an encoder, you can initiate a TX transaction by calling :cpp:func:`rmt_transmit`. This function takes several positional parameters like channel handle, encoder handle, and payload buffer. Besides, you also need to provide a transmission-specific configuration in :cpp:type:`rmt_transmit_config_t`:
|
||||||
|
|
||||||
- :cpp:member:`rmt_transmit_config_t::loop_count` sets the number of transmission loops. After the transmitter has finished one round of transmission, it can restart the same transmission again if this value is not set to zero. As the loop is controlled by hardware, the RMT channel can be used to generate many periodic sequences with minimal CPU intervention.
|
- :cpp:member:`rmt_transmit_config_t::loop_count` sets the number of transmission loops. After the transmitter has finished one round of transmission, it can restart the same transmission again if this value is not set to zero. As the loop is controlled by hardware, the RMT channel can be used to generate many periodic sequences with minimal CPU intervention.
|
||||||
|
|
||||||
@ -249,6 +249,7 @@ Once we got an encoder, we can initiate a TX transaction by calling :cpp:func:`r
|
|||||||
The **loop transmit** feature is not supported on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you configure this option, or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
|
The **loop transmit** feature is not supported on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you configure this option, or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
|
||||||
|
|
||||||
- :cpp:member:`rmt_transmit_config_t::eot_level` sets the output level when the transmitter finishes working or stops working by calling :cpp:func:`rmt_disable`.
|
- :cpp:member:`rmt_transmit_config_t::eot_level` sets the output level when the transmitter finishes working or stops working by calling :cpp:func:`rmt_disable`.
|
||||||
|
- :cpp:member:`rmt_transmit_config_t::queue_nonblocking` sets whether to wait for a free slot in the transaction queue when it is full. If this value is set to ``true``, then the function will return with an error code :c:macro:`ESP_ERR_INVALID_STATE` when the queue is full. Otherwise, the function will block until a free slot is available in the queue.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -264,7 +265,7 @@ Internally, :cpp:func:`rmt_transmit` constructs a transaction descriptor and sen
|
|||||||
Multiple Channels Simultaneous Transmission
|
Multiple Channels Simultaneous Transmission
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
In some real-time control applications (e.g., to make two robotic arms move simultaneously), we do not want any time drift in between when startup multiple TX channels. The RMT driver can help to manage this by creating a so-called **Sync Manager**. The sync manager is represented by :cpp:type:`rmt_sync_manager_handle_t` in the driver. The procedure of RMT sync transmission is shown as follows:
|
In some real-time control applications (e.g., to make two robotic arms move simultaneously), you do not want any time drift between different channels. The RMT driver can help to manage this by creating a so-called **Sync Manager**. The sync manager is represented by :cpp:type:`rmt_sync_manager_handle_t` in the driver. The procedure of RMT sync transmission is shown as follows:
|
||||||
|
|
||||||
.. figure:: /../_static/rmt_tx_sync.png
|
.. figure:: /../_static/rmt_tx_sync.png
|
||||||
:align: center
|
:align: center
|
||||||
@ -380,16 +381,16 @@ RMT Encoder
|
|||||||
|
|
||||||
An RMT encoder is part of the RMT TX transaction, whose responsibility is to generate and write the correct RMT symbols into hardware memory or DMA buffer at a specific time. There are some special restrictions for an encoding function:
|
An RMT encoder is part of the RMT TX transaction, whose responsibility is to generate and write the correct RMT symbols into hardware memory or DMA buffer at a specific time. There are some special restrictions for an encoding function:
|
||||||
|
|
||||||
- During a single transaction, the encoding function may be called multiple times. This is necessary because the target RMT memory block cannot hold all the artifacts at once. To overcome this limitation, we utilize a **ping-pong** approach, where the encoding session is divided into multiple parts. This means that the encoder needs to **keep track of its state** in order to continue encoding from where it left off in the previous part.
|
- During a single transaction, the encoding function may be called multiple times. This is necessary because the target RMT memory block cannot hold all the artifacts at once. To overcome this limitation, the driver utilizes a **ping-pong** approach, where the encoding session is divided into multiple parts. This means that the encoder needs to **keep track of its state** to continue encoding from where it left off in the previous part.
|
||||||
- The encoding function is running in the ISR context. To speed up the encoding session, it is highly recommended to put the encoding function into IRAM. This can also avoid the cache miss during encoding.
|
- The encoding function is running in the ISR context. To speed up the encoding session, it is highly recommended to put the encoding function into IRAM. This can also avoid the cache miss during encoding.
|
||||||
|
|
||||||
To help get started with the RMT driver faster, some commonly-used encoders are provided out-of-the-box. They can either work alone or be chained together into a new encoder. See also `Composite Pattern <https://en.wikipedia.org/wiki/Composite_pattern>`__ for the principle behind it. The driver has defined the encoder interface in :cpp:type:`rmt_encoder_t`, it contains the following functions:
|
To help get started with the RMT driver faster, some commonly used encoders are provided out-of-the-box. They can either work alone or be chained together into a new encoder. See also `Composite Pattern <https://en.wikipedia.org/wiki/Composite_pattern>`__ for the principle behind it. The driver has defined the encoder interface in :cpp:type:`rmt_encoder_t`, it contains the following functions:
|
||||||
|
|
||||||
- :cpp:member:`rmt_encoder_t::encode` is the fundamental function of an encoder. This is where the encoding session happens.
|
- :cpp:member:`rmt_encoder_t::encode` is the fundamental function of an encoder. This is where the encoding session happens.
|
||||||
|
|
||||||
- The function might be called multiple times within a single transaction. The encode function should return the state of the current encoding session.
|
- The function might be called multiple times within a single transaction. The encode function should return the state of the current encoding session.
|
||||||
- The supported states are listed in the :cpp:type:`rmt_encode_state_t`. If the result contains :cpp:enumerator:`RMT_ENCODING_COMPLETE`, it means the current encoder has finished work.
|
- The supported states are listed in the :cpp:type:`rmt_encode_state_t`. If the result contains :cpp:enumerator:`RMT_ENCODING_COMPLETE`, it means the current encoder has finished work.
|
||||||
- If the result contains :cpp:enumerator:`RMT_ENCODING_MEM_FULL`, we need to yield from the current session, as there is no space to save more encoding artifacts.
|
- If the result contains :cpp:enumerator:`RMT_ENCODING_MEM_FULL`, the program needs to yield from the current session, as there is no space to save more encoding artifacts.
|
||||||
|
|
||||||
- :cpp:member:`rmt_encoder_t::reset` should reset the encoder state back to the initial state (the RMT encoder is stateful).
|
- :cpp:member:`rmt_encoder_t::reset` should reset the encoder state back to the initial state (the RMT encoder is stateful).
|
||||||
|
|
||||||
@ -424,7 +425,7 @@ Besides the primitive encoders provided by the driver, the user can implement hi
|
|||||||
Customize RMT Encoder for NEC Protocol
|
Customize RMT Encoder for NEC Protocol
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
In this section, we demonstrates how to write an NEC encoder. The NEC IR protocol uses pulse distance encoding of the message bits. Each pulse burst is ``562.5 µs`` in length, logical bits are transmitted as follows. It is worth mentioning that the least significant bit of each byte is sent first.
|
This section demonstrates how to write an NEC encoder. The NEC IR protocol uses pulse distance encoding of the message bits. Each pulse burst is ``562.5 µs`` in length, logical bits are transmitted as follows. It is worth mentioning that the least significant bit of each byte is sent first.
|
||||||
|
|
||||||
- Logical ``0``: a ``562.5 µs`` pulse burst followed by a ``562.5 µs`` space, with a total transmit time of ``1.125 ms``
|
- Logical ``0``: a ``562.5 µs`` pulse burst followed by a ``562.5 µs`` space, with a total transmit time of ``1.125 ms``
|
||||||
- Logical ``1``: a ``562.5 µs`` pulse burst followed by a ``1.6875 ms`` space, with a total transmit time of ``2.25 ms``
|
- Logical ``1``: a ``562.5 µs`` pulse burst followed by a ``1.6875 ms`` space, with a total transmit time of ``2.25 ms``
|
||||||
@ -445,7 +446,7 @@ When a key is pressed on the remote controller, the transmitted message includes
|
|||||||
- 8-bit logical inverse of the command
|
- 8-bit logical inverse of the command
|
||||||
- a final ``562.5 µs`` pulse burst to signify the end of message transmission
|
- a final ``562.5 µs`` pulse burst to signify the end of message transmission
|
||||||
|
|
||||||
Then we can construct the NEC :cpp:member:`rmt_encoder_t::encode` function in the same order, for example:
|
Then you can construct the NEC :cpp:member:`rmt_encoder_t::encode` function in the same order, for example:
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
|
|
||||||
@ -549,7 +550,9 @@ There is a Kconfig option :ref:`CONFIG_RMT_ISR_IRAM_SAFE` that has the following
|
|||||||
2. Place all functions used by the ISR into IRAM [2]_
|
2. Place all functions used by the ISR into IRAM [2]_
|
||||||
3. Place the driver object into DRAM in case it is mapped to PSRAM by accident
|
3. Place the driver object into DRAM in case it is mapped to PSRAM by accident
|
||||||
|
|
||||||
This Kconfig option allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
|
This Kconfig option allows the interrupt handler to run while the cache is disabled but comes at the cost of increased IRAM consumption.
|
||||||
|
|
||||||
|
Another Kconfig option :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` can place :cpp:func:`rmt_receive` into the IRAM as well. So that the receive function can be used even when the flash cache is disabled.
|
||||||
|
|
||||||
.. _rmt-thread-safety:
|
.. _rmt-thread-safety:
|
||||||
|
|
||||||
@ -559,6 +562,10 @@ Thread Safety
|
|||||||
The factory function :cpp:func:`rmt_new_tx_channel`, :cpp:func:`rmt_new_rx_channel` and :cpp:func:`rmt_new_sync_manager` are guaranteed to be thread-safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
The factory function :cpp:func:`rmt_new_tx_channel`, :cpp:func:`rmt_new_rx_channel` and :cpp:func:`rmt_new_sync_manager` are guaranteed to be thread-safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
||||||
Other functions that take the :cpp:type:`rmt_channel_handle_t` and :cpp:type:`rmt_sync_manager_handle_t` as the first positional parameter, are not thread-safe. which means the user should avoid calling them from multiple tasks.
|
Other functions that take the :cpp:type:`rmt_channel_handle_t` and :cpp:type:`rmt_sync_manager_handle_t` as the first positional parameter, are not thread-safe. which means the user should avoid calling them from multiple tasks.
|
||||||
|
|
||||||
|
The following functions are allowed to use under ISR context as well.
|
||||||
|
|
||||||
|
- :cpp:func:`rmt_receive`
|
||||||
|
|
||||||
.. _rmt-kconfig-options:
|
.. _rmt-kconfig-options:
|
||||||
|
|
||||||
Kconfig Options
|
Kconfig Options
|
||||||
@ -566,6 +573,7 @@ Kconfig Options
|
|||||||
|
|
||||||
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see also :ref:`rmt-iram-safe` for more information.
|
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see also :ref:`rmt-iram-safe` for more information.
|
||||||
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` is used to enable the debug log at the cost of increased firmware binary size.
|
||||||
|
- :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` controls where to place the RMT receive function (IRAM or Flash), see :ref:`rmt-iram-safe` for more information.
|
||||||
|
|
||||||
Application Examples
|
Application Examples
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -249,6 +249,7 @@ RMT 是一种特殊的通信外设,无法像 SPI 和 I2C 那样发送原始字
|
|||||||
注意,不是所有 ESP 芯片都支持 **循环发送** 功能,在配置此选项前,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。若所选芯片不支持配置此选项,可能会报告 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
|
注意,不是所有 ESP 芯片都支持 **循环发送** 功能,在配置此选项前,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。若所选芯片不支持配置此选项,可能会报告 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
|
||||||
|
|
||||||
- :cpp:member:`rmt_transmit_config_t::eot_level` 设置发射器完成工作时的输出电平,该设置同时适用于调用 :cpp:func:`rmt_disable` 停止发射器工作时的输出电平。
|
- :cpp:member:`rmt_transmit_config_t::eot_level` 设置发射器完成工作时的输出电平,该设置同时适用于调用 :cpp:func:`rmt_disable` 停止发射器工作时的输出电平。
|
||||||
|
- :cpp:member:`rmt_transmit_config_t::queue_nonblocking` 设置当传输队列满的时候该函数是否需要等待。如果该值设置为 ``true`` 那么当遇到队列满的时候,该函数会立即返回错误代码 :c:macro:`ESP_ERR_INVALID_STATE`。否则,函数会阻塞当前线程,直到传输队列有空档。
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -551,6 +552,8 @@ IRAM 安全
|
|||||||
|
|
||||||
启用该选项可以保证 cache 禁用时的中断运行,但会相应增加 IRAM 占用。
|
启用该选项可以保证 cache 禁用时的中断运行,但会相应增加 IRAM 占用。
|
||||||
|
|
||||||
|
另外一个 Kconfig 选项 :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` 可以将 :cpp:func:`rmt_receive` 函数放进内部的 IRAM 中,从而当 flash cache 被关闭的时候,这个函数也能够被使用。
|
||||||
|
|
||||||
.. _rmt-thread-safety:
|
.. _rmt-thread-safety:
|
||||||
|
|
||||||
线程安全
|
线程安全
|
||||||
@ -559,6 +562,10 @@ IRAM 安全
|
|||||||
RMT 驱动程序会确保工厂函数 :cpp:func:`rmt_new_tx_channel`、:cpp:func:`rmt_new_rx_channel` 和 :cpp:func:`rmt_new_sync_manager` 的线程安全。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。
|
RMT 驱动程序会确保工厂函数 :cpp:func:`rmt_new_tx_channel`、:cpp:func:`rmt_new_rx_channel` 和 :cpp:func:`rmt_new_sync_manager` 的线程安全。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。
|
||||||
其他以 :cpp:type:`rmt_channel_handle_t` 和 :cpp:type:`rmt_sync_manager_handle_t` 作为第一个位置参数的函数均非线程安全,在没有设置互斥锁保护的任务中,应避免从多个任务中调用这类函数。
|
其他以 :cpp:type:`rmt_channel_handle_t` 和 :cpp:type:`rmt_sync_manager_handle_t` 作为第一个位置参数的函数均非线程安全,在没有设置互斥锁保护的任务中,应避免从多个任务中调用这类函数。
|
||||||
|
|
||||||
|
以下函数允许在 ISR 上下文中使用:
|
||||||
|
|
||||||
|
- :cpp:func:`rmt_receive`
|
||||||
|
|
||||||
.. _rmt-kconfig-options:
|
.. _rmt-kconfig-options:
|
||||||
|
|
||||||
Kconfig 选项
|
Kconfig 选项
|
||||||
@ -566,6 +573,7 @@ Kconfig 选项
|
|||||||
|
|
||||||
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。详情请参阅 :ref:`rmt-iram-safe`。
|
- :ref:`CONFIG_RMT_ISR_IRAM_SAFE` 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。详情请参阅 :ref:`rmt-iram-safe`。
|
||||||
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` 用于启用调试日志输出,启用此选项将增加固件的二进制文件大小。
|
- :ref:`CONFIG_RMT_ENABLE_DEBUG_LOG` 用于启用调试日志输出,启用此选项将增加固件的二进制文件大小。
|
||||||
|
- :ref:`CONFIG_RMT_RECV_FUNC_IN_IRAM` 用于控制 RMT 接收函数被链接到系统内存的哪个位置(IRAM 还是 Flash)。详情请参阅 :ref:`rmt-iram-safe`。
|
||||||
|
|
||||||
应用示例
|
应用示例
|
||||||
--------------------
|
--------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user