Merge branch 'feature/specify_rmt_intr_priority' into 'master'

feat(rmt): specify interrupt priority

Closes IDF-7955

See merge request espressif/esp-idf!25596
This commit is contained in:
morris 2023-09-02 11:38:46 +08:00
commit 3b0c0ae4d4
10 changed files with 199 additions and 10 deletions

View File

@ -40,6 +40,8 @@ typedef struct {
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
} flags; /*!< RX channel config flags */
int intr_priority; /*!< RMT interrupt priority,
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
} rmt_rx_channel_config_t;
/**

View File

@ -16,6 +16,7 @@
extern "C" {
#endif
/**
* @brief Group of RMT TX callbacks
* @note The callbacks are all running under ISR environment
@ -43,6 +44,8 @@ typedef struct {
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 */
} 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;
/**

View File

@ -53,6 +53,8 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
// enable APB access RMT registers
periph_module_enable(rmt_periph_signals.groups[group_id].module);
periph_module_reset(rmt_periph_signals.groups[group_id].module);
// "uninitialize" group intr_priority, read comments in `rmt_new_tx_channel()` for detail
group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITALIZED;
// hal layer initialize
rmt_hal_init(&group->hal);
}
@ -204,3 +206,48 @@ esp_err_t rmt_disable(rmt_channel_handle_t channel)
ESP_RETURN_ON_FALSE(channel->fsm == RMT_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
return channel->disable(channel);
}
bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority)
{
bool priority_conflict = false;
portENTER_CRITICAL(&group->spinlock);
if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITALIZED) {
// intr_priority never allocated, accept user's value unconditionally
// intr_priority could only be set once here
group->intr_priority = intr_priority;
} else {
// group intr_priority already specified
// If interrupt priority specified before, it CANNOT BE CHANGED until `rmt_release_group_handle()` called
// So we have to check if the new priority specified conflicts with the old one
if (intr_priority) {
// User specified intr_priority, check if conflict or not
// Even though the `group->intr_priority` is 0, an intr_priority must have been specified automatically too,
// although we do not know it exactly now, so specifying the intr_priority again might also cause conflict.
// So no matter if `group->intr_priority` is 0 or not, we have to check.
// Value `0` of `group->intr_priority` means "unknown", NOT "unspecified"!
if (intr_priority != (group->intr_priority)) {
// intr_priority conflicts!
priority_conflict = true;
}
}
// else do nothing
// user did not specify intr_priority, then keep the old priority
// We'll use the `RMT_INTR_ALLOC_FLAG | RMT_ALLOW_INTR_PRIORITY_MASK`, which should always success
}
// The `group->intr_priority` will not change any longer, even though another task tries to modify it.
// So we could exit critical here safely.
portEXIT_CRITICAL(&group->spinlock);
return priority_conflict;
}
int rmt_get_isr_flags(rmt_group_t *group) {
int isr_flags = RMT_INTR_ALLOC_FLAG;
if (group->intr_priority) {
// Use user-specified priority bit
isr_flags |= (1 << (group->intr_priority));
} else {
// Allow all LOWMED priority bits
isr_flags |= RMT_ALLOW_INTR_PRIORITY_MASK;
}
return isr_flags;
}

View File

@ -35,21 +35,26 @@ extern "C" {
// RMT driver object is per-channel, the interrupt source is shared between channels
#if CONFIG_RMT_ISR_IRAM_SAFE
#define RMT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)
#define RMT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)
#else
#define RMT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED)
#define RMT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED)
#endif
// Hopefully the channel offset won't change in other targets
#define RMT_TX_CHANNEL_OFFSET_IN_GROUP 0
#define RMT_RX_CHANNEL_OFFSET_IN_GROUP (SOC_RMT_CHANNELS_PER_GROUP - SOC_RMT_TX_CANDIDATES_PER_GROUP)
#define RMT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
// DMA buffer size must align to `rmt_symbol_word_t`
#define RMT_DMA_DESC_BUF_MAX_SIZE (DMA_DESCRIPTOR_BUFFER_MAX_SIZE & ~(sizeof(rmt_symbol_word_t) - 1))
#define RMT_DMA_NODES_PING_PONG 2 // two nodes ping-pong
#define RMT_PM_LOCK_NAME_LEN_MAX 16
#define RMT_GROUP_INTR_PRIORITY_UNINITALIZED (-1)
typedef struct {
struct {
rmt_symbol_word_t symbols[SOC_RMT_MEM_WORDS_PER_CHANNEL];
@ -92,6 +97,7 @@ struct rmt_group_t {
rmt_tx_channel_t *tx_channels[SOC_RMT_TX_CANDIDATES_PER_GROUP]; // array of RMT TX channels
rmt_rx_channel_t *rx_channels[SOC_RMT_RX_CANDIDATES_PER_GROUP]; // array of RMT RX channels
rmt_sync_manager_t *sync_manager; // sync manager, this can be extended into an array if there're more sync controllers in one RMT group
int intr_priority; // RMT interrupt priority
};
struct rmt_channel_t {
@ -131,12 +137,13 @@ typedef struct {
uint32_t eot_level : 1; // Set the output level for the "End Of Transmission"
uint32_t encoding_done: 1; // Indicate whether the encoding has finished (not the encoding of transmission)
} flags;
} rmt_tx_trans_desc_t;
struct rmt_tx_channel_t {
rmt_channel_t base; // channel base class
size_t mem_off; // runtime argument, indicating the next writing position in the RMT hardware memory
size_t mem_end; // runtime argument, incidating the end of current writing region
size_t mem_end; // runtime argument, indicating the end of current writing region
size_t ping_pong_symbols; // ping-pong size (half of the RMT channel memory)
size_t queue_size; // size of transaction queue
size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
@ -145,7 +152,7 @@ struct rmt_tx_channel_t {
void *user_data; // user context
rmt_tx_done_callback_t on_trans_done; // callback, invoked on trans done
dma_descriptor_t dma_nodes[RMT_DMA_NODES_PING_PONG]; // DMA descriptor nodes, make up a circular link list
rmt_tx_trans_desc_t trans_desc_pool[]; // tranfer descriptor pool
rmt_tx_trans_desc_t trans_desc_pool[]; // transfer descriptor pool
};
typedef struct {
@ -194,6 +201,24 @@ void rmt_release_group_handle(rmt_group_t *group);
*/
esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t clk_src);
/**
* @brief Set interrupt priority to RMT group
* @param group RMT group to set interrupt priority to
* @param intr_priority User-specified interrupt priority (in num, not bitmask)
* @return If the priority conflicts
* - true: Interrupt priority conflict with previous specified
* - false: Interrupt priority set successfully
*/
bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority);
/**
* @brief Get isr_flags to be passed to `esp_intr_alloc_intrstatus()` according to `intr_priority` set in RMT group
* @param group RMT group
* @return isr_flags
*/
int rmt_get_isr_flags(rmt_group_t *group);
#ifdef __cplusplus
}
#endif

