mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user