feat(hal): graudate the RMT hal driver into a new component

This commit is contained in:
Chen Jichang
2025-11-13 17:54:54 +08:00
parent cb5f2ff4c2
commit 2cb84ecf95
65 changed files with 625 additions and 919 deletions

View File

@@ -22,9 +22,9 @@
#endif
typedef struct rmt_platform_t {
_lock_t mutex; // platform level mutex lock
rmt_group_t *groups[SOC_RMT_GROUPS]; // array of RMT group instances
int group_ref_counts[SOC_RMT_GROUPS]; // reference count used to protect group install/uninstall
_lock_t mutex; // platform level mutex lock
rmt_group_t *groups[RMT_LL_GET(INST_NUM)]; // array of RMT group instances
int group_ref_counts[RMT_LL_GET(INST_NUM)]; // reference count used to protect group install/uninstall
} rmt_platform_t;
static rmt_platform_t s_platform; // singleton platform
@@ -48,7 +48,7 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
group->group_id = group_id;
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// initial occupy_mask: 1111...100...0
group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1);
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
@@ -118,11 +118,11 @@ void rmt_release_group_handle(rmt_group_t *group)
_lock_release(&s_platform.mutex);
switch (clk_src) {
#if SOC_RMT_SUPPORT_RC_FAST
#if RMT_LL_SUPPORT(RC_FAST)
case RMT_CLK_SRC_RC_FAST:
periph_rtc_dig_clk8m_disable();
break;
#endif // SOC_RMT_SUPPORT_RC_FAST
#endif // RMT_LL_SUPPORT(RC_FAST)
default:
break;
}
@@ -142,7 +142,7 @@ void rmt_release_group_handle(rmt_group_t *group)
}
}
#if !SOC_RMT_CHANNEL_CLK_INDEPENDENT
#if !RMT_LL_GET(CHANNEL_CLK_INDEPENDENT)
static esp_err_t rmt_set_group_prescale(rmt_channel_t *chan, uint32_t expect_resolution_hz, uint32_t *ret_channel_prescale)
{
uint32_t periph_src_clk_hz = 0;
@@ -194,7 +194,7 @@ static esp_err_t rmt_set_group_prescale(rmt_channel_t *chan, uint32_t expect_res
*ret_channel_prescale = channel_prescale;
return ESP_OK;
}
#endif // SOC_RMT_CHANNEL_CLK_INDEPENDENT
#endif // RMT_LL_GET(CHANNEL_CLK_INDEPENDENT)
esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t clk_src, uint32_t expect_channel_resolution)
{
@@ -213,13 +213,13 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
"group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
// TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
#if SOC_RMT_SUPPORT_RC_FAST
#if RMT_LL_SUPPORT(RC_FAST)
if (clk_src == RMT_CLK_SRC_RC_FAST) {
// RC_FAST clock is not enabled automatically on start up, we enable it here manually.
// Note there's a ref count in the enable/disable function, we must call them in pair in the driver.
periph_rtc_dig_clk8m_enable();
}
#endif // SOC_RMT_SUPPORT_RC_FAST
#endif // RMT_LL_SUPPORT(RC_FAST)
#if CONFIG_PM_ENABLE
// if DMA is not used, we're using CPU to push the data to the RMT FIFO
@@ -236,7 +236,7 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), TAG, "clock source enable failed");
uint32_t real_div;
#if SOC_RMT_CHANNEL_CLK_INDEPENDENT
#if RMT_LL_GET(CHANNEL_CLK_INDEPENDENT)
uint32_t periph_src_clk_hz = 0;
// get clock source frequency
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz),
@@ -251,7 +251,7 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
#else
// set division for group clock source, to achieve highest resolution while guaranteeing the channel resolution.
ESP_RETURN_ON_ERROR(rmt_set_group_prescale(chan, expect_channel_resolution, &real_div), TAG, "set rmt group prescale failed");
#endif // SOC_RMT_CHANNEL_CLK_INDEPENDENT
#endif // RMT_LL_GET(CHANNEL_CLK_INDEPENDENT)
if (chan->direction == RMT_CHANNEL_DIRECTION_TX) {
rmt_ll_tx_set_channel_clock_div(group->hal.regs, chan->channel_id, real_div);

View File

@@ -26,7 +26,7 @@
#include "esp_check.h"
#include "esp_err.h"
#include "soc/soc_caps.h"
#include "soc/rmt_periph.h"
#include "hal/rmt_periph.h"
#include "hal/rmt_types.h"
#include "hal/rmt_hal.h"
#include "hal/rmt_ll.h"
@@ -74,7 +74,7 @@ extern "C" {
// 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_RX_CHANNEL_OFFSET_IN_GROUP (RMT_LL_GET(CHANS_PER_INST) - RMT_LL_GET(TX_CANDIDATES_PER_INST))
#define RMT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
@@ -102,7 +102,7 @@ extern "C" {
typedef struct {
struct {
rmt_symbol_word_t symbols[SOC_RMT_MEM_WORDS_PER_CHANNEL];
} channels[SOC_RMT_CHANNELS_PER_GROUP];
} channels[RMT_LL_GET(CHANS_PER_INST)];
} rmt_block_mem_t;
// RMTMEM address is declared in <target>.peripherals.ld
@@ -140,8 +140,8 @@ struct rmt_group_t {
rmt_clock_source_t clk_src; // record the group clock source, group clock is shared by all channels
uint32_t resolution_hz; // resolution of group clock. clk_src_hz / prescale = resolution_hz
uint32_t occupy_mask; // a set bit in the mask indicates the channel is not available
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_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
};

View File

@@ -7,7 +7,6 @@
#include "esp_memory_utils.h"
#include "esp_cache.h"
#include "esp_rom_gpio.h"
#include "soc/rmt_periph.h"
#include "driver/gpio.h"
#include "driver/rmt_rx.h"
#include "rmt_private.h"
@@ -90,12 +89,12 @@ static esp_err_t rmt_rx_register_to_group(rmt_rx_channel_t *rx_channel, const rm
// start to search for a free channel
// a channel can take up its neighbour's memory block, so the neighbour channel won't work, we should skip these "invaded" ones
int channel_scan_start = RMT_RX_CHANNEL_OFFSET_IN_GROUP;
int channel_scan_end = RMT_RX_CHANNEL_OFFSET_IN_GROUP + SOC_RMT_RX_CANDIDATES_PER_GROUP;
int channel_scan_end = RMT_RX_CHANNEL_OFFSET_IN_GROUP + RMT_LL_GET(RX_CANDIDATES_PER_INST);
if (config->flags.with_dma) {
// for DMA mode, the memory block number is always 1; for non-DMA mode, memory block number is configured by user
mem_block_num = 1;
// Only the last channel has the DMA capability
channel_scan_start = RMT_RX_CHANNEL_OFFSET_IN_GROUP + SOC_RMT_RX_CANDIDATES_PER_GROUP - 1;
channel_scan_start = RMT_RX_CHANNEL_OFFSET_IN_GROUP + RMT_LL_GET(RX_CANDIDATES_PER_INST) - 1;
rx_channel->ping_pong_symbols = 0; // with DMA, we don't need to do ping-pong
} else {
// one channel can occupy multiple memory blocks
@@ -112,7 +111,7 @@ static esp_err_t rmt_rx_register_to_group(rmt_rx_channel_t *rx_channel, const rm
uint32_t channel_mask = (1 << mem_block_num) - 1;
rmt_group_t *group = NULL;
int channel_id = -1;
for (int i = 0; i < SOC_RMT_GROUPS; i++) {
for (int i = 0; i < RMT_LL_GET(INST_NUM); i++) {
group = rmt_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
portENTER_CRITICAL(&group->spinlock);
@@ -156,7 +155,7 @@ static esp_err_t rmt_rx_destroy(rmt_rx_channel_t *rx_channel)
int group_id = rx_channel->base.group->group_id;
int channel_id = rx_channel->base.channel_id;
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig,
soc_rmt_signals[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig,
false);
}
if (rx_channel->base.intr) {
@@ -250,7 +249,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
// 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(rmt_periph_signals.groups[group_id].irq, isr_flags,
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);
ESP_GOTO_ON_ERROR(ret, err, TAG, "install rx interrupt failed");
@@ -274,7 +273,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
// always enable rx wrap, both DMA mode and ping-pong mode rely this feature
rmt_ll_rx_enable_wrap(hal->regs, channel_id, true);
#endif
#if SOC_RMT_SUPPORT_RX_DEMODULATION
#if RMT_LL_SUPPORT(RX_DEMODULATION)
// disable carrier demodulation by default, can re-enable by `rmt_apply_carrier()`
rmt_ll_rx_enable_carrier_demodulation(hal->regs, channel_id, false);
#endif
@@ -283,7 +282,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
gpio_func_sel(config->gpio_num, PIN_FUNC_GPIO);
gpio_input_enable(config->gpio_num);
esp_rom_gpio_connect_in_signal(config->gpio_num,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig,
soc_rmt_signals[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig,
config->flags.invert_in);
rx_channel->base.gpio_num = config->gpio_num;
@@ -451,7 +450,7 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
static esp_err_t rmt_rx_demodulate_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config)
{
#if !SOC_RMT_SUPPORT_RX_DEMODULATION
#if !RMT_LL_SUPPORT(RX_DEMODULATION)
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "rx demodulation not supported");
#else
rmt_group_t *group = channel->group;
@@ -591,7 +590,7 @@ bool rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
rmt_ll_rx_enable(hal->regs, channel_id, false);
portEXIT_CRITICAL_ISR(&channel->spinlock);
#if !SOC_RMT_SUPPORT_ASYNC_STOP
#if !RMT_LL_SUPPORT(ASYNC_STOP)
// This is a workaround for ESP32.
// The RX engine can not be disabled once it is enabled in ESP32
// If the state isn't RMT_FSM_RUN, it means the RX engine was disabled

View File

@@ -121,12 +121,12 @@ static esp_err_t rmt_tx_register_to_group(rmt_tx_channel_t *tx_channel, const rm
// start to search for a free channel
// a channel can take up its neighbour's memory block, so the neighbour channel won't work, we should skip these "invaded" ones
int channel_scan_start = RMT_TX_CHANNEL_OFFSET_IN_GROUP;
int channel_scan_end = RMT_TX_CHANNEL_OFFSET_IN_GROUP + SOC_RMT_TX_CANDIDATES_PER_GROUP;
int channel_scan_end = RMT_TX_CHANNEL_OFFSET_IN_GROUP + RMT_LL_GET(TX_CANDIDATES_PER_INST);
if (config->flags.with_dma) {
// for DMA mode, the memory block number is always 1; for non-DMA mode, memory block number is configured by user
mem_block_num = 1;
// Only the last channel has the DMA capability
channel_scan_start = RMT_TX_CHANNEL_OFFSET_IN_GROUP + SOC_RMT_TX_CANDIDATES_PER_GROUP - 1;
channel_scan_start = RMT_TX_CHANNEL_OFFSET_IN_GROUP + RMT_LL_GET(TX_CANDIDATES_PER_INST) - 1;
} else {
// one channel can occupy multiple memory blocks
mem_block_num = config->mem_block_symbols / SOC_RMT_MEM_WORDS_PER_CHANNEL;
@@ -142,7 +142,7 @@ static esp_err_t rmt_tx_register_to_group(rmt_tx_channel_t *tx_channel, const rm
uint32_t channel_mask = (1 << mem_block_num) - 1;
rmt_group_t *group = NULL;
int channel_id = -1;
for (int i = 0; i < SOC_RMT_GROUPS; i++) {
for (int i = 0; i < RMT_LL_GET(INST_NUM); i++) {
group = rmt_acquire_group_handle(i);
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", i);
portENTER_CRITICAL(&group->spinlock);
@@ -309,7 +309,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
// 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(rmt_periph_signals.groups[group_id].irq, isr_flags,
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);
@@ -344,7 +344,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
gpio_func_sel(config->gpio_num, PIN_FUNC_GPIO);
// connect the signal to the GPIO by matrix, it will also enable the output path properly
esp_rom_gpio_connect_out_signal(config->gpio_num,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
soc_rmt_signals[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
config->flags.invert_out, false);
tx_channel->base.gpio_num = config->gpio_num;
@@ -386,7 +386,7 @@ static esp_err_t rmt_del_tx_channel(rmt_channel_handle_t channel)
esp_err_t rmt_new_sync_manager(const rmt_sync_manager_config_t *config, rmt_sync_manager_handle_t *ret_synchro)
{
#if !SOC_RMT_SUPPORT_TX_SYNCHRO
#if !RMT_LL_SUPPORT(TX_SYNCHRO)
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "sync manager not supported");
#else
esp_err_t ret = ESP_OK;
@@ -450,12 +450,12 @@ err:
free(synchro);
}
return ret;
#endif // !SOC_RMT_SUPPORT_TX_SYNCHRO
#endif // !RMT_LL_SUPPORT(TX_SYNCHRO)
}
esp_err_t rmt_sync_reset(rmt_sync_manager_handle_t synchro)
{
#if !SOC_RMT_SUPPORT_TX_SYNCHRO
#if !RMT_LL_SUPPORT(TX_SYNCHRO)
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "sync manager not supported");
#else
ESP_RETURN_ON_FALSE(synchro, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
@@ -469,12 +469,12 @@ esp_err_t rmt_sync_reset(rmt_sync_manager_handle_t synchro)
portEXIT_CRITICAL(&group->spinlock);
return ESP_OK;
#endif // !SOC_RMT_SUPPORT_TX_SYNCHRO
#endif // !RMT_LL_SUPPORT(TX_SYNCHRO)
}
esp_err_t rmt_del_sync_manager(rmt_sync_manager_handle_t synchro)
{
#if !SOC_RMT_SUPPORT_TX_SYNCHRO
#if !RMT_LL_SUPPORT(TX_SYNCHRO)
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "sync manager not supported");
#else
ESP_RETURN_ON_FALSE(synchro, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
@@ -492,7 +492,7 @@ esp_err_t rmt_del_sync_manager(rmt_sync_manager_handle_t synchro)
ESP_LOGD(TAG, "del sync manager in group(%d)", group_id);
rmt_release_group_handle(group);
return ESP_OK;
#endif // !SOC_RMT_SUPPORT_TX_SYNCHRO
#endif // !RMT_LL_SUPPORT(TX_SYNCHRO)
}
esp_err_t rmt_tx_register_event_callbacks(rmt_channel_handle_t channel, const rmt_tx_event_callbacks_t *cbs, void *user_data)
@@ -823,14 +823,14 @@ static esp_err_t rmt_tx_disable(rmt_channel_handle_t channel)
// disable the hardware
portENTER_CRITICAL(&channel->spinlock);
rmt_ll_tx_enable_loop(hal->regs, channel->channel_id, false);
#if SOC_RMT_SUPPORT_ASYNC_STOP
#if RMT_LL_SUPPORT(ASYNC_STOP)
rmt_ll_tx_stop(hal->regs, channel->channel_id);
#endif
portEXIT_CRITICAL(&channel->spinlock);
portENTER_CRITICAL(&group->spinlock);
rmt_ll_enable_interrupt(hal->regs, RMT_LL_EVENT_TX_MASK(channel_id), false);
#if !SOC_RMT_SUPPORT_ASYNC_STOP
#if !RMT_LL_SUPPORT(ASYNC_STOP)
// we do a trick to stop the undergoing transmission
// stop interrupt, insert EOF marker to the RMT memory, polling the trans_done event
memset(channel->hw_mem_base, 0, channel->mem_block_num * SOC_RMT_MEM_WORDS_PER_CHANNEL * sizeof(rmt_symbol_word_t));
@@ -890,9 +890,7 @@ static esp_err_t rmt_tx_modulate_carrier(rmt_channel_handle_t channel, const rmt
portENTER_CRITICAL(&channel->spinlock);
rmt_ll_tx_set_carrier_level(hal->regs, channel_id, !config->flags.polarity_active_low);
rmt_ll_tx_set_carrier_high_low_ticks(hal->regs, channel_id, high_ticks, low_ticks);
#if SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY
rmt_ll_tx_enable_carrier_always_on(hal->regs, channel_id, config->flags.always_on);
#endif
portEXIT_CRITICAL(&channel->spinlock);
// save real carrier frequency
real_frequency = group->resolution_hz / total_ticks;
@@ -1156,7 +1154,7 @@ esp_err_t rmt_tx_switch_gpio(rmt_channel_handle_t channel, gpio_num_t gpio_num,
// Configure the new GPIO
gpio_func_sel(gpio_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(gpio_num,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
soc_rmt_signals[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
invert_out, false);
tx_chan->base.gpio_num = gpio_num;