View File

@ -184,6 +184,11 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#endif
esp_err_t ret = ESP_OK;
rmt_rx_channel_t *rx_channel = NULL;
// Check if priority is valid
if (config->intr_priority) {
ESP_GOTO_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority);
ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority);
}
ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number");
ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
@ -227,7 +232,14 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#endif // SOC_RMT_SUPPORT_DMA
} else {
// RMT interrupt is mandatory if the channel doesn't use DMA
int isr_flags = RMT_INTR_ALLOC_FLAG;
// --- install interrupt service
// interrupt is mandatory to run basic RMT transactions, so it's not lazy installed in `rmt_tx_register_event_callbacks()`
// 1-- Set user specified priority to `group->intr_priority`
bool priority_conflict = rmt_set_intr_priority_to_group(group, config->intr_priority);
ESP_GOTO_ON_FALSE(!priority_conflict, ESP_ERR_INVALID_ARG, err, TAG, "intr_priority conflict");
// 2-- Get interrupt allocation flag
int isr_flags = rmt_get_isr_flags(group);
// 3-- Allocate interrupt using isr_flag
ret = esp_intr_alloc_intrstatus(rmt_periph_signals.groups[group_id].irq, isr_flags,
(uint32_t)rmt_ll_get_interrupt_status_reg(hal->regs),
RMT_LL_EVENT_RX_MASK(channel_id), rmt_rx_default_isr, rx_channel, &rx_channel->base.intr);

View File

@ -210,10 +210,16 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
#endif
esp_err_t ret = ESP_OK;
rmt_tx_channel_t *tx_channel = NULL;
// Check if priority is valid
if (config->intr_priority) {
ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
}
ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number");
ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL);
#if SOC_RMT_SUPPORT_DMA
// we only support 2 nodes ping-pong, if the configured memory block size needs more than two DMA descriptors, should treat it as invalid
ESP_GOTO_ON_FALSE(config->mem_block_symbols <= RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t),
@ -246,13 +252,19 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
portENTER_CRITICAL(&group->spinlock);
rmt_hal_tx_channel_reset(&group->hal, channel_id);
portEXIT_CRITICAL(&group->spinlock);
// install interrupt service
// install tx interrupt
// --- install interrupt service
// interrupt is mandatory to run basic RMT transactions, so it's not lazy installed in `rmt_tx_register_event_callbacks()`
int isr_flags = RMT_INTR_ALLOC_FLAG;
// 1-- Set user specified priority to `group->intr_priority`
bool priority_conflict = rmt_set_intr_priority_to_group(group, config->intr_priority);
ESP_GOTO_ON_FALSE(!priority_conflict, ESP_ERR_INVALID_ARG, err, TAG, "intr_priority conflict");
// 2-- Get interrupt allocation flag
int isr_flags = rmt_get_isr_flags(group);
// 3-- Allocate interrupt using isr_flag
ret = esp_intr_alloc_intrstatus(rmt_periph_signals.groups[group_id].irq, isr_flags,
(uint32_t)rmt_ll_get_interrupt_status_reg(hal->regs),
RMT_LL_EVENT_TX_MASK(channel_id), rmt_tx_default_isr, tx_channel, &tx_channel->base.intr);
(uint32_t) rmt_ll_get_interrupt_status_reg(hal->regs),
RMT_LL_EVENT_TX_MASK(channel_id), rmt_tx_default_isr, tx_channel,
&tx_channel->base.intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install tx interrupt failed");
// install DMA service
#if SOC_RMT_SUPPORT_DMA

