refactor(rmt): switch group interrupts to private shared binding

Bind RMT channel interrupts by module name and allocate them as private
shared so channels in the same RMT instance reuse one CPU interrupt line
without exposing it to other peripherals. Drop RMT-side interrupt
priority conflict state and let the interrupt allocator enforce
compatibility.

Use soc_rmt_signals[group_id].module_name when creating RMT PM locks and
drop the per-channel formatted lock name buffer. This keeps naming
consistent with interrupt binding and removes unnecessary string
formatting state.
This commit is contained in:
morris
2026-03-26 16:09:15 +08:00
parent 8509ec1958
commit 4084c88a2e
15 changed files with 38 additions and 95 deletions
+1 -50
View File
@@ -39,8 +39,6 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
group->occupy_mask = UINT32_MAX & ~((1 << RMT_LL_GET(CHANS_PER_INST)) - 1);
// group clock won't be configured at this stage, it will be set when allocate the first channel
group->clk_src = 0;
// group interrupt priority is shared between all channels, it will be set when allocate the first channel
group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITIALIZED;
// enable the bus clock for the RMT peripheral
PERIPH_RCC_ATOMIC() {
rmt_ll_enable_bus_clock(group_id, true);
@@ -217,8 +215,7 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
// note, even if the clock source is APB, we still use CPU_FREQ_MAX lock to ensure the stability of the RMT operation.
esp_pm_lock_type_t pm_lock_type = chan->dma_chan ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_CPU_FREQ_MAX;
sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, chan->channel_id); // e.g. rmt_0_0
ret = esp_pm_lock_create(pm_lock_type, 0, chan->pm_lock_name, &chan->pm_lock);
ret = esp_pm_lock_create(pm_lock_type, 0, soc_rmt_signals[group->group_id].module_name, &chan->pm_lock);
ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
#endif // CONFIG_PM_ENABLE
@@ -293,52 +290,6 @@ esp_err_t rmt_disable(rmt_channel_handle_t channel)
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_UNINITIALIZED) {
// 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_isr_priority_to_flags(rmt_group_t *group)
{
int isr_flags = 0;
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;
}
#if RMT_USE_RETENTION_LINK
static esp_err_t rmt_create_sleep_retention_link_cb(void *arg)
{
+4 -25
View File
@@ -61,15 +61,15 @@ extern "C" {
// RMT driver object is per-channel, the interrupt source is shared between channels
#if CONFIG_RMT_TX_ISR_CACHE_SAFE
#define RMT_TX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)
#define RMT_TX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED_PRIVATE | ESP_INTR_FLAG_IRAM)
#else
#define RMT_TX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED)
#define RMT_TX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED_PRIVATE)
#endif
#if CONFIG_RMT_RX_ISR_CACHE_SAFE
#define RMT_RX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM)
#define RMT_RX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED_PRIVATE | ESP_INTR_FLAG_IRAM)
#else
#define RMT_RX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED)
#define RMT_RX_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED_PRIVATE)
#endif
// Hopefully the channel offset won't change in other targets
@@ -79,8 +79,6 @@ extern "C" {
#define RMT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#define RMT_DMA_NODES_PING_PONG 2 // two nodes ping-pong
#define RMT_PM_LOCK_NAME_LEN_MAX 16
#define RMT_GROUP_INTR_PRIORITY_UNINITIALIZED (-1)
// RMT is a slow peripheral, it only supports AHB-GDMA
#define RMT_DMA_DESC_ALIGN 4
@@ -143,7 +141,6 @@ struct rmt_group_t {
rmt_tx_channel_t *tx_channels[RMT_LL_GET(TX_CANDIDATES_PER_INST)]; // array of RMT TX channels
rmt_rx_channel_t *rx_channels[RMT_LL_GET(RX_CANDIDATES_PER_INST)]; // 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 {
@@ -161,7 +158,6 @@ struct rmt_channel_t {
gdma_channel_handle_t dma_chan; // DMA channel
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock; // power management lock
char pm_lock_name[RMT_PM_LOCK_NAME_LEN_MAX]; // pm lock name
#endif
// RMT channel common interface
// The following IO functions will have per-implementation for TX and RX channel
@@ -257,23 +253,6 @@ 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, uint32_t expect_channel_resolution);
/**
* @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 Convert the interrupt priority to flags
* @param group RMT group
* @return isr_flags which is compatible to `ESP_INTR_FLAG_*`
*/
int rmt_isr_priority_to_flags(rmt_group_t *group);
/**
* @brief Create sleep retention link
*
+11 -9
View File
@@ -242,15 +242,17 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
// RMT interrupt is mandatory if the channel doesn't use DMA
// --- 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_isr_priority_to_flags(group) | RMT_RX_INTR_ALLOC_FLAG;
// 3-- Allocate interrupt using isr_flag
ret = esp_intr_alloc_intrstatus(soc_rmt_signals[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);
int isr_flags = (config->intr_priority ? (1 << config->intr_priority) : RMT_ALLOW_INTR_PRIORITY_MASK) | RMT_RX_INTR_ALLOC_FLAG;
esp_intr_alloc_info_t intr_info = {
.source = soc_rmt_signals[group_id].irq,
.flags = isr_flags,
.intrstatusreg = (uint32_t)rmt_ll_get_interrupt_status_reg(hal->regs),
.intrstatusmask = RMT_LL_EVENT_RX_MASK(channel_id),
.handler = rmt_rx_default_isr,
.arg = rx_channel,
.bind_by.name = soc_rmt_signals[group_id].module_name,
};
ret = esp_intr_alloc_info(&intr_info, &rx_channel->base.intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install rx interrupt failed");
}
+11 -11
View File
@@ -300,18 +300,18 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
rmt_hal_tx_channel_reset(&group->hal, channel_id);
portEXIT_CRITICAL(&group->spinlock);
// 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()`
// 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_isr_priority_to_flags(group) | RMT_TX_INTR_ALLOC_FLAG;
// 3-- Allocate interrupt using isr_flag
ret = esp_intr_alloc_intrstatus(soc_rmt_signals[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);
int isr_flags = (config->intr_priority ? (1 << config->intr_priority) : RMT_ALLOW_INTR_PRIORITY_MASK) | RMT_TX_INTR_ALLOC_FLAG;
esp_intr_alloc_info_t intr_info = {
.source = soc_rmt_signals[group_id].irq,
.flags = isr_flags,
.intrstatusreg = (uint32_t)rmt_ll_get_interrupt_status_reg(hal->regs),
.intrstatusmask = RMT_LL_EVENT_TX_MASK(channel_id),
.handler = rmt_tx_default_isr,
.arg = tx_channel,
.bind_by.name = soc_rmt_signals[group_id].module_name,
};
ret = esp_intr_alloc_info(&intr_info, &tx_channel->base.intr);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install tx interrupt failed");
// install DMA service
#if SOC_RMT_SUPPORT_DMA
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -10,6 +10,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -11,6 +11,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_PAD_OUT0_IDX,
@@ -10,6 +10,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -10,6 +10,7 @@
const soc_rmt_signal_desc_t soc_rmt_signals[1] = {
[0] = {
.irq = ETS_RMT_INTR_SOURCE,
.module_name = "rmt0",
.channels = {
[0] = {
.tx_sig = RMT_SIG_OUT0_IDX,
@@ -25,6 +25,7 @@ extern "C" {
typedef struct {
const int irq;
const char *module_name;
struct {
struct {
const int tx_sig;