View File

@ -196,3 +196,40 @@ TEST_CASE("rmt rx nec with carrier", "[rmt]")
test_rmt_rx_nec_carrier(128, true, RMT_CLK_SRC_DEFAULT);
#endif
}
TEST_CASE("RMT RX test specifying interrupt priority", "[rmt]")
{
rmt_clock_source_t clk_srcs[] = SOC_RMT_CLKS;
rmt_rx_channel_config_t rx_channel_cfg = {
.clk_src = clk_srcs[0],
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = 0,
.flags.with_dma = false, // Interrupt will only be allocated when dma disabled
.flags.io_loop_back = true, // the GPIO will act like a loopback
.intr_priority = 3,
};
// --- Check if specifying interrupt priority works
printf("install rx channel\r\n");
rmt_channel_handle_t rx_channel = NULL;
TEST_ESP_OK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));
rmt_channel_handle_t another_rx_channel = NULL;
rmt_rx_channel_config_t another_rx_channel_cfg = rx_channel_cfg;
// --- Check if rmt interrupt priority valid check works
another_rx_channel_cfg.intr_priority = 4; ///< Specifying a invalid intr_priority
TEST_ESP_ERR(rmt_new_rx_channel(&another_rx_channel_cfg, &another_rx_channel), ESP_ERR_INVALID_ARG);
// --- Check if rmt interrupt priority conflict check works
another_rx_channel_cfg.intr_priority = 1; ///< Specifying a conflict intr_priority
TEST_ESP_ERR(rmt_new_rx_channel(&another_rx_channel_cfg, &another_rx_channel), ESP_ERR_INVALID_ARG);
another_rx_channel_cfg.intr_priority = 0; ///< Do not specify an intr_priority, should not conflict
TEST_ESP_OK(rmt_new_rx_channel(&another_rx_channel_cfg, &another_rx_channel));
// --- Check if channel works
TEST_ESP_OK(rmt_enable(rx_channel));
TEST_ESP_OK(rmt_enable(another_rx_channel));
// --- Post-test
TEST_ESP_OK(rmt_disable(rx_channel));
TEST_ESP_OK(rmt_disable(another_rx_channel));
TEST_ESP_OK(rmt_del_channel(rx_channel));
TEST_ESP_OK(rmt_del_channel(another_rx_channel));
}

View File

@ -28,6 +28,7 @@ TEST_CASE("rmt bytes encoder", "[rmt]")
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.trans_queue_depth = 4,
.gpio_num = 0,
.intr_priority = 3
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel = NULL;
@ -75,6 +76,10 @@ TEST_CASE("rmt bytes encoder", "[rmt]")
printf("remove tx channel and encoder\r\n");
TEST_ESP_OK(rmt_del_channel(tx_channel));
TEST_ESP_OK(rmt_del_encoder(bytes_encoder));
// Test if intr_priority check works
tx_channel_cfg.intr_priority = 4; // 4 is an invalid interrupt priority
TEST_ESP_ERR(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel), ESP_ERR_INVALID_ARG);
}
static void test_rmt_channel_single_trans(size_t mem_block_symbols, bool with_dma)
@ -86,6 +91,7 @@ static void test_rmt_channel_single_trans(size_t mem_block_symbols, bool with_dm
.trans_queue_depth = 4,
.gpio_num = 0,
.flags.with_dma = with_dma,
.intr_priority = 2
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel_single_led = NULL;
@ -140,6 +146,7 @@ static void test_rmt_ping_pong_trans(size_t mem_block_symbols, bool with_dma)
.trans_queue_depth = 4,
.gpio_num = 0,
.flags.with_dma = with_dma,
.intr_priority = 1
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel_multi_leds = NULL;
@ -226,6 +233,7 @@ static void test_rmt_trans_done_event(size_t mem_block_symbols, bool with_dma)
.trans_queue_depth = 1,
.gpio_num = 0,
.flags.with_dma = with_dma,
.intr_priority = 3
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel_multi_leds = NULL;
@ -299,6 +307,7 @@ static void test_rmt_loop_trans(size_t mem_block_symbols, bool with_dma)
.trans_queue_depth = 4,
.gpio_num = 0,
.flags.with_dma = with_dma,
.intr_priority = 2
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel_multi_leds = NULL;
@ -358,6 +367,7 @@ TEST_CASE("rmt infinite loop transaction", "[rmt]")
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = 2,
.trans_queue_depth = 3,
.intr_priority = 1
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel = NULL;
@ -437,6 +447,7 @@ static void test_rmt_tx_nec_carrier(size_t mem_block_symbols, bool with_dma)
.gpio_num = 2,
.trans_queue_depth = 4,
.flags.with_dma = with_dma,
.intr_priority = 3
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel = NULL;
@ -511,6 +522,7 @@ static void test_rmt_multi_channels_trans(size_t channel0_mem_block_symbols, siz
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 4,
.intr_priority = 3
};
printf("install tx channels\r\n");
rmt_channel_handle_t tx_channels[TEST_RMT_CHANS] = {NULL};
@ -643,3 +655,38 @@ TEST_CASE("rmt multiple channels transaction", "[rmt]")
test_rmt_multi_channels_trans(1024, SOC_RMT_MEM_WORDS_PER_CHANNEL, true, false);
#endif
}
TEST_CASE("RMT TX test specifying interrupt priority", "[rmt]")
{
rmt_tx_channel_config_t tx_channel_cfg = {
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.trans_queue_depth = 4,
.gpio_num = 0,
.intr_priority = 3
};
// --- Check if specifying interrupt priority works
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel = NULL;
TEST_ESP_OK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));
rmt_channel_handle_t another_tx_channel = NULL;
rmt_tx_channel_config_t another_tx_channel_cfg = tx_channel_cfg;
// --- Check if rmt interrupt priority valid check works
another_tx_channel_cfg.intr_priority = 4;
TEST_ESP_ERR(rmt_new_tx_channel(&another_tx_channel_cfg, &another_tx_channel), ESP_ERR_INVALID_ARG);
// --- Check if rmt interrupt priority conflict check works
another_tx_channel_cfg.intr_priority = 1; ///< Specifying a conflict intr_priority
TEST_ESP_ERR(rmt_new_tx_channel(&another_tx_channel_cfg, &another_tx_channel), ESP_ERR_INVALID_ARG);
another_tx_channel_cfg.intr_priority = 0; ///< Do not specify an intr_priority, should not conflict
TEST_ESP_OK(rmt_new_tx_channel(&another_tx_channel_cfg, &another_tx_channel));
// --- Check if channel works
TEST_ESP_OK(rmt_enable(tx_channel));
TEST_ESP_OK(rmt_enable(another_tx_channel));
// --- Post-test
TEST_ESP_OK(rmt_disable(tx_channel));
TEST_ESP_OK(rmt_disable(another_tx_channel));
TEST_ESP_OK(rmt_del_channel(tx_channel));
TEST_ESP_OK(rmt_del_channel(another_tx_channel));
}

View File

@ -92,6 +92,7 @@ To install an RMT TX channel, there is a configuration structure that needs to b
- :cpp:member:`rmt_tx_channel_config_t::with_dma` enables the DMA backend for the channel. Using the DMA allows a significant amount of the channel's workload to be offloaded from the CPU. However, the DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter a :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` enables both input and output capabilities on the channel's assigned GPIO. Thus, by binding a TX and RX channel to the same GPIO, loopback can be achieved.
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` configures the channel's assigned GPIO as open-drain. When combined with :cpp:member:`rmt_tx_channel_config_t::io_loop_back`, a bi-directional bus (e.g., 1-wire) can be achieved.
- :cpp:member:`rmt_tx_channel_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`rmt_tx_channel_config_t::intr_priority`. Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`rmt_del_channel` is called.
Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function returns an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function returns :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g., DMA backend) is not supported by the hardware, it returns :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
@ -125,6 +126,7 @@ To install an RMT RX channel, there is a configuration structure that needs to b
- :cpp:member:`rmt_rx_channel_config_t::invert_in` is used to invert the input signals before it is passed to the RMT receiver. The inversion is done by the GPIO matrix instead of by the RMT peripheral.
- :cpp:member:`rmt_rx_channel_config_t::with_dma` enables the DMA backend for the channel. Using the DMA allows a significant amount of the channel's workload to be offloaded from the CPU. However, the DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter a :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_rx_channel_config_t::io_loop_back` enables both input and output capabilities on the channel's assigned GPIO. Thus, by binding a TX and RX channel to the same GPIO, loopback can be achieved.
- :cpp:member:`rmt_rx_channel_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`rmt_rx_channel_config_t::intr_priority`. Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`rmt_del_channel` is called.
Once the :cpp:type:`rmt_rx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_rx_channel` to allocate and initialize an RX channel. This function returns an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function returns :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g., DMA backend) is not supported by the hardware, it returns :c:macro:`ESP_ERR_NOT_SUPPORTED` error.

View File

@ -92,6 +92,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
- :cpp:member:`rmt_tx_channel_config_t::with_dma` 为通道启用 DMA 后端。启用 DMA 后端可以释放 CPU 上的大部分通道工作负载,显著减轻 CPU 负担。但并非所有 ESP 芯片都支持 DMA 后端,在启用此选项前,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。若所选芯片不支持 DMA 后端,可能会报告 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 启用通道所分配的 GPIO 上的输入和输出功能,将发送通道和接收通道绑定到同一个 GPIO 上,从而实现回环功能。
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` 配置通道分配的 GPIO 为开漏模式 (open-drain)。当与 :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 结合使用时,可以实现双向总线,如 1-wire。
- :cpp:member:`rmt_tx_channel_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` 驱动将会使用一个中低优先级的中断优先级可能为12或3否则会使用 :cpp:member:`rmt_tx_channel_config_t::intr_priority` 指定的优先级。请使用优先级序号123而不是bitmask的形式(1<<1)(1<<2)(1<<3))。请注意,中断优先级一旦设置,在 :cpp:func:`rmt_del_channel` 被调用之前不可再次修改。
将必要参数填充到结构体 :cpp:type:`rmt_tx_channel_config_t` 后,可以调用 :cpp:func:`rmt_new_tx_channel` 来分配和初始化 TX 通道。如果函数运行正确,会返回 RMT 通道句柄;如果 RMT 资源池内缺少空闲通道,会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误;如果硬件不支持 DMA 后端等部分功能,则返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
@ -125,6 +126,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
- :cpp:member:`rmt_rx_channel_config_t::invert_in` 在输入信号传递到 RMT 接收器前对其进行反转。该反转由 GPIO 交换矩阵完成,而非 RMT 外设。
- :cpp:member:`rmt_rx_channel_config_t::with_dma` 为通道启用 DMA 后端。启用 DMA 后端可以释放 CPU 上的大部分通道工作负载,显著减轻 CPU 负担。但并非所有 ESP 芯片都支持 DMA 后端,在启用此选项前,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。若所选芯片不支持 DMA 后端,可能会报告 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
- :cpp:member:`rmt_rx_channel_config_t::io_loop_back` 启用通道所分配的 GPIO 上的输入和输出功能,将发送通道和接收通道绑定到同一个 GPIO 上,从而实现回环功能。
- :cpp:member:`rmt_rx_channel_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` 驱动将会使用一个中低优先级的中断优先级可能为12或3否则会使用 :cpp:member:`rmt_rx_channel_config_t::intr_priority` 指定的优先级。请使用优先级序号123而不是bitmask的形式(1<<1)(1<<2)(1<<3))。请注意,中断优先级一旦设置,在 :cpp:func:`rmt_del_channel` 被调用之前不可再次修改。
将必要参数填充到结构体 :cpp:type:`rmt_rx_channel_config_t` 后,可以调用 :cpp:func:`rmt_new_rx_channel` 来分配和初始化 RX 通道。如果函数运行正确,会返回 RMT 通道句柄;如果 RMT 资源池内缺少空闲通道,会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误;如果硬件不支持 DMA 后端等部分功能,则返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。