feat(rmt): add driver support for esp32p4

including DMA feature
This commit is contained in:
morris
2023-08-01 17:32:26 +08:00
parent 80e3ece7d1
commit 6bb05cccdd
42 changed files with 1882 additions and 593 deletions

View File

@@ -299,32 +299,7 @@ menu "Driver Configurations"
orsource "./pcnt/Kconfig.pcnt" orsource "./pcnt/Kconfig.pcnt"
menu "RMT Configuration" orsource "./rmt/Kconfig.rmt"
depends on SOC_RMT_SUPPORTED
config RMT_ISR_IRAM_SAFE
bool "RMT ISR IRAM-Safe"
default n
select GDMA_ISR_IRAM_SAFE if SOC_RMT_SUPPORT_DMA # RMT basic functionality relies on GDMA callback
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA # RMT needs to restart the GDMA in the interrupt
help
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).
config RMT_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy rmt driver (driver/rmt.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
config RMT_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for RMT driver.
Note that, this option only controls the RMT driver log, won't affect other drivers.
endmenu # RMT Configuration
orsource "./mcpwm/Kconfig.mcpwm" orsource "./mcpwm/Kconfig.mcpwm"

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -58,6 +58,18 @@ static const char *TAG = "rmt(legacy)";
#define RMT_DECODE_RX_CHANNEL(encode_chan) ((encode_chan - RMT_RX_CHANNEL_ENCODING_START)) #define RMT_DECODE_RX_CHANNEL(encode_chan) ((encode_chan - RMT_RX_CHANNEL_ENCODING_START))
#define RMT_ENCODE_RX_CHANNEL(decode_chan) ((decode_chan + RMT_RX_CHANNEL_ENCODING_START)) #define RMT_ENCODE_RX_CHANNEL(decode_chan) ((decode_chan + RMT_RX_CHANNEL_ENCODING_START))
#if SOC_PERIPH_CLK_CTRL_SHARED
#define RMT_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define RMT_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_RCC_ATOMIC()
#endif
typedef struct { typedef struct {
rmt_hal_context_t hal; rmt_hal_context_t hal;
_lock_t rmt_driver_isr_lock; _lock_t rmt_driver_isr_lock;
@@ -125,8 +137,10 @@ static void rmt_module_enable(void)
{ {
RMT_ENTER_CRITICAL(); RMT_ENTER_CRITICAL();
if (rmt_contex.rmt_module_enabled == false) { if (rmt_contex.rmt_module_enabled == false) {
periph_module_reset(rmt_periph_signals.groups[0].module); RMT_RCC_ATOMIC() {
periph_module_enable(rmt_periph_signals.groups[0].module); rmt_ll_enable_bus_clock(0, true);
rmt_ll_reset_register(0);
}
rmt_contex.rmt_module_enabled = true; rmt_contex.rmt_module_enabled = true;
} }
RMT_EXIT_CRITICAL(); RMT_EXIT_CRITICAL();
@@ -137,7 +151,9 @@ static void rmt_module_disable(void)
{ {
RMT_ENTER_CRITICAL(); RMT_ENTER_CRITICAL();
if (rmt_contex.rmt_module_enabled == true) { if (rmt_contex.rmt_module_enabled == true) {
periph_module_disable(rmt_periph_signals.groups[0].module); RMT_RCC_ATOMIC() {
rmt_ll_enable_bus_clock(0, false);
}
rmt_contex.rmt_module_enabled = false; rmt_contex.rmt_module_enabled = false;
} }
RMT_EXIT_CRITICAL(); RMT_EXIT_CRITICAL();
@@ -415,7 +431,9 @@ esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk)
ESP_RETURN_ON_FALSE(channel < RMT_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, RMT_CHANNEL_ERROR_STR); ESP_RETURN_ON_FALSE(channel < RMT_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, RMT_CHANNEL_ERROR_STR);
RMT_ENTER_CRITICAL(); RMT_ENTER_CRITICAL();
// `rmt_clock_source_t` and `rmt_source_clk_t` are binary compatible, as the underlying enum entries come from the same `soc_module_clk_t` // `rmt_clock_source_t` and `rmt_source_clk_t` are binary compatible, as the underlying enum entries come from the same `soc_module_clk_t`
rmt_ll_set_group_clock_src(rmt_contex.hal.regs, channel, (rmt_clock_source_t)base_clk, 1, 0, 0); RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_set_group_clock_src(rmt_contex.hal.regs, channel, (rmt_clock_source_t)base_clk, 1, 0, 0);
}
RMT_EXIT_CRITICAL(); RMT_EXIT_CRITICAL();
return ESP_OK; return ESP_OK;
} }
@@ -552,6 +570,7 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz;
bool carrier_en = rmt_param->tx_config.carrier_en; bool carrier_en = rmt_param->tx_config.carrier_en;
uint32_t rmt_source_clk_hz; uint32_t rmt_source_clk_hz;
rmt_clock_source_t clk_src = RMT_BASECLK_DEFAULT;
ESP_RETURN_ON_FALSE(rmt_is_channel_number_valid(channel, mode), ESP_ERR_INVALID_ARG, TAG, RMT_CHANNEL_ERROR_STR); ESP_RETURN_ON_FALSE(rmt_is_channel_number_valid(channel, mode), ESP_ERR_INVALID_ARG, TAG, RMT_CHANNEL_ERROR_STR);
ESP_RETURN_ON_FALSE(mem_cnt + channel <= SOC_RMT_CHANNELS_PER_GROUP && mem_cnt > 0, ESP_ERR_INVALID_ARG, TAG, RMT_MEM_CNT_ERROR_STR); ESP_RETURN_ON_FALSE(mem_cnt + channel <= SOC_RMT_CHANNELS_PER_GROUP && mem_cnt > 0, ESP_ERR_INVALID_ARG, TAG, RMT_MEM_CNT_ERROR_STR);
@@ -567,19 +586,18 @@ static esp_err_t rmt_internal_config(rmt_dev_t *dev, const rmt_config_t *rmt_par
if (rmt_param->flags & RMT_CHANNEL_FLAGS_AWARE_DFS) { if (rmt_param->flags & RMT_CHANNEL_FLAGS_AWARE_DFS) {
#if SOC_RMT_SUPPORT_XTAL #if SOC_RMT_SUPPORT_XTAL
// clock src: XTAL_CLK // clock src: XTAL_CLK
esp_clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_XTAL, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz); clk_src = RMT_BASECLK_XTAL;
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_XTAL, 1, 0, 0);
#elif SOC_RMT_SUPPORT_REF_TICK #elif SOC_RMT_SUPPORT_REF_TICK
// clock src: REF_CLK // clock src: REF_CLK
esp_clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_REF, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz); clk_src = RMT_BASECLK_REF;
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_REF, 1, 0, 0);
#else #else
#error "No clock source is aware of DFS" #error "No clock source is aware of DFS"
#endif #endif
} else { }
// fallback to use default clock source esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz);
esp_clk_tree_src_get_freq_hz((soc_module_clk_t)RMT_BASECLK_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rmt_source_clk_hz); RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_set_group_clock_src(dev, channel, (rmt_clock_source_t)RMT_BASECLK_DEFAULT, 1, 0, 0); rmt_ll_set_group_clock_src(dev, channel, clk_src, 1, 0, 0);
rmt_ll_enable_group_clock(dev, true);
} }
RMT_EXIT_CRITICAL(); RMT_EXIT_CRITICAL();

View File

@@ -34,7 +34,7 @@ static const char *TIMER_TAG = "timer_group";
#define TIMER_ENTER_CRITICAL(mux) portENTER_CRITICAL_SAFE(mux); #define TIMER_ENTER_CRITICAL(mux) portENTER_CRITICAL_SAFE(mux);
#define TIMER_EXIT_CRITICAL(mux) portEXIT_CRITICAL_SAFE(mux); #define TIMER_EXIT_CRITICAL(mux) portEXIT_CRITICAL_SAFE(mux);
#if CONFIG_IDF_TARGET_ESP32P4 #if SOC_PERIPH_CLK_CTRL_SHARED
#define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() #define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else #else
#define GPTIMER_CLOCK_SRC_ATOMIC() #define GPTIMER_CLOCK_SRC_ATOMIC()

View File

@@ -32,7 +32,7 @@
static const char *TAG = "gptimer"; static const char *TAG = "gptimer";
#if CONFIG_IDF_TARGET_ESP32P4 #if SOC_PERIPH_CLK_CTRL_SHARED
#define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() #define GPTIMER_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else #else
#define GPTIMER_CLOCK_SRC_ATOMIC() #define GPTIMER_CLOCK_SRC_ATOMIC()

View File

@@ -0,0 +1,26 @@
menu "RMT Configuration"
depends on SOC_RMT_SUPPORTED
config RMT_ISR_IRAM_SAFE
bool "RMT ISR IRAM-Safe"
default n
select GDMA_ISR_IRAM_SAFE if SOC_RMT_SUPPORT_DMA # RMT basic functionality relies on GDMA callback
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA # RMT needs to restart the GDMA in the interrupt
help
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).
config RMT_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy rmt driver (driver/rmt.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
config RMT_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for RMT driver.
Note that, this option only controls the RMT driver log, won't affect other drivers.
endmenu # RMT Configuration

View File

@@ -24,6 +24,18 @@
static const char *TAG = "rmt"; static const char *TAG = "rmt";
#if SOC_PERIPH_CLK_CTRL_SHARED
#define RMT_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_CLOCK_SRC_ATOMIC()
#endif
#if !SOC_RCC_IS_INDEPENDENT
#define RMT_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define RMT_RCC_ATOMIC()
#endif
typedef struct rmt_platform_t { typedef struct rmt_platform_t {
_lock_t mutex; // platform level mutex lock _lock_t mutex; // platform level mutex lock
rmt_group_t *groups[SOC_RMT_GROUPS]; // array of RMT group instances rmt_group_t *groups[SOC_RMT_GROUPS]; // array of RMT group instances
@@ -50,11 +62,13 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1); group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1);
// group clock won't be configured at this stage, it will be set when allocate the first channel // group clock won't be configured at this stage, it will be set when allocate the first channel
group->clk_src = 0; group->clk_src = 0;
// enable APB access RMT registers // group interrupt priority is shared between all channels, it will be set when allocate the first channel
periph_module_enable(rmt_periph_signals.groups[group_id].module); group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITIALIZED;
periph_module_reset(rmt_periph_signals.groups[group_id].module); // enable the bus clock for the RMT peripheral
// "uninitialize" group intr_priority, read comments in `rmt_new_tx_channel()` for detail RMT_RCC_ATOMIC() {
group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITALIZED; rmt_ll_enable_bus_clock(group_id, true);
rmt_ll_reset_register(group_id);
}
// hal layer initialize // hal layer initialize
rmt_hal_init(&group->hal); rmt_hal_init(&group->hal);
} }
@@ -78,15 +92,23 @@ void rmt_release_group_handle(rmt_group_t *group)
int group_id = group->group_id; int group_id = group->group_id;
rmt_clock_source_t clk_src = group->clk_src; rmt_clock_source_t clk_src = group->clk_src;
bool do_deinitialize = false; bool do_deinitialize = false;
rmt_hal_context_t *hal = &group->hal;
_lock_acquire(&s_platform.mutex); _lock_acquire(&s_platform.mutex);
s_platform.group_ref_counts[group_id]--; s_platform.group_ref_counts[group_id]--;
if (s_platform.group_ref_counts[group_id] == 0) { if (s_platform.group_ref_counts[group_id] == 0) {
do_deinitialize = true; do_deinitialize = true;
s_platform.groups[group_id] = NULL; s_platform.groups[group_id] = NULL;
// disable core clock
RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_enable_group_clock(hal->regs, false);
}
// hal layer deinitialize // hal layer deinitialize
rmt_hal_deinit(&group->hal); rmt_hal_deinit(hal);
periph_module_disable(rmt_periph_signals.groups[group_id].module); // disable bus clock
RMT_RCC_ATOMIC() {
rmt_ll_enable_bus_clock(group_id, false);
}
free(group); free(group);
} }
_lock_release(&s_platform.mutex); _lock_release(&s_platform.mutex);
@@ -165,7 +187,10 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
#endif // CONFIG_PM_ENABLE #endif // CONFIG_PM_ENABLE
// no division for group clock source, to achieve highest resolution // no division for group clock source, to achieve highest resolution
rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0); RMT_CLOCK_SRC_ATOMIC() {
rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0);
rmt_ll_enable_group_clock(group->hal.regs, true);
}
group->resolution_hz = periph_src_clk_hz; group->resolution_hz = periph_src_clk_hz;
ESP_LOGD(TAG, "group clock resolution:%"PRIu32, group->resolution_hz); ESP_LOGD(TAG, "group clock resolution:%"PRIu32, group->resolution_hz);
return ret; return ret;
@@ -211,7 +236,7 @@ bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority)
{ {
bool priority_conflict = false; bool priority_conflict = false;
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITALIZED) { if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITIALIZED) {
// intr_priority never allocated, accept user's value unconditionally // intr_priority never allocated, accept user's value unconditionally
// intr_priority could only be set once here // intr_priority could only be set once here
group->intr_priority = intr_priority; group->intr_priority = intr_priority;
@@ -240,7 +265,8 @@ bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority)
return priority_conflict; return priority_conflict;
} }
int rmt_get_isr_flags(rmt_group_t *group) { int rmt_get_isr_flags(rmt_group_t *group)
{
int isr_flags = RMT_INTR_ALLOC_FLAG; int isr_flags = RMT_INTR_ALLOC_FLAG;
if (group->intr_priority) { if (group->intr_priority) {
// Use user-specified priority bit // Use user-specified priority bit

View File

@@ -62,8 +62,8 @@ static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_han
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);
const uint8_t *nd = (const uint8_t *)primary_data; const uint8_t *nd = (const uint8_t *)primary_data;
rmt_encode_state_t state = RMT_ENCODING_RESET; rmt_encode_state_t state = RMT_ENCODING_RESET;
dma_descriptor_t *desc0 = NULL; rmt_dma_descriptor_t *desc0 = NULL;
dma_descriptor_t *desc1 = NULL; rmt_dma_descriptor_t *desc1 = NULL;
size_t byte_index = bytes_encoder->last_byte_index; size_t byte_index = bytes_encoder->last_byte_index;
size_t bit_index = bytes_encoder->last_bit_index; size_t bit_index = bytes_encoder->last_bit_index;
@@ -72,7 +72,12 @@ static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_han
// how many symbols we can save for this round // how many symbols we can save for this round
size_t mem_have = tx_chan->mem_end - tx_chan->mem_off; size_t mem_have = tx_chan->mem_end - tx_chan->mem_off;
// where to put the encoded symbols? DMA buffer or RMT HW memory // where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to = channel->dma_chan ? channel->dma_mem_base : channel->hw_mem_base; rmt_symbol_word_t *mem_to_nc = NULL;
if (channel->dma_chan) {
mem_to_nc = (rmt_symbol_word_t *)RMT_GET_NON_CACHE_ADDR(channel->dma_mem_base);
} else {
mem_to_nc = channel->hw_mem_base;
}
// how many symbols will be encoded in this round // how many symbols will be encoded in this round
size_t encode_len = MIN(mem_want, mem_have); size_t encode_len = MIN(mem_want, mem_have);
bool encoding_truncated = mem_have < mem_want; bool encoding_truncated = mem_have < mem_want;
@@ -81,9 +86,9 @@ static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_han
if (channel->dma_chan) { if (channel->dma_chan) {
// mark the start descriptor // mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) { if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes[0]; desc0 = &tx_chan->dma_nodes_nc[0];
} else { } else {
desc0 = &tx_chan->dma_nodes[1]; desc0 = &tx_chan->dma_nodes_nc[1];
} }
} }
@@ -97,9 +102,9 @@ static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_han
} }
while ((len > 0) && (bit_index < 8)) { while ((len > 0) && (bit_index < 8)) {
if (cur_byte & (1 << bit_index)) { if (cur_byte & (1 << bit_index)) {
mem_to[tx_chan->mem_off++] = bytes_encoder->bit1; mem_to_nc[tx_chan->mem_off++] = bytes_encoder->bit1;
} else { } else {
mem_to[tx_chan->mem_off++] = bytes_encoder->bit0; mem_to_nc[tx_chan->mem_off++] = bytes_encoder->bit0;
} }
len--; len--;
bit_index++; bit_index++;
@@ -113,9 +118,9 @@ static size_t IRAM_ATTR rmt_encode_bytes(rmt_encoder_t *encoder, rmt_channel_han
if (channel->dma_chan) { if (channel->dma_chan) {
// mark the end descriptor // mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) { if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes[0]; desc1 = &tx_chan->dma_nodes_nc[0];
} else { } else {
desc1 = &tx_chan->dma_nodes[1]; desc1 = &tx_chan->dma_nodes_nc[1];
} }
// cross line, means desc0 has prepared with sufficient data buffer // cross line, means desc0 has prepared with sufficient data buffer
@@ -168,8 +173,8 @@ static size_t IRAM_ATTR rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_hand
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_symbol_word_t *symbols = (rmt_symbol_word_t *)primary_data; rmt_symbol_word_t *symbols = (rmt_symbol_word_t *)primary_data;
rmt_encode_state_t state = RMT_ENCODING_RESET; rmt_encode_state_t state = RMT_ENCODING_RESET;
dma_descriptor_t *desc0 = NULL; rmt_dma_descriptor_t *desc0 = NULL;
dma_descriptor_t *desc1 = NULL; rmt_dma_descriptor_t *desc1 = NULL;
size_t symbol_index = copy_encoder->last_symbol_index; size_t symbol_index = copy_encoder->last_symbol_index;
// how many symbols will be copied by the encoder // how many symbols will be copied by the encoder
@@ -177,7 +182,12 @@ static size_t IRAM_ATTR rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_hand
// how many symbols we can save for this round // how many symbols we can save for this round
size_t mem_have = tx_chan->mem_end - tx_chan->mem_off; size_t mem_have = tx_chan->mem_end - tx_chan->mem_off;
// where to put the encoded symbols? DMA buffer or RMT HW memory // where to put the encoded symbols? DMA buffer or RMT HW memory
rmt_symbol_word_t *mem_to = channel->dma_chan ? channel->dma_mem_base : channel->hw_mem_base; rmt_symbol_word_t *mem_to_nc = NULL;
if (channel->dma_chan) {
mem_to_nc = (rmt_symbol_word_t *)RMT_GET_NON_CACHE_ADDR(channel->dma_mem_base);
} else {
mem_to_nc = channel->hw_mem_base;
}
// how many symbols will be encoded in this round // how many symbols will be encoded in this round
size_t encode_len = MIN(mem_want, mem_have); size_t encode_len = MIN(mem_want, mem_have);
bool encoding_truncated = mem_have < mem_want; bool encoding_truncated = mem_have < mem_want;
@@ -186,24 +196,24 @@ static size_t IRAM_ATTR rmt_encode_copy(rmt_encoder_t *encoder, rmt_channel_hand
if (channel->dma_chan) { if (channel->dma_chan) {
// mark the start descriptor // mark the start descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) { if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc0 = &tx_chan->dma_nodes[0]; desc0 = &tx_chan->dma_nodes_nc[0];
} else { } else {
desc0 = &tx_chan->dma_nodes[1]; desc0 = &tx_chan->dma_nodes_nc[1];
} }
} }
size_t len = encode_len; size_t len = encode_len;
while (len > 0) { while (len > 0) {
mem_to[tx_chan->mem_off++] = symbols[symbol_index++]; mem_to_nc[tx_chan->mem_off++] = symbols[symbol_index++];
len--; len--;
} }
if (channel->dma_chan) { if (channel->dma_chan) {
// mark the end descriptor // mark the end descriptor
if (tx_chan->mem_off < tx_chan->ping_pong_symbols) { if (tx_chan->mem_off < tx_chan->ping_pong_symbols) {
desc1 = &tx_chan->dma_nodes[0]; desc1 = &tx_chan->dma_nodes_nc[0];
} else { } else {
desc1 = &tx_chan->dma_nodes[1]; desc1 = &tx_chan->dma_nodes_nc[1];
} }
// cross line, means desc0 has prepared with sufficient data buffer // cross line, means desc0 has prepared with sufficient data buffer

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -13,9 +13,11 @@
#include "freertos/idf_additions.h" #include "freertos/idf_additions.h"
#include "esp_err.h" #include "esp_err.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/gdma_channel.h"
#include "hal/rmt_types.h" #include "hal/rmt_types.h"
#include "hal/rmt_hal.h" #include "hal/rmt_hal.h"
#include "hal/dma_types.h" #include "hal/dma_types.h"
#include "hal/cache_ll.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_pm.h" #include "esp_pm.h"
@@ -44,16 +46,27 @@ extern "C" {
#define RMT_TX_CHANNEL_OFFSET_IN_GROUP 0 #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 (SOC_RMT_CHANNELS_PER_GROUP - SOC_RMT_TX_CANDIDATES_PER_GROUP)
#define RMT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED #define RMT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
// DMA buffer size must align to `rmt_symbol_word_t` // 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_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_DMA_NODES_PING_PONG 2 // two nodes ping-pong
#define RMT_PM_LOCK_NAME_LEN_MAX 16 #define RMT_PM_LOCK_NAME_LEN_MAX 16
#define RMT_GROUP_INTR_PRIORITY_UNINITIALIZED (-1)
#define RMT_GROUP_INTR_PRIORITY_UNINITALIZED (-1) #if SOC_GDMA_TRIG_PERIPH_RMT0_BUS == SOC_GDMA_BUS_AHB
#define RMT_DMA_DESC_ALIGN 32
typedef dma_descriptor_align4_t rmt_dma_descriptor_t;
#else
#error "Unsupported RMT DMA bus"
#endif
#ifdef CACHE_LL_L2MEM_NON_CACHE_ADDR
#define RMT_GET_NON_CACHE_ADDR(addr) ((addr) ? CACHE_LL_L2MEM_NON_CACHE_ADDR(addr) : 0)
#else
#define RMT_GET_NON_CACHE_ADDR(addr) (addr)
#endif
typedef struct { typedef struct {
struct { struct {
@@ -151,7 +164,8 @@ struct rmt_tx_channel_t {
rmt_tx_trans_desc_t *cur_trans; // points to current transaction rmt_tx_trans_desc_t *cur_trans; // points to current transaction
void *user_data; // user context void *user_data; // user context
rmt_tx_done_callback_t on_trans_done; // callback, invoked on trans done 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_dma_descriptor_t *dma_nodes; // DMA descriptor nodes
rmt_dma_descriptor_t *dma_nodes_nc; // DMA descriptor nodes accessed in non-cached way
rmt_tx_trans_desc_t trans_desc_pool[]; // transfer descriptor pool rmt_tx_trans_desc_t trans_desc_pool[]; // transfer descriptor pool
}; };
@@ -164,13 +178,14 @@ typedef struct {
struct rmt_rx_channel_t { struct rmt_rx_channel_t {
rmt_channel_t base; // channel base class rmt_channel_t base; // channel base class
size_t mem_off; // starting offset to fetch the symbols in RMTMEM size_t mem_off; // starting offset to fetch the symbols in RMT-MEM
size_t ping_pong_symbols; // ping-pong size (half of the RMT channel memory) size_t ping_pong_symbols; // ping-pong size (half of the RMT channel memory)
rmt_rx_done_callback_t on_recv_done; // callback, invoked on receive done rmt_rx_done_callback_t on_recv_done; // callback, invoked on receive done
void *user_data; // user context void *user_data; // user context
rmt_rx_trans_desc_t trans_desc; // transaction description rmt_rx_trans_desc_t trans_desc; // transaction description
size_t num_dma_nodes; // number of DMA nodes, determined by how big the memory block that user configures size_t num_dma_nodes; // number of DMA nodes, determined by how big the memory block that user configures
dma_descriptor_t dma_nodes[]; // DMA link nodes rmt_dma_descriptor_t *dma_nodes; // DMA link nodes
rmt_dma_descriptor_t *dma_nodes_nc; // DMA descriptor nodes accessed in non-cached way
}; };
/** /**
@@ -201,7 +216,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); 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 * @brief Set interrupt priority to RMT group
* @param group RMT group to set interrupt priority to * @param group RMT group to set interrupt priority to

View File

@@ -21,10 +21,12 @@
#include "soc/rmt_periph.h" #include "soc/rmt_periph.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "hal/rmt_ll.h" #include "hal/rmt_ll.h"
#include "hal/cache_hal.h"
#include "hal/gpio_hal.h" #include "hal/gpio_hal.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/rmt_rx.h" #include "driver/rmt_rx.h"
#include "rmt_private.h" #include "rmt_private.h"
#include "rom/cache.h"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
@@ -39,26 +41,26 @@ static void rmt_rx_default_isr(void *args);
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
static bool rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data); static bool rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
static void rmt_rx_mount_dma_buffer(dma_descriptor_t *desc_array, size_t array_size, const void *buffer, size_t buffer_size) static void rmt_rx_mount_dma_buffer(rmt_dma_descriptor_t *desc_array, rmt_dma_descriptor_t *desc_array_nc, size_t array_size, const void *buffer, size_t buffer_size)
{ {
size_t prepared_length = 0; size_t prepared_length = 0;
uint8_t *data = (uint8_t *)buffer; uint8_t *data = (uint8_t *)buffer;
int dma_node_i = 0; int dma_node_i = 0;
dma_descriptor_t *desc = NULL; rmt_dma_descriptor_t *desc = NULL;
while (buffer_size > RMT_DMA_DESC_BUF_MAX_SIZE) { while (buffer_size > RMT_DMA_DESC_BUF_MAX_SIZE) {
desc = &desc_array[dma_node_i]; desc = &desc_array_nc[dma_node_i];
desc->dw0.suc_eof = 0; desc->dw0.suc_eof = 0;
desc->dw0.size = RMT_DMA_DESC_BUF_MAX_SIZE; desc->dw0.size = RMT_DMA_DESC_BUF_MAX_SIZE;
desc->dw0.length = 0; desc->dw0.length = 0;
desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
desc->buffer = &data[prepared_length]; desc->buffer = &data[prepared_length];
desc->next = &desc_array[dma_node_i + 1]; desc->next = &desc_array[dma_node_i + 1]; // note, we must use the cache address for the "next" pointer
prepared_length += RMT_DMA_DESC_BUF_MAX_SIZE; prepared_length += RMT_DMA_DESC_BUF_MAX_SIZE;
buffer_size -= RMT_DMA_DESC_BUF_MAX_SIZE; buffer_size -= RMT_DMA_DESC_BUF_MAX_SIZE;
dma_node_i++; dma_node_i++;
} }
if (buffer_size) { if (buffer_size) {
desc = &desc_array[dma_node_i]; desc = &desc_array_nc[dma_node_i];
desc->dw0.suc_eof = 0; desc->dw0.suc_eof = 0;
desc->dw0.size = buffer_size; desc->dw0.size = buffer_size;
desc->dw0.length = 0; desc->dw0.length = 0;
@@ -77,11 +79,6 @@ static esp_err_t rmt_rx_init_dma_link(rmt_rx_channel_t *rx_channel, const rmt_rx
#if SOC_GDMA_TRIG_PERIPH_RMT0_BUS == SOC_GDMA_BUS_AHB #if SOC_GDMA_TRIG_PERIPH_RMT0_BUS == SOC_GDMA_BUS_AHB
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &rx_channel->base.dma_chan), TAG, "allocate RX DMA channel failed"); ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &rx_channel->base.dma_chan), TAG, "allocate RX DMA channel failed");
#endif #endif
gdma_strategy_config_t gdma_strategy_conf = {
.auto_update_desc = true,
.owner_check = true,
};
gdma_apply_strategy(rx_channel->base.dma_chan, &gdma_strategy_conf);
gdma_rx_event_callbacks_t cbs = { gdma_rx_event_callbacks_t cbs = {
.on_recv_eof = rmt_dma_rx_eof_cb, .on_recv_eof = rmt_dma_rx_eof_cb,
}; };
@@ -173,6 +170,9 @@ static esp_err_t rmt_rx_destroy(rmt_rx_channel_t *rx_channel)
// de-register channel from RMT group // de-register channel from RMT group
rmt_rx_unregister_from_group(&rx_channel->base, rx_channel->base.group); rmt_rx_unregister_from_group(&rx_channel->base, rx_channel->base.group);
} }
if (rx_channel->dma_nodes) {
free(rx_channel->dma_nodes);
}
free(rx_channel); free(rx_channel);
return ESP_OK; return ESP_OK;
} }
@@ -197,18 +197,21 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported"); ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported");
#endif // SOC_RMT_SUPPORT_DMA #endif // SOC_RMT_SUPPORT_DMA
size_t num_dma_nodes = 0;
if (config->flags.with_dma) {
num_dma_nodes = config->mem_block_symbols * sizeof(rmt_symbol_word_t) / RMT_DMA_DESC_BUF_MAX_SIZE + 1;
}
// malloc channel memory // malloc channel memory
uint32_t mem_caps = RMT_MEM_ALLOC_CAPS; uint32_t mem_caps = RMT_MEM_ALLOC_CAPS;
if (config->flags.with_dma) { rx_channel = heap_caps_calloc(1, sizeof(rmt_rx_channel_t), mem_caps);
// DMA descriptors must be placed in internal SRAM
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
}
rx_channel = heap_caps_calloc(1, sizeof(rmt_rx_channel_t) + num_dma_nodes * sizeof(dma_descriptor_t), mem_caps);
ESP_GOTO_ON_FALSE(rx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for rx channel"); ESP_GOTO_ON_FALSE(rx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for rx channel");
// create DMA descriptor
size_t num_dma_nodes = 0;
if (config->flags.with_dma) {
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
num_dma_nodes = config->mem_block_symbols * sizeof(rmt_symbol_word_t) / RMT_DMA_DESC_BUF_MAX_SIZE + 1;
// DMA descriptors must be placed in internal SRAM
rx_channel->dma_nodes = heap_caps_aligned_calloc(RMT_DMA_DESC_ALIGN, num_dma_nodes, sizeof(rmt_dma_descriptor_t), mem_caps);
ESP_GOTO_ON_FALSE(rx_channel->dma_nodes, ESP_ERR_NO_MEM, err, TAG, "no mem for rx channel DMA nodes");
// we will use the non-cached address to manipulate the DMA descriptor, for simplicity
rx_channel->dma_nodes_nc = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(rx_channel->dma_nodes);
}
rx_channel->num_dma_nodes = num_dma_nodes; rx_channel->num_dma_nodes = num_dma_nodes;
// register the channel to group // register the channel to group
ESP_GOTO_ON_ERROR(rmt_rx_register_to_group(rx_channel, config), err, TAG, "register channel failed"); ESP_GOTO_ON_ERROR(rmt_rx_register_to_group(rx_channel, config), err, TAG, "register channel failed");
@@ -348,6 +351,12 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
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(esp_ptr_internal(buffer), ESP_ERR_INVALID_ARG, TAG, "buffer must locate in internal RAM for DMA use");
#if CONFIG_IDF_TARGET_ESP32P4
uint32_t data_cache_line_mask = cache_hal_get_cache_line_size(CACHE_TYPE_DATA) - 1;
ESP_RETURN_ON_FALSE(((uintptr_t)buffer & data_cache_line_mask) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer must be aligned to cache line size");
ESP_RETURN_ON_FALSE((buffer_size & data_cache_line_mask) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer size must be aligned to cache line size");
#endif
} }
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(buffer_size <= rx_chan->num_dma_nodes * RMT_DMA_DESC_BUF_MAX_SIZE,
@@ -371,9 +380,9 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
if (channel->dma_chan) { if (channel->dma_chan) {
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
rmt_rx_mount_dma_buffer(rx_chan->dma_nodes, rx_chan->num_dma_nodes, buffer, buffer_size); rmt_rx_mount_dma_buffer(rx_chan->dma_nodes, rx_chan->dma_nodes_nc, rx_chan->num_dma_nodes, buffer, buffer_size);
gdma_reset(channel->dma_chan); gdma_reset(channel->dma_chan);
gdma_start(channel->dma_chan, (intptr_t)rx_chan->dma_nodes); gdma_start(channel->dma_chan, (intptr_t)rx_chan->dma_nodes); // note, we must use the cached descriptor address to start the DMA
#endif #endif
} }
@@ -624,12 +633,12 @@ static void IRAM_ATTR rmt_rx_default_isr(void *args)
} }
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
static size_t IRAM_ATTR rmt_rx_get_received_symbol_num_from_dma(dma_descriptor_t *desc) static size_t IRAM_ATTR rmt_rx_get_received_symbol_num_from_dma(rmt_dma_descriptor_t *desc_nc)
{ {
size_t received_bytes = 0; size_t received_bytes = 0;
while (desc) { while (desc_nc) {
received_bytes += desc->dw0.length; received_bytes += desc_nc->dw0.length;
desc = desc->next; desc_nc = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(desc_nc->next);
} }
received_bytes = ALIGN_UP(received_bytes, sizeof(rmt_symbol_word_t)); received_bytes = ALIGN_UP(received_bytes, sizeof(rmt_symbol_word_t));
return received_bytes / sizeof(rmt_symbol_word_t); return received_bytes / sizeof(rmt_symbol_word_t);
@@ -650,10 +659,18 @@ 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);
#if CONFIG_IDF_TARGET_ESP32P4
int invalidate_map = CACHE_MAP_L1_DCACHE;
if (esp_ptr_external_ram((const void *)trans_desc->buffer)) {
invalidate_map |= CACHE_MAP_L2_CACHE;
}
Cache_Invalidate_Addr(invalidate_map, (uint32_t)trans_desc->buffer, trans_desc->buffer_size);
#endif
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,
.num_symbols = rmt_rx_get_received_symbol_num_from_dma(rx_chan->dma_nodes), .num_symbols = rmt_rx_get_received_symbol_num_from_dma(rx_chan->dma_nodes_nc),
}; };
if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) { if (rx_chan->on_recv_done(channel, &edata, rx_chan->user_data)) {
need_yield = true; need_yield = true;

View File

@@ -46,29 +46,26 @@ static bool rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t
static esp_err_t rmt_tx_init_dma_link(rmt_tx_channel_t *tx_channel, const rmt_tx_channel_config_t *config) static esp_err_t rmt_tx_init_dma_link(rmt_tx_channel_t *tx_channel, const rmt_tx_channel_config_t *config)
{ {
rmt_symbol_word_t *dma_mem_base = heap_caps_calloc(1, sizeof(rmt_symbol_word_t) * config->mem_block_symbols, RMT_MEM_ALLOC_CAPS | MALLOC_CAP_DMA); rmt_symbol_word_t *dma_mem_base = heap_caps_calloc(1, sizeof(rmt_symbol_word_t) * config->mem_block_symbols,
RMT_MEM_ALLOC_CAPS | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
ESP_RETURN_ON_FALSE(dma_mem_base, ESP_ERR_NO_MEM, TAG, "no mem for tx DMA buffer"); ESP_RETURN_ON_FALSE(dma_mem_base, ESP_ERR_NO_MEM, TAG, "no mem for tx DMA buffer");
tx_channel->base.dma_mem_base = dma_mem_base; tx_channel->base.dma_mem_base = dma_mem_base;
for (int i = 0; i < RMT_DMA_NODES_PING_PONG; i++) { for (int i = 0; i < RMT_DMA_NODES_PING_PONG; i++) {
// each descriptor shares half of the DMA buffer // each descriptor shares half of the DMA buffer
tx_channel->dma_nodes[i].buffer = dma_mem_base + tx_channel->ping_pong_symbols * i; tx_channel->dma_nodes_nc[i].buffer = dma_mem_base + tx_channel->ping_pong_symbols * i;
tx_channel->dma_nodes[i].dw0.size = tx_channel->ping_pong_symbols * sizeof(rmt_symbol_word_t); tx_channel->dma_nodes_nc[i].dw0.size = tx_channel->ping_pong_symbols * sizeof(rmt_symbol_word_t);
// the ownership will be switched to DMA in `rmt_tx_do_transaction()` // the ownership will be switched to DMA in `rmt_tx_do_transaction()`
tx_channel->dma_nodes[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU; tx_channel->dma_nodes_nc[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU;
// each node can generate the DMA eof interrupt, and the driver will do a ping-pong trick in the eof callback // each node can generate the DMA eof interrupt, and the driver will do a ping-pong trick in the eof callback
tx_channel->dma_nodes[i].dw0.suc_eof = 1; tx_channel->dma_nodes_nc[i].dw0.suc_eof = 1;
} }
gdma_channel_alloc_config_t dma_chan_config = { gdma_channel_alloc_config_t dma_chan_config = {
.direction = GDMA_CHANNEL_DIRECTION_TX, .direction = GDMA_CHANNEL_DIRECTION_TX,
}; };
#if SOC_GDMA_TRIG_PERIPH_RMT0_BUS == SOC_GDMA_BUS_AHB #if SOC_GDMA_TRIG_PERIPH_RMT0_BUS == SOC_GDMA_BUS_AHB
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &tx_channel->base.dma_chan), TAG, "allocate TX DMA channel failed"); ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(&dma_chan_config, &tx_channel->base.dma_chan), TAG, "allocate TX DMA channel failed");
#endif #endif
gdma_strategy_config_t gdma_strategy_conf = {
.auto_update_desc = true,
.owner_check = true,
};
gdma_apply_strategy(tx_channel->base.dma_chan, &gdma_strategy_conf);
gdma_tx_event_callbacks_t cbs = { gdma_tx_event_callbacks_t cbs = {
.on_trans_eof = rmt_dma_tx_eof_cb, .on_trans_eof = rmt_dma_tx_eof_cb,
}; };
@@ -199,6 +196,9 @@ static esp_err_t rmt_tx_destroy(rmt_tx_channel_t *tx_channel)
// de-register channel from RMT group // de-register channel from RMT group
rmt_tx_unregister_from_group(&tx_channel->base, tx_channel->base.group); rmt_tx_unregister_from_group(&tx_channel->base, tx_channel->base.group);
} }
if (tx_channel->dma_nodes) {
free(tx_channel->dma_nodes);
}
free(tx_channel); free(tx_channel);
return ESP_OK; return ESP_OK;
} }
@@ -231,12 +231,17 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
// malloc channel memory // malloc channel memory
uint32_t mem_caps = RMT_MEM_ALLOC_CAPS; uint32_t mem_caps = RMT_MEM_ALLOC_CAPS;
tx_channel = heap_caps_calloc(1, sizeof(rmt_tx_channel_t) + sizeof(rmt_tx_trans_desc_t) * config->trans_queue_depth, mem_caps);
ESP_GOTO_ON_FALSE(tx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for tx channel");
// create DMA descriptors
if (config->flags.with_dma) { if (config->flags.with_dma) {
// DMA descriptors must be placed in internal SRAM // DMA descriptors must be placed in internal SRAM
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
tx_channel->dma_nodes = heap_caps_aligned_calloc(RMT_DMA_DESC_ALIGN, RMT_DMA_NODES_PING_PONG, sizeof(rmt_dma_descriptor_t), mem_caps);
ESP_GOTO_ON_FALSE(tx_channel->dma_nodes, ESP_ERR_NO_MEM, err, TAG, "no mem for tx DMA nodes");
// we will use the non-cached address to manipulate the DMA descriptor, for simplicity
tx_channel->dma_nodes_nc = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(tx_channel->dma_nodes);
} }
tx_channel = heap_caps_calloc(1, sizeof(rmt_tx_channel_t) + sizeof(rmt_tx_trans_desc_t) * config->trans_queue_depth, mem_caps);
ESP_GOTO_ON_FALSE(tx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for tx channel");
// create transaction queues // create transaction queues
ESP_GOTO_ON_ERROR(rmt_tx_create_trans_queue(tx_channel, config), err, TAG, "install trans queues failed"); ESP_GOTO_ON_ERROR(rmt_tx_create_trans_queue(tx_channel, config), err, TAG, "install trans queues failed");
// register the channel to group // register the channel to group
@@ -318,9 +323,9 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
tx_channel->base.disable = rmt_tx_disable; tx_channel->base.disable = rmt_tx_disable;
// return general channel handle // return general channel handle
*ret_chan = &tx_channel->base; *ret_chan = &tx_channel->base;
ESP_LOGD(TAG, "new tx channel(%d,%d) at %p, gpio=%d, res=%"PRIu32"Hz, hw_mem_base=%p, dma_mem_base=%p, ping_pong_size=%zu, queue_depth=%zu", ESP_LOGD(TAG, "new tx channel(%d,%d) at %p, gpio=%d, res=%"PRIu32"Hz, hw_mem_base=%p, dma_mem_base=%p, dma_nodes_nc=%p,ping_pong_size=%zu, queue_depth=%zu",
group_id, channel_id, tx_channel, config->gpio_num, tx_channel->base.resolution_hz, group_id, channel_id, tx_channel, config->gpio_num, tx_channel->base.resolution_hz,
tx_channel->base.hw_mem_base, tx_channel->base.dma_mem_base, tx_channel->ping_pong_symbols, tx_channel->queue_size); tx_channel->base.hw_mem_base, tx_channel->base.dma_mem_base, tx_channel->dma_nodes_nc, tx_channel->ping_pong_symbols, tx_channel->queue_size);
return ESP_OK; return ESP_OK;
err: err:
@@ -548,12 +553,17 @@ static void IRAM_ATTR rmt_tx_mark_eof(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_group_t *group = channel->group;
int channel_id = channel->channel_id; int channel_id = channel->channel_id;
rmt_symbol_word_t *mem_to = channel->dma_chan ? channel->dma_mem_base : channel->hw_mem_base; rmt_symbol_word_t *mem_to_nc = NULL;
rmt_tx_trans_desc_t *cur_trans = tx_chan->cur_trans; rmt_tx_trans_desc_t *cur_trans = tx_chan->cur_trans;
dma_descriptor_t *desc = NULL; rmt_dma_descriptor_t *desc_nc = NULL;
if (channel->dma_chan) {
mem_to_nc = (rmt_symbol_word_t *)RMT_GET_NON_CACHE_ADDR(channel->dma_mem_base);
} else {
mem_to_nc = channel->hw_mem_base;
}
// a RMT word whose duration is zero means a "stop" pattern // a RMT word whose duration is zero means a "stop" pattern
mem_to[tx_chan->mem_off++] = (rmt_symbol_word_t) { mem_to_nc[tx_chan->mem_off++] = (rmt_symbol_word_t) {
.duration0 = 0, .duration0 = 0,
.level0 = cur_trans->flags.eot_level, .level0 = cur_trans->flags.eot_level,
.duration1 = 0, .duration1 = 0,
@@ -563,16 +573,16 @@ static void IRAM_ATTR rmt_tx_mark_eof(rmt_tx_channel_t *tx_chan)
size_t off = 0; size_t off = 0;
if (channel->dma_chan) { if (channel->dma_chan) {
if (tx_chan->mem_off <= tx_chan->ping_pong_symbols) { if (tx_chan->mem_off <= tx_chan->ping_pong_symbols) {
desc = &tx_chan->dma_nodes[0]; desc_nc = &tx_chan->dma_nodes_nc[0];
off = tx_chan->mem_off; off = tx_chan->mem_off;
} else { } else {
desc = &tx_chan->dma_nodes[1]; desc_nc = &tx_chan->dma_nodes_nc[1];
off = tx_chan->mem_off - tx_chan->ping_pong_symbols; off = tx_chan->mem_off - tx_chan->ping_pong_symbols;
} }
desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; desc_nc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
desc->dw0.length = off * sizeof(rmt_symbol_word_t); desc_nc->dw0.length = off * sizeof(rmt_symbol_word_t);
// break down the DMA descriptor link // break down the DMA descriptor link
desc->next = NULL; desc_nc->next = NULL;
} else { } else {
portENTER_CRITICAL_ISR(&group->spinlock); portENTER_CRITICAL_ISR(&group->spinlock);
// This is the end of a sequence of encoding sessions, disable the threshold interrupt as no more data will be put into RMT memory block // This is the end of a sequence of encoding sessions, disable the threshold interrupt as no more data will be put into RMT memory block
@@ -586,6 +596,7 @@ static size_t IRAM_ATTR rmt_encode_check_result(rmt_tx_channel_t *tx_chan, rmt_t
rmt_encode_state_t encode_state = RMT_ENCODING_RESET; rmt_encode_state_t encode_state = RMT_ENCODING_RESET;
rmt_encoder_handle_t encoder = t->encoder; rmt_encoder_handle_t encoder = t->encoder;
size_t encoded_symbols = encoder->encode(encoder, &tx_chan->base, t->payload, t->payload_bytes, &encode_state); size_t encoded_symbols = encoder->encode(encoder, &tx_chan->base, t->payload, t->payload_bytes, &encode_state);
if (encode_state & RMT_ENCODING_COMPLETE) { if (encode_state & RMT_ENCODING_COMPLETE) {
t->flags.encoding_done = true; t->flags.encoding_done = true;
// inserting EOF symbol if there's extra space // inserting EOF symbol if there's extra space
@@ -615,12 +626,12 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
#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);
// chain the descritpros into a ring, and will break it in `rmt_encode_eof()` // chain the descriptors into a ring, and will break it in `rmt_encode_eof()`
for (int i = 0; i < RMT_DMA_NODES_PING_PONG; i++) { for (int i = 0; i < RMT_DMA_NODES_PING_PONG; i++) {
tx_chan->dma_nodes[i].next = &tx_chan->dma_nodes[i + 1]; tx_chan->dma_nodes_nc[i].next = &tx_chan->dma_nodes[i + 1]; // note, we must use the cache address for the next pointer
tx_chan->dma_nodes[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU; tx_chan->dma_nodes_nc[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU;
} }
tx_chan->dma_nodes[1].next = &tx_chan->dma_nodes[0]; tx_chan->dma_nodes_nc[1].next = &tx_chan->dma_nodes[0];
} }
#endif // SOC_RMT_SUPPORT_DMA #endif // SOC_RMT_SUPPORT_DMA
@@ -672,7 +683,7 @@ static void IRAM_ATTR rmt_tx_do_transaction(rmt_tx_channel_t *tx_chan, rmt_tx_tr
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
if (channel->dma_chan) { if (channel->dma_chan) {
gdma_start(channel->dma_chan, (intptr_t)tx_chan->dma_nodes); gdma_start(channel->dma_chan, (intptr_t)tx_chan->dma_nodes); // note, we must use the cached descriptor address to start the DMA
// delay a while, wait for DMA data going to RMT memory block // delay a while, wait for DMA data going to RMT memory block
esp_rom_delay_us(1); esp_rom_delay_us(1);
} }
@@ -1040,22 +1051,26 @@ static void IRAM_ATTR rmt_tx_default_isr(void *args)
static bool IRAM_ATTR rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data) static bool IRAM_ATTR rmt_dma_tx_eof_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
{ {
rmt_tx_channel_t *tx_chan = (rmt_tx_channel_t *)user_data; rmt_tx_channel_t *tx_chan = (rmt_tx_channel_t *)user_data;
dma_descriptor_t *eof_desc = (dma_descriptor_t *)event_data->tx_eof_desc_addr; rmt_dma_descriptor_t *eof_desc_nc = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(event_data->tx_eof_desc_addr);
// if the DMA descriptor link is still a ring (i.e. hasn't broken down by `rmt_tx_mark_eof()`), then we treat it as a valid ping-pong event rmt_dma_descriptor_t *n = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(eof_desc_nc->next); // next points to a cache address, needs to convert it to a non-cached one
if (eof_desc->next && eof_desc->next->next) { if (n) {
// continue pingpong transmission rmt_dma_descriptor_t *nn = (rmt_dma_descriptor_t *)RMT_GET_NON_CACHE_ADDR(n->next);
rmt_tx_trans_desc_t *t = tx_chan->cur_trans; // if the DMA descriptor link is still a ring (i.e. hasn't broken down by `rmt_tx_mark_eof()`), then we treat it as a valid ping-pong event
size_t encoded_symbols = t->transmitted_symbol_num; if (nn) {
if (t->flags.encoding_done) { // continue ping-pong transmission
rmt_tx_mark_eof(tx_chan); rmt_tx_trans_desc_t *t = tx_chan->cur_trans;
encoded_symbols += 1; size_t encoded_symbols = t->transmitted_symbol_num;
} else { if (t->flags.encoding_done) {
encoded_symbols += rmt_encode_check_result(tx_chan, t); rmt_tx_mark_eof(tx_chan);
encoded_symbols += 1;
} else {
encoded_symbols += rmt_encode_check_result(tx_chan, t);
}
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
// tell DMA that we have a new descriptor attached
gdma_append(dma_chan);
} }
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
// tell DMA that we have a new descriptor attached
gdma_append(dma_chan);
} }
return false; return false;
} }

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -17,6 +17,7 @@
#include "esp_rom_gpio.h" #include "esp_rom_gpio.h"
#include "ir_tools.h" #include "ir_tools.h"
#include "driver/rmt.h" #include "driver/rmt.h"
#include "soc/rmt_periph.h"
#define RMT_RX_CHANNEL_ENCODING_START (SOC_RMT_CHANNELS_PER_GROUP-SOC_RMT_TX_CANDIDATES_PER_GROUP) #define RMT_RX_CHANNEL_ENCODING_START (SOC_RMT_CHANNELS_PER_GROUP-SOC_RMT_TX_CANDIDATES_PER_GROUP)
#define RMT_TX_CHANNEL_ENCODING_END (SOC_RMT_TX_CANDIDATES_PER_GROUP-1) #define RMT_TX_CHANNEL_ENCODING_END (SOC_RMT_TX_CANDIDATES_PER_GROUP-1)
@@ -72,8 +73,12 @@ static void rmt_setup_testbench(int tx_channel, int rx_channel, uint32_t flags)
// Routing internal signals by IO Matrix (bind rmt tx and rx signal on the same GPIO) // Routing internal signals by IO Matrix (bind rmt tx and rx signal on the same GPIO)
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[RMT_DATA_IO], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[RMT_DATA_IO], PIN_FUNC_GPIO);
TEST_ESP_OK(gpio_set_direction(RMT_DATA_IO, GPIO_MODE_INPUT_OUTPUT)); TEST_ESP_OK(gpio_set_direction(RMT_DATA_IO, GPIO_MODE_INPUT_OUTPUT));
esp_rom_gpio_connect_out_signal(RMT_DATA_IO, RMT_SIG_OUT0_IDX + tx_channel, 0, 0); if (tx_channel >= 0) {
esp_rom_gpio_connect_in_signal(RMT_DATA_IO, RMT_SIG_IN0_IDX + rx_channel, 0); esp_rom_gpio_connect_out_signal(RMT_DATA_IO, rmt_periph_signals.groups[0].channels[tx_channel].tx_sig, 0, 0);
}
if (rx_channel >= 0) {
esp_rom_gpio_connect_in_signal(RMT_DATA_IO, rmt_periph_signals.groups[0].channels[rx_channel].rx_sig, 0);
}
// install driver // install driver
if (tx_channel >= 0) { if (tx_channel >= 0) {
@@ -161,7 +166,6 @@ TEST_CASE("RMT miscellaneous functions", "[rmt]")
TEST_ASSERT_EQUAL_INT(RMT_BASECLK_XTAL, src_clk); TEST_ASSERT_EQUAL_INT(RMT_BASECLK_XTAL, src_clk);
#endif #endif
TEST_ESP_OK(rmt_set_tx_carrier(channel, 0, 10, 10, 1)); TEST_ESP_OK(rmt_set_tx_carrier(channel, 0, 10, 10, 1));
TEST_ESP_OK(rmt_set_idle_level(channel, 1, 0)); TEST_ESP_OK(rmt_set_idle_level(channel, 1, 0));

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define TEST_RMT_GPIO_NUM_A 0
#define TEST_RMT_GPIO_NUM_B 2

View File

@@ -12,12 +12,13 @@
#include "driver/rmt_tx.h" #include "driver/rmt_tx.h"
#include "driver/rmt_rx.h" #include "driver/rmt_rx.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "test_board.h"
TEST_CASE("rmt channel install & uninstall", "[rmt]") TEST_CASE("rmt channel install & uninstall", "[rmt]")
{ {
rmt_tx_channel_config_t tx_channel_cfg = { rmt_tx_channel_config_t tx_channel_cfg = {
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL, .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, .resolution_hz = 1000000,
.trans_queue_depth = 1, .trans_queue_depth = 1,
@@ -25,7 +26,7 @@ TEST_CASE("rmt channel install & uninstall", "[rmt]")
rmt_channel_handle_t tx_channels[SOC_RMT_TX_CANDIDATES_PER_GROUP] = {}; rmt_channel_handle_t tx_channels[SOC_RMT_TX_CANDIDATES_PER_GROUP] = {};
rmt_rx_channel_config_t rx_channel_cfg = { rmt_rx_channel_config_t rx_channel_cfg = {
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL, .mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = 2, .gpio_num = TEST_RMT_GPIO_NUM_B,
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, .resolution_hz = 1000000,
}; };
@@ -98,3 +99,60 @@ TEST_CASE("rmt channel install & uninstall", "[rmt]")
} }
#endif // SOC_RMT_SUPPORT_DMA #endif // SOC_RMT_SUPPORT_DMA
} }
TEST_CASE("RMT 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));
rmt_rx_channel_config_t rx_channel_cfg = {
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.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));
TEST_ESP_OK(rmt_del_channel(tx_channel));
TEST_ESP_OK(rmt_del_channel(another_tx_channel));
TEST_ESP_OK(rmt_del_channel(rx_channel));
TEST_ESP_OK(rmt_del_channel(another_rx_channel));
}

View File

@@ -16,6 +16,7 @@
#include "esp_timer.h" #include "esp_timer.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "test_util_rmt_encoders.h" #include "test_util_rmt_encoders.h"
#include "test_board.h"
static void IRAM_ATTR test_delay_post_cache_disable(void *args) static void IRAM_ATTR test_delay_post_cache_disable(void *args)
{ {
@@ -29,7 +30,7 @@ static void test_rmt_tx_iram_safe(size_t mem_block_symbols, bool with_dma)
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution) .resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
}; };
printf("install tx channel\r\n"); printf("install tx channel\r\n");
@@ -86,8 +87,6 @@ TEST_CASE("rmt tx iram safe", "[rmt]")
#endif #endif
} }
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;
@@ -121,7 +120,7 @@ static void test_rmt_rx_iram_safe(size_t mem_block_symbols, bool with_dma, rmt_c
.clk_src = clk_src, .clk_src = clk_src,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us .resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = mem_block_symbols, .mem_block_symbols = mem_block_symbols,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.flags.io_loop_back = true, // the GPIO will act like a loopback .flags.io_loop_back = true, // the GPIO will act like a loopback
}; };

View File

@@ -13,6 +13,7 @@
#include "driver/rmt_rx.h" #include "driver/rmt_rx.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "test_util_rmt_encoders.h" #include "test_util_rmt_encoders.h"
#include "test_board.h"
#if CONFIG_RMT_ISR_IRAM_SAFE #if CONFIG_RMT_ISR_IRAM_SAFE
#define TEST_RMT_CALLBACK_ATTR IRAM_ATTR #define TEST_RMT_CALLBACK_ATTR IRAM_ATTR
@@ -42,11 +43,16 @@ static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx
static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt_clock_source_t clk_src) static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt_clock_source_t clk_src)
{ {
uint32_t const test_rx_buffer_symbols = 128;
rmt_symbol_word_t *remote_codes = heap_caps_aligned_calloc(64, test_rx_buffer_symbols, sizeof(rmt_symbol_word_t),
MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(remote_codes);
rmt_rx_channel_config_t rx_channel_cfg = { rmt_rx_channel_config_t rx_channel_cfg = {
.clk_src = clk_src, .clk_src = clk_src,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us .resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = mem_block_symbols, .mem_block_symbols = mem_block_symbols,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.flags.io_loop_back = true, // the GPIO will act like a loopback .flags.io_loop_back = true, // the GPIO will act like a loopback
}; };
@@ -67,7 +73,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, 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,
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.io_loop_back = true, // TX channel and RX channel will bounded to the same GPIO .flags.io_loop_back = true, // TX channel and RX channel will bounded to the same GPIO
}; };
printf("install tx channel\r\n"); printf("install tx channel\r\n");
@@ -86,15 +92,13 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
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 = { rmt_receive_config_t receive_config = {
.signal_range_min_ns = 1250, .signal_range_min_ns = 1250,
.signal_range_max_ns = 12000000, .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, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send NEC frame without carrier\r\n"); printf("send NEC frame without carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0x0440, 0x3003 // address, command 0x0440, 0x3003 // address, command
@@ -102,7 +106,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
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(34, test_user_data.received_symbol_num); TEST_ASSERT_EQUAL(34, test_user_data.received_symbol_num);
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config)); TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send NEC frame without carrier\r\n"); printf("send NEC frame without carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0x0440, 0x3003 // address, command 0x0440, 0x3003 // address, command
@@ -112,7 +116,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
#if SOC_RMT_SUPPORT_RX_PINGPONG #if SOC_RMT_SUPPORT_RX_PINGPONG
// 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, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send customized NEC frame without carrier\r\n"); printf("send customized NEC frame without carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0xFF00, 0xFF00, 0xFF00, 0xFF00 0xFF00, 0xFF00, 0xFF00, 0xFF00
@@ -121,7 +125,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
TEST_ASSERT_EQUAL(66, test_user_data.received_symbol_num); TEST_ASSERT_EQUAL(66, test_user_data.received_symbol_num);
#else #else
// 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, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send customized NEC frame without carrier\r\n"); printf("send customized NEC frame without carrier\r\n");
// the maximum symbols can receive is its memory block capacity // the maximum symbols can receive is its memory block capacity
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
@@ -144,7 +148,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
carrier_cfg.frequency_hz = 25000; carrier_cfg.frequency_hz = 25000;
TEST_ESP_OK(rmt_apply_carrier(rx_channel, &carrier_cfg)); TEST_ESP_OK(rmt_apply_carrier(rx_channel, &carrier_cfg));
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config)); TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send NEC frame with carrier\r\n"); printf("send NEC frame with carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0x0440, 0x3003 // address, command 0x0440, 0x3003 // address, command
@@ -153,7 +157,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
TEST_ASSERT_EQUAL(34, test_user_data.received_symbol_num); TEST_ASSERT_EQUAL(34, test_user_data.received_symbol_num);
#if SOC_RMT_SUPPORT_RX_PINGPONG #if SOC_RMT_SUPPORT_RX_PINGPONG
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config)); TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send customized frame with carrier\r\n"); printf("send customized frame with carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0xFF00, 0xFF00, 0xFF00, 0xFF00 0xFF00, 0xFF00, 0xFF00, 0xFF00
@@ -167,7 +171,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
TEST_ESP_OK(rmt_apply_carrier(rx_channel, NULL)); TEST_ESP_OK(rmt_apply_carrier(rx_channel, NULL));
#endif // SOC_RMT_SUPPORT_RX_DEMODULATION #endif // SOC_RMT_SUPPORT_RX_DEMODULATION
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config)); TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, test_rx_buffer_symbols * sizeof(rmt_symbol_word_t), &receive_config));
printf("send NEC frame without carrier\r\n"); printf("send NEC frame without carrier\r\n");
TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) { TEST_ESP_OK(rmt_transmit(tx_channel, nec_encoder, (uint16_t[]) {
0x0440, 0x3003 // address, command 0x0440, 0x3003 // address, command
@@ -183,6 +187,7 @@ static void test_rmt_rx_nec_carrier(size_t mem_block_symbols, bool with_dma, rmt
TEST_ESP_OK(rmt_del_channel(rx_channel)); TEST_ESP_OK(rmt_del_channel(rx_channel));
TEST_ESP_OK(rmt_del_channel(tx_channel)); TEST_ESP_OK(rmt_del_channel(tx_channel));
TEST_ESP_OK(rmt_del_encoder(nec_encoder)); TEST_ESP_OK(rmt_del_encoder(nec_encoder));
free(remote_codes);
} }
TEST_CASE("rmt rx nec with carrier", "[rmt]") TEST_CASE("rmt rx nec with carrier", "[rmt]")
@@ -196,40 +201,3 @@ TEST_CASE("rmt rx nec with carrier", "[rmt]")
test_rmt_rx_nec_carrier(128, true, RMT_CLK_SRC_DEFAULT); test_rmt_rx_nec_carrier(128, true, RMT_CLK_SRC_DEFAULT);
#endif #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

@@ -13,6 +13,7 @@
#include "esp_timer.h" #include "esp_timer.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "test_util_rmt_encoders.h" #include "test_util_rmt_encoders.h"
#include "test_board.h"
#if CONFIG_RMT_ISR_IRAM_SAFE #if CONFIG_RMT_ISR_IRAM_SAFE
#define TEST_RMT_CALLBACK_ATTR IRAM_ATTR #define TEST_RMT_CALLBACK_ATTR IRAM_ATTR
@@ -27,7 +28,7 @@ TEST_CASE("rmt bytes encoder", "[rmt]")
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us .resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.intr_priority = 3 .intr_priority = 3
}; };
printf("install tx channel\r\n"); printf("install tx channel\r\n");
@@ -89,7 +90,7 @@ static void test_rmt_channel_single_trans(size_t mem_block_symbols, bool with_dm
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution) .resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.intr_priority = 2 .intr_priority = 2
}; };
@@ -144,7 +145,7 @@ static void test_rmt_ping_pong_trans(size_t mem_block_symbols, bool with_dma)
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution) .resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.intr_priority = 1 .intr_priority = 1
}; };
@@ -215,7 +216,6 @@ TEST_CASE("rmt ping-pong transaction", "[rmt]")
#endif #endif
} }
TEST_RMT_CALLBACK_ATTR TEST_RMT_CALLBACK_ATTR
static bool test_rmt_tx_done_cb_check_event_data(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data) static bool test_rmt_tx_done_cb_check_event_data(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data)
{ {
@@ -231,7 +231,7 @@ static void test_rmt_trans_done_event(size_t mem_block_symbols, bool with_dma)
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution) .resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 1, .trans_queue_depth = 1,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.intr_priority = 3 .intr_priority = 3
}; };
@@ -305,7 +305,7 @@ static void test_rmt_loop_trans(size_t mem_block_symbols, bool with_dma)
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution) .resolution_hz = 10000000, // 10MHz, 1 tick = 0.1us (led strip needs a high resolution)
.trans_queue_depth = 4, .trans_queue_depth = 4,
.gpio_num = 0, .gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.intr_priority = 2 .intr_priority = 2
}; };
@@ -365,7 +365,7 @@ TEST_CASE("rmt infinite loop transaction", "[rmt]")
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.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 = TEST_RMT_GPIO_NUM_B,
.trans_queue_depth = 3, .trans_queue_depth = 3,
.intr_priority = 1 .intr_priority = 1
}; };
@@ -444,7 +444,7 @@ static void test_rmt_tx_nec_carrier(size_t mem_block_symbols, bool with_dma)
.clk_src = RMT_CLK_SRC_DEFAULT, .clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us .resolution_hz = 1000000, // 1MHz, 1 tick = 1us
.mem_block_symbols = mem_block_symbols, .mem_block_symbols = mem_block_symbols,
.gpio_num = 2, .gpio_num = TEST_RMT_GPIO_NUM_B,
.trans_queue_depth = 4, .trans_queue_depth = 4,
.flags.with_dma = with_dma, .flags.with_dma = with_dma,
.intr_priority = 3 .intr_priority = 3
@@ -503,7 +503,6 @@ TEST_CASE("rmt tx nec with carrier", "[rmt]")
#endif #endif
} }
TEST_RMT_CALLBACK_ATTR TEST_RMT_CALLBACK_ATTR
static bool test_rmt_tx_done_cb_record_time(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data) static bool test_rmt_tx_done_cb_record_time(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_data)
{ {
@@ -526,7 +525,7 @@ static void test_rmt_multi_channels_trans(size_t channel0_mem_block_symbols, siz
}; };
printf("install tx channels\r\n"); printf("install tx channels\r\n");
rmt_channel_handle_t tx_channels[TEST_RMT_CHANS] = {NULL}; rmt_channel_handle_t tx_channels[TEST_RMT_CHANS] = {NULL};
int gpio_nums[TEST_RMT_CHANS] = {0, 2}; int gpio_nums[TEST_RMT_CHANS] = {TEST_RMT_GPIO_NUM_A, TEST_RMT_GPIO_NUM_B};
size_t mem_blk_syms[TEST_RMT_CHANS] = {channel0_mem_block_symbols, channel1_mem_block_symbols}; size_t mem_blk_syms[TEST_RMT_CHANS] = {channel0_mem_block_symbols, channel1_mem_block_symbols};
bool dma_flags[TEST_RMT_CHANS] = {channel0_with_dma, channel1_with_dma}; bool dma_flags[TEST_RMT_CHANS] = {channel0_with_dma, channel1_with_dma};
for (int i = 0; i < TEST_RMT_CHANS; i++) { for (int i = 0; i < TEST_RMT_CHANS; i++) {
@@ -655,38 +654,3 @@ TEST_CASE("rmt multiple channels transaction", "[rmt]")
test_rmt_multi_channels_trans(1024, SOC_RMT_MEM_WORDS_PER_CHANNEL, true, false); test_rmt_multi_channels_trans(1024, SOC_RMT_MEM_WORDS_PER_CHANNEL, true, false);
#endif #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

@@ -11,12 +11,6 @@
#include "driver/rmt_encoder.h" #include "driver/rmt_encoder.h"
#include "esp_attr.h" #include "esp_attr.h"
#if CONFIG_RMT_ISR_IRAM_SAFE
#define TEST_RMT_ENCODER_ATTR IRAM_ATTR
#else
#define TEST_RMT_ENCODER_ATTR
#endif
typedef struct { typedef struct {
rmt_encoder_t base; rmt_encoder_t base;
rmt_encoder_t *bytes_encoder; rmt_encoder_t *bytes_encoder;
@@ -25,8 +19,7 @@ typedef struct {
rmt_symbol_word_t reset_code; rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t; } rmt_led_strip_encoder_t;
TEST_RMT_ENCODER_ATTR IRAM_ATTR static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{ {
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encode_state_t session_state = RMT_ENCODING_RESET; rmt_encode_state_t session_state = RMT_ENCODING_RESET;
@@ -119,7 +112,7 @@ typedef struct {
int state; int state;
} rmt_nec_protocol_encoder_t; } rmt_nec_protocol_encoder_t;
static size_t rmt_encode_nec_protocol(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) IRAM_ATTR static size_t rmt_encode_nec_protocol(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{ {
rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base); rmt_nec_protocol_encoder_t *nec_encoder = __containerof(encoder, rmt_nec_protocol_encoder_t, base);
rmt_encode_state_t session_state = RMT_ENCODING_RESET; rmt_encode_state_t session_state = RMT_ENCODING_RESET;

View File

@@ -447,11 +447,11 @@ static bool mcp_gdma_rx_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_
if (atomic_compare_exchange_strong(&mcp_gdma->fsm, &expected_fsm, MCP_FSM_IDLE_WAIT)) { if (atomic_compare_exchange_strong(&mcp_gdma->fsm, &expected_fsm, MCP_FSM_IDLE_WAIT)) {
// if the data is in the cache, invalidate, then CPU can see the latest data // if the data is in the cache, invalidate, then CPU can see the latest data
#if MCP_NEEDS_INVALIDATE_DST_CACHE #if MCP_NEEDS_INVALIDATE_DST_CACHE
int write_back_map = CACHE_MAP_L1_DCACHE; int invalidate_map = CACHE_MAP_L1_DCACHE;
if (esp_ptr_external_ram((const void *)trans->memcpy_dst_addr)) { if (esp_ptr_external_ram((const void *)trans->memcpy_dst_addr)) {
write_back_map |= CACHE_MAP_L2_CACHE; invalidate_map |= CACHE_MAP_L2_CACHE;
} }
Cache_Invalidate_Addr(write_back_map, (uint32_t)trans->memcpy_dst_addr, trans->memcpy_size); Cache_Invalidate_Addr(invalidate_map, (uint32_t)trans->memcpy_dst_addr, trans->memcpy_size);
#endif #endif
// invoked callback registered by user // invoked callback registered by user

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,6 +16,7 @@
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "soc/rmt_struct.h" #include "soc/rmt_struct.h"
#include "soc/dport_reg.h"
#include "hal/rmt_types.h" #include "hal/rmt_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -40,6 +41,41 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
uint32_t reg_val = DPORT_READ_PERI_REG(DPORT_PERIP_CLK_EN_REG);
reg_val &= ~DPORT_RMT_CLK_EN;
reg_val |= enable << 9;
DPORT_WRITE_PERI_REG(DPORT_PERIP_CLK_EN_REG, reg_val);
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
DPORT_WRITE_PERI_REG(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);
DPORT_WRITE_PERI_REG(DPORT_PERIP_RST_EN_REG, 0);
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_reset_register(__VA_ARGS__)
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,6 +16,7 @@
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "soc/rmt_struct.h" #include "soc/rmt_struct.h"
#include "soc/system_struct.h"
#include "hal/rmt_types.h" #include "hal/rmt_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -41,6 +42,38 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en0.reg_rmt_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en0.reg_rmt_rst = 1;
SYSTEM.perip_rst_en0.reg_rmt_rst = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_reset_register(__VA_ARGS__)
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -42,6 +42,30 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
PCR.rmt_conf.rmt_clk_en = enable;
}
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
PCR.rmt_conf.rmt_rst_en = 1;
PCR.rmt_conf.rmt_rst_en = 0;
}
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -42,6 +42,30 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
PCR.rmt_conf.rmt_clk_en = enable;
}
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
PCR.rmt_conf.rmt_rst_en = 1;
PCR.rmt_conf.rmt_rst_en = 0;
}
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -76,8 +76,6 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
return HP_SYS_CLKRST_REG_SYSTIMER_CLK_EN; return HP_SYS_CLKRST_REG_SYSTIMER_CLK_EN;
case PERIPH_LEDC_MODULE: case PERIPH_LEDC_MODULE:
return HP_SYS_CLKRST_REG_LEDC_CLK_EN; return HP_SYS_CLKRST_REG_LEDC_CLK_EN;
case PERIPH_RMT_MODULE:
return HP_SYS_CLKRST_REG_RMT_CLK_EN;
case PERIPH_SARADC_MODULE: case PERIPH_SARADC_MODULE:
return HP_SYS_CLKRST_REG_ADC_CLK_EN; return HP_SYS_CLKRST_REG_ADC_CLK_EN;
case PERIPH_PVT_MODULE: case PERIPH_PVT_MODULE:
@@ -147,8 +145,6 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
return HP_SYS_CLKRST_REG_RST_EN_I2C0; return HP_SYS_CLKRST_REG_RST_EN_I2C0;
case PERIPH_I2C1_MODULE: case PERIPH_I2C1_MODULE:
return HP_SYS_CLKRST_REG_RST_EN_I2C1; return HP_SYS_CLKRST_REG_RST_EN_I2C1;
case PERIPH_RMT_MODULE:
return HP_SYS_CLKRST_REG_RST_EN_RMT;
case PERIPH_TWAI0_MODULE: case PERIPH_TWAI0_MODULE:
return HP_SYS_CLKRST_REG_RST_EN_CAN0; return HP_SYS_CLKRST_REG_RST_EN_CAN0;
case PERIPH_TWAI1_MODULE: case PERIPH_TWAI1_MODULE:
@@ -253,7 +249,6 @@ static inline uint32_t periph_ll_get_clk_en_reg(periph_module_t periph)
return HP_SYS_CLKRST_PERI_CLK_CTRL119_REG; return HP_SYS_CLKRST_PERI_CLK_CTRL119_REG;
case PERIPH_SYSTIMER_MODULE: case PERIPH_SYSTIMER_MODULE:
case PERIPH_LEDC_MODULE: case PERIPH_LEDC_MODULE:
case PERIPH_RMT_MODULE:
return HP_SYS_CLKRST_PERI_CLK_CTRL21_REG; return HP_SYS_CLKRST_PERI_CLK_CTRL21_REG;
case PERIPH_SARADC_MODULE: case PERIPH_SARADC_MODULE:
return HP_SYS_CLKRST_PERI_CLK_CTRL22_REG; return HP_SYS_CLKRST_PERI_CLK_CTRL22_REG;
@@ -299,7 +294,6 @@ static inline uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
case PERIPH_I2C0_MODULE: case PERIPH_I2C0_MODULE:
case PERIPH_I2C1_MODULE: case PERIPH_I2C1_MODULE:
return HP_SYS_CLKRST_HP_RST_EN1_REG; return HP_SYS_CLKRST_HP_RST_EN1_REG;
case PERIPH_RMT_MODULE:
case PERIPH_TWAI0_MODULE: case PERIPH_TWAI0_MODULE:
case PERIPH_TWAI1_MODULE: case PERIPH_TWAI1_MODULE:
case PERIPH_TWAI2_MODULE: case PERIPH_TWAI2_MODULE:

View File

@@ -0,0 +1,923 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @note TX and RX channels are index from 0 in the LL driver, i.e. tx_channel = [0,3], rx_channel = [0,3]
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "hal/misc.h"
#include "hal/assert.h"
#include "hal/rmt_types.h"
#include "soc/rmt_struct.h"
#include "soc/hp_sys_clkrst_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define RMT_LL_EVENT_TX_DONE(channel) (1 << (channel))
#define RMT_LL_EVENT_TX_THRES(channel) (1 << ((channel) + 8))
#define RMT_LL_EVENT_TX_LOOP_END(channel) (1 << ((channel) + 12))
#define RMT_LL_EVENT_TX_ERROR(channel) (1 << ((channel) + 4))
#define RMT_LL_EVENT_RX_DONE(channel) (1 << ((channel) + 16))
#define RMT_LL_EVENT_RX_THRES(channel) (1 << ((channel) + 24))
#define RMT_LL_EVENT_RX_ERROR(channel) (1 << ((channel) + 20))
#define RMT_LL_EVENT_TX_MASK(channel) (RMT_LL_EVENT_TX_DONE(channel) | RMT_LL_EVENT_TX_THRES(channel) | RMT_LL_EVENT_TX_LOOP_END(channel))
#define RMT_LL_EVENT_RX_MASK(channel) (RMT_LL_EVENT_RX_DONE(channel) | RMT_LL_EVENT_RX_THRES(channel))
#define RMT_LL_MAX_LOOP_COUNT_PER_BATCH 1023
#define RMT_LL_MAX_FILTER_VALUE 255
#define RMT_LL_MAX_IDLE_VALUE 32767
typedef enum {
RMT_LL_MEM_OWNER_SW = 0,
RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
HP_SYS_CLKRST.soc_clk_ctrl2.reg_rmt_sys_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_rmt = 1;
HP_SYS_CLKRST.hp_rst_en1.reg_rst_en_rmt = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_reset_register(__VA_ARGS__)
/**
* @brief Set clock source and divider for RMT channel group
*
* @param dev Peripheral instance address
* @param channel not used as clock source is set for all channels
* @param src Clock source
* @param divider_integral Integral part of the divider
* @param divider_denominator Denominator part of the divider
* @param divider_numerator Numerator part of the divider
*/
static inline void rmt_ll_set_group_clock_src(rmt_dev_t *dev, uint32_t channel, rmt_clock_source_t src,
uint32_t divider_integral, uint32_t divider_denominator, uint32_t divider_numerator)
{
(void)dev;
// Formula: rmt_sclk = module_clock_src / (1 + div_num + div_a / div_b)
(void)channel; // the source clock is set for all channels
HAL_ASSERT(divider_integral >= 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl22, reg_rmt_clk_div_num, divider_integral - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl22, reg_rmt_clk_div_numerator, divider_numerator);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl22, reg_rmt_clk_div_denominator, divider_denominator);
switch (src) {
case RMT_CLK_SRC_PLL_F80M:
HP_SYS_CLKRST.peri_clk_ctrl22.reg_rmt_clk_src_sel = 2;
break;
case RMT_CLK_SRC_RC_FAST:
HP_SYS_CLKRST.peri_clk_ctrl22.reg_rmt_clk_src_sel = 1;
break;
case RMT_CLK_SRC_XTAL:
HP_SYS_CLKRST.peri_clk_ctrl22.reg_rmt_clk_src_sel = 0;
break;
default:
HAL_ASSERT(false && "unsupported RMT clock source");
break;
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_set_group_clock_src(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_set_group_clock_src(__VA_ARGS__)
/**
* @brief Enable RMT peripheral source clock
*
* @param dev Peripheral instance address
* @param en True to enable, False to disable
*/
static inline void rmt_ll_enable_group_clock(rmt_dev_t *dev, bool en)
{
(void)dev;
HP_SYS_CLKRST.peri_clk_ctrl22.reg_rmt_clk_en = en;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_group_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_group_clock(__VA_ARGS__)
/**
* @brief Enable clock gate for register and memory
*
* @param dev Peripheral instance address
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_enable_periph_clock(rmt_dev_t *dev, bool enable)
{
dev->sys_conf.clk_en = enable; // register clock gating
dev->sys_conf.mem_clk_force_on = enable; // memory clock gating
}
/**
* @brief Power down memory
*
* @param dev Peripheral instance address
* @param enable True to power down, False to power up
*/
static inline void rmt_ll_power_down_mem(rmt_dev_t *dev, bool enable)
{
dev->sys_conf.mem_force_pu = !enable;
dev->sys_conf.mem_force_pd = enable;
}
/**
* @brief Enable APB accessing RMT memory in nonfifo mode
*
* @param dev Peripheral instance address
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_enable_mem_access_nonfifo(rmt_dev_t *dev, bool enable)
{
dev->sys_conf.apb_fifo_mask = enable;
}
////////////////////////////////////////TX Channel Specific/////////////////////////////////////////////////////////////
/**
* @brief Reset clock divider for TX channels by mask
*
* @param dev Peripheral instance address
* @param channel_mask Mask of TX channels
*/
static inline void rmt_ll_tx_reset_channels_clock_div(rmt_dev_t *dev, uint32_t channel_mask)
{
// write 1 to reset
dev->ref_cnt_rst.val |= channel_mask & 0x0F;
}
/**
* @brief Set TX channel clock divider
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param div Division value
*/
static inline void rmt_ll_tx_set_channel_clock_div(rmt_dev_t *dev, uint32_t channel, uint32_t div)
{
HAL_ASSERT(div >= 1 && div <= 256 && "divider out of range");
// limit the maximum divider to 256
if (div >= 256) {
div = 0; // 0 means 256 division
}
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chnconf0[channel], div_cnt_chn, div);
}
/**
* @brief Reset RMT reading pointer for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
{
dev->chnconf0[channel].mem_rd_rst_chn = 1;
dev->chnconf0[channel].mem_rd_rst_chn = 0;
dev->chnconf0[channel].apb_mem_rst_chn = 1;
dev->chnconf0[channel].apb_mem_rst_chn = 0;
}
/**
* @brief Enable DMA access for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_tx_enable_dma(rmt_dev_t *dev, uint32_t channel, bool enable)
{
HAL_ASSERT(channel == 3 && "only TX channel 3 has DMA ability");
dev->chnconf0[channel].dma_access_en_chn = enable;
}
/**
* @brief Start transmitting for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel)
{
// update other configuration registers before start transmitting
dev->chnconf0[channel].conf_update_chn = 1;
dev->chnconf0[channel].tx_start_chn = 1;
}
/**
* @brief Stop transmitting for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel)
{
dev->chnconf0[channel].tx_stop_chn = 1;
// stop won't take place until configurations updated
dev->chnconf0[channel].conf_update_chn = 1;
}
/**
* @brief Set memory block number for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param block_num memory block number
*/
static inline void rmt_ll_tx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, uint8_t block_num)
{
dev->chnconf0[channel].mem_size_chn = block_num;
}
/**
* @brief Enable TX wrap
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_tx_enable_wrap(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chnconf0[channel].mem_tx_wrap_en_chn = enable;
}
/**
* @brief Enable transmitting in a loop
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chnconf0[channel].tx_conti_mode_chn = enable;
}
/**
* @brief Set loop count for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param count TX loop count
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_set_loop_count(rmt_dev_t *dev, uint32_t channel, uint32_t count)
{
HAL_ASSERT(count <= RMT_LL_MAX_LOOP_COUNT_PER_BATCH && "loop count out of range");
dev->chn_tx_lim[channel].tx_loop_num_chn = count;
}
/**
* @brief Reset loop count for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_reset_loop_count(rmt_dev_t *dev, uint32_t channel)
{
dev->chn_tx_lim[channel].loop_count_reset_chn = 1;
dev->chn_tx_lim[channel].loop_count_reset_chn = 0;
}
/**
* @brief Enable loop count for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_enable_loop_count(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chn_tx_lim[channel].tx_loop_cnt_en_chn = enable;
}
/**
* @brief Enable loop stop at count value automatically
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_enable_loop_autostop(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chn_tx_lim[channel].loop_stop_en_chn = enable;
}
/**
* @brief Enable transmit multiple channels synchronously
*
* @param dev Peripheral instance address
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_tx_enable_sync(rmt_dev_t *dev, bool enable)
{
dev->tx_sim.tx_sim_en = enable;
}
/**
* @brief Clear the TX channels synchronous group
*
* @param dev Peripheral instance address
*/
static inline void rmt_ll_tx_clear_sync_group(rmt_dev_t *dev)
{
dev->tx_sim.val &= ~(0x0F);
}
/**
* @brief Add TX channels to the synchronous group
*
* @param dev Peripheral instance address
* @param channel_mask Mask of TX channels to be added to the synchronous group
*/
static inline void rmt_ll_tx_sync_group_add_channels(rmt_dev_t *dev, uint32_t channel_mask)
{
dev->tx_sim.val |= (channel_mask & 0x0F);
}
/**
* @brief Remove TX channels from the synchronous group
*
* @param dev Peripheral instance address
* @param channel_mask Mask of TX channels to be removed from the synchronous group
*/
static inline void rmt_ll_tx_sync_group_remove_channels(rmt_dev_t *dev, uint32_t channel_mask)
{
dev->tx_sim.val &= ~channel_mask;
}
/**
* @brief Fix the output level when TX channel is in IDLE state
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param level IDLE level (1 => high, 0 => low)
* @param enable True to fix the IDLE level, otherwise the IDLE level is determined by EOF encoder
*/
__attribute__((always_inline))
static inline void rmt_ll_tx_fix_idle_level(rmt_dev_t *dev, uint32_t channel, uint8_t level, bool enable)
{
dev->chnconf0[channel].idle_out_en_chn = enable;
dev->chnconf0[channel].idle_out_lv_chn = level;
}
/**
* @brief Set the amount of RMT symbols that can trigger the limitation interrupt
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param limit Specify the number of symbols
*/
static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
{
dev->chn_tx_lim[channel].tx_lim_chn = limit;
}
/**
* @brief Set high and low duration of carrier signal
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param high_ticks Duration of high level
* @param low_ticks Duration of low level
*/
static inline void rmt_ll_tx_set_carrier_high_low_ticks(rmt_dev_t *dev, uint32_t channel, uint32_t high_ticks, uint32_t low_ticks)
{
HAL_ASSERT(high_ticks >= 1 && high_ticks <= 65536 && low_ticks >= 1 && low_ticks <= 65536 && "out of range high/low ticks");
// ticks=0 means 65536 in hardware
if (high_ticks >= 65536) {
high_ticks = 0;
}
if (low_ticks >= 65536) {
low_ticks = 0;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chncarrier_duty[channel], carrier_high_chn, high_ticks);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chncarrier_duty[channel], carrier_low_chn, low_ticks);
}
/**
* @brief Enable modulating carrier signal to TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_tx_enable_carrier_modulation(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chnconf0[channel].carrier_en_chn = enable;
}
/**
* @brief Set on high or low to modulate the carrier signal
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param level Which level to modulate on (0=>low level, 1=>high level)
*/
static inline void rmt_ll_tx_set_carrier_level(rmt_dev_t *dev, uint32_t channel, uint8_t level)
{
dev->chnconf0[channel].carrier_out_lv_chn = level;
}
/**
* @brief Enable to always output carrier signal, regardless of a valid data transmission
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @param enable True to output carrier signal in all RMT state, False to only ouput carrier signal for effective data
*/
static inline void rmt_ll_tx_enable_carrier_always_on(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chnconf0[channel].carrier_eff_en_chn = !enable;
}
////////////////////////////////////////RX Channel Specific/////////////////////////////////////////////////////////////
/**
* @brief Reset clock divider for RX channels by mask
*
* @param dev Peripheral instance address
* @param channel_mask Mask of RX channels
*/
static inline void rmt_ll_rx_reset_channels_clock_div(rmt_dev_t *dev, uint32_t channel_mask)
{
dev->ref_cnt_rst.val |= ((channel_mask & 0x0F) << 4);
}
/**
* @brief Set RX channel clock divider
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param div Division value
*/
static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t channel, uint32_t div)
{
HAL_ASSERT(div >= 1 && div <= 256 && "divider out of range");
// limit the maximum divider to 256
if (div >= 256) {
div = 0; // 0 means 256 division
}
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chmconf[channel].conf0, div_cnt_chm, div);
}
/**
* @brief Reset RMT writing pointer for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
*/
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 = 0;
dev->chmconf[channel].conf1.apb_mem_rst_chm = 1;
dev->chmconf[channel].conf1.apb_mem_rst_chm = 0;
}
/**
* @brief Enable DMA access for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_rx_enable_dma(rmt_dev_t *dev, uint32_t channel, bool enable)
{
HAL_ASSERT(channel == 3 && "only RX channel 3 has DMA ability");
dev->chmconf[channel].conf0.dma_access_en_chm = enable;
}
/**
* @brief Enable receiving for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_enable(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chmconf[channel].conf1.rx_en_chm = enable;
// rx won't be enabled until configurations updated
dev->chmconf[channel].conf1.conf_update_chm = 1;
}
/**
* @brief Set memory block number for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param block_num memory block number
*/
static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, uint8_t block_num)
{
dev->chmconf[channel].conf0.mem_size_chm = block_num;
}
/**
* @brief Set the time length for RX channel before going into IDLE state
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param thres Time length threshold
*/
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;
}
/**
* @brief Set RMT memory owner for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param owner Memory owner
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt_ll_mem_owner_t owner)
{
dev->chmconf[channel].conf1.mem_owner_chm = owner;
}
/**
* @brief Enable filter for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX chanenl number
* @param enable True to enable, False to disable
*/
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;
}
/**
* @brief Set RX channel filter threshold (i.e. the maximum width of one pulse signal that would be treated as a noise)
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param thres Filter threshold
*/
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);
}
/**
* @brief Get RMT memory write cursor offset
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @return writer offset
*/
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_memory_writer_offset(rmt_dev_t *dev, uint32_t channel)
{
return dev->chmstatus[channel].mem_waddr_ex_chm - (channel + 4) * 48;
}
/**
* @brief Set the amount of RMT symbols that can trigger the limitation interrupt
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param limit Specify the number of symbols
*/
static inline void rmt_ll_rx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
{
dev->chm_rx_lim[channel].rx_lim_chm = limit;
}
/**
* @brief Set high and low duration of carrier signal
*
* @param dev dev Peripheral instance address
* @param channel RMT TX channel number
* @param high_ticks Duration of high level
* @param low_ticks Duration of low level
*/
static inline void rmt_ll_rx_set_carrier_high_low_ticks(rmt_dev_t *dev, uint32_t channel, uint32_t high_ticks, uint32_t low_ticks)
{
HAL_ASSERT(high_ticks >= 1 && high_ticks <= 65536 && low_ticks >= 1 && low_ticks <= 65536 && "out of range high/low ticks");
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chm_rx_carrier_rm[channel], carrier_high_thres_chm, high_ticks - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->chm_rx_carrier_rm[channel], carrier_low_thres_chm, low_ticks - 1);
}
/**
* @brief Enable demodulating the carrier on RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_rx_enable_carrier_demodulation(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chmconf[channel].conf0.carrier_en_chm = enable;
}
/**
* @brief Set on high or low to demodulate the carrier signal
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param level Which level to demodulate (0=>low level, 1=>high level)
*/
static inline void rmt_ll_rx_set_carrier_level(rmt_dev_t *dev, uint32_t channel, uint8_t level)
{
dev->chmconf[channel].conf0.carrier_out_lv_chm = level;
}
/**
* @brief Enable RX wrap
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @param enable True to enable, False to disable
*/
static inline void rmt_ll_rx_enable_wrap(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->chmconf[channel].conf1.mem_rx_wrap_en_chm = enable;
}
//////////////////////////////////////////Interrupt Specific////////////////////////////////////////////////////////////
/**
* @brief Enable RMT interrupt for specific event mask
*
* @param dev Peripheral instance address
* @param mask Event mask
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable)
{
if (enable) {
dev->int_ena.val |= mask;
} else {
dev->int_ena.val &= ~mask;
}
}
/**
* @brief Clear RMT interrupt status by mask
*
* @param dev Peripheral instance address
* @param mask Interupt status mask
*/
__attribute__((always_inline))
static inline void rmt_ll_clear_interrupt_status(rmt_dev_t *dev, uint32_t mask)
{
dev->int_clr.val = mask;
}
/**
* @brief Get interrupt status register address
*
* @param dev Peripheral instance address
* @return Register address
*/
static inline volatile void *rmt_ll_get_interrupt_status_reg(rmt_dev_t *dev)
{
return &dev->int_st;
}
/**
* @brief Get interrupt status for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @return Interrupt status
*/
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_interrupt_status(rmt_dev_t *dev, uint32_t channel)
{
return dev->int_st.val & RMT_LL_EVENT_TX_MASK(channel);
}
/**
* @brief Get interrupt raw status for TX channel
*
* @param dev Peripheral instance address
* @param channel RMT TX channel number
* @return Interrupt raw status
*/
static inline uint32_t rmt_ll_tx_get_interrupt_status_raw(rmt_dev_t *dev, uint32_t channel)
{
return dev->int_raw.val & (RMT_LL_EVENT_TX_MASK(channel) | RMT_LL_EVENT_TX_ERROR(channel));
}
/**
* @brief Get interrupt raw status for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @return Interrupt raw status
*/
static inline uint32_t rmt_ll_rx_get_interrupt_status_raw(rmt_dev_t *dev, uint32_t channel)
{
return dev->int_raw.val & (RMT_LL_EVENT_RX_MASK(channel) | RMT_LL_EVENT_RX_ERROR(channel));
}
/**
* @brief Get interrupt status for RX channel
*
* @param dev Peripheral instance address
* @param channel RMT RX channel number
* @return Interrupt status
*/
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_interrupt_status(rmt_dev_t *dev, uint32_t channel)
{
return dev->int_st.val & RMT_LL_EVENT_RX_MASK(channel);
}
//////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
/////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
/////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_status_word(rmt_dev_t *dev, uint32_t channel)
{
return dev->chnstatus[channel].val;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_status_word(rmt_dev_t *dev, uint32_t channel)
{
return dev->chmstatus[channel].val;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_channel_clock_div(rmt_dev_t *dev, uint32_t channel)
{
uint32_t div = HAL_FORCE_READ_U32_REG_FIELD(dev->chnconf0[channel], div_cnt_chn);
return div == 0 ? 256 : div;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_channel_clock_div(rmt_dev_t *dev, uint32_t channel)
{
uint32_t div = HAL_FORCE_READ_U32_REG_FIELD(dev->chmconf[channel].conf0, div_cnt_chm);
return div == 0 ? 256 : div;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_idle_thres(rmt_dev_t *dev, uint32_t channel)
{
return dev->chmconf[channel].conf0.idle_thres_chm;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_mem_blocks(rmt_dev_t *dev, uint32_t channel)
{
return dev->chnconf0[channel].mem_size_chn;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_mem_blocks(rmt_dev_t *dev, uint32_t channel)
{
return dev->chmconf[channel].conf0.mem_size_chm;
}
__attribute__((always_inline))
static inline bool rmt_ll_tx_is_loop_enabled(rmt_dev_t *dev, uint32_t channel)
{
return dev->chnconf0[channel].tx_conti_mode_chn;
}
__attribute__((always_inline))
static inline rmt_clock_source_t rmt_ll_get_group_clock_src(rmt_dev_t *dev, uint32_t channel)
{
rmt_clock_source_t clk_src = RMT_CLK_SRC_PLL_F80M;
switch (HP_SYS_CLKRST.peri_clk_ctrl22.reg_rmt_clk_src_sel) {
case 0:
clk_src = RMT_CLK_SRC_XTAL;
break;
case 1:
clk_src = RMT_CLK_SRC_RC_FAST;
break;
case 2:
clk_src = RMT_CLK_SRC_PLL_F80M;
break;
}
return clk_src;
}
__attribute__((always_inline))
static inline bool rmt_ll_tx_is_idle_enabled(rmt_dev_t *dev, uint32_t channel)
{
return dev->chnconf0[channel].idle_out_en_chn;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_idle_level(rmt_dev_t *dev, uint32_t channel)
{
return dev->chnconf0[channel].idle_out_lv_chn;
}
static inline bool rmt_ll_is_mem_powered_down(rmt_dev_t *dev)
{
// the RTC domain can also power down RMT memory
// so it's probably not enough to detect whether it's powered down or not
// mem_force_pd has higher priority than mem_force_pu
return (dev->sys_conf.mem_force_pd) || !(dev->sys_conf.mem_force_pu);
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_mem_owner(rmt_dev_t *dev, uint32_t channel)
{
return dev->chmconf[channel].conf1.mem_owner_chm;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_rx_get_limit(rmt_dev_t *dev, uint32_t channel)
{
return dev->chm_rx_lim[channel].rx_lim_chm;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_tx_end_interrupt_status(rmt_dev_t *dev)
{
return dev->int_st.val & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_rx_end_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 16) & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_tx_err_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 4) & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_rx_err_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 20) & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 8) & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_rx_thres_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 24) & 0x0F;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_tx_loop_interrupt_status(rmt_dev_t *dev)
{
return (dev->int_st.val >> 12) & 0x0F;
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,13 +16,13 @@
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "soc/rmt_struct.h" #include "soc/rmt_struct.h"
#include "soc/system_reg.h"
#include "hal/rmt_types.h" #include "hal/rmt_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#define RMT_LL_EVENT_TX_DONE(channel) (1 << ((channel) * 3)) #define RMT_LL_EVENT_TX_DONE(channel) (1 << ((channel) * 3))
#define RMT_LL_EVENT_TX_THRES(channel) (1 << ((channel) + 12)) #define RMT_LL_EVENT_TX_THRES(channel) (1 << ((channel) + 12))
#define RMT_LL_EVENT_TX_LOOP_END(channel) (1 << ((channel) + 16)) #define RMT_LL_EVENT_TX_LOOP_END(channel) (1 << ((channel) + 16))
@@ -42,6 +42,41 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
uint32_t reg_val = READ_PERI_REG(DPORT_PERIP_CLK_EN0_REG);
reg_val &= ~DPORT_RMT_CLK_EN_M;
reg_val |= enable << DPORT_RMT_CLK_EN_S;
WRITE_PERI_REG(DPORT_PERIP_CLK_EN0_REG, reg_val);
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
WRITE_PERI_REG(DPORT_PERIP_RST_EN0_REG, DPORT_RMT_RST_M);
WRITE_PERI_REG(DPORT_PERIP_RST_EN0_REG, 0);
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_reset_register(__VA_ARGS__)
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -16,6 +16,7 @@
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "soc/rmt_struct.h" #include "soc/rmt_struct.h"
#include "soc/system_struct.h"
#include "hal/rmt_types.h" #include "hal/rmt_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@@ -41,6 +42,38 @@ typedef enum {
RMT_LL_MEM_OWNER_HW = 1, RMT_LL_MEM_OWNER_HW = 1,
} rmt_ll_mem_owner_t; } rmt_ll_mem_owner_t;
/**
* @brief Enable the bus clock for RMT module
*
* @param group_id Group ID
* @param enable true to enable, false to disable
*/
static inline void rmt_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
SYSTEM.perip_clk_en0.rmt_clk_en = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_enable_bus_clock(__VA_ARGS__)
/**
* @brief Reset the RMT module
*
* @param group_id Group ID
*/
static inline void rmt_ll_reset_register(int group_id)
{
(void)group_id;
SYSTEM.perip_rst_en0.rmt_rst = 1;
SYSTEM.perip_rst_en0.rmt_rst = 0;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define rmt_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; rmt_ll_reset_register(__VA_ARGS__)
/** /**
* @brief Enable clock gate for register and memory * @brief Enable clock gate for register and memory
* *

View File

@@ -14,7 +14,6 @@ void rmt_hal_init(rmt_hal_context_t *hal)
rmt_ll_enable_mem_access_nonfifo(hal->regs, true); // APB access the RMTMEM in nonfifo mode rmt_ll_enable_mem_access_nonfifo(hal->regs, true); // APB access the RMTMEM in nonfifo mode
rmt_ll_enable_interrupt(hal->regs, UINT32_MAX, false); // disable all interupt events rmt_ll_enable_interrupt(hal->regs, UINT32_MAX, false); // disable all interupt events
rmt_ll_clear_interrupt_status(hal->regs, UINT32_MAX); // clear all pending events rmt_ll_clear_interrupt_status(hal->regs, UINT32_MAX); // clear all pending events
rmt_ll_enable_group_clock(hal->regs, true); // enable clock source
#if SOC_RMT_SUPPORT_TX_SYNCHRO #if SOC_RMT_SUPPORT_TX_SYNCHRO
rmt_ll_tx_clear_sync_group(hal->regs); rmt_ll_tx_clear_sync_group(hal->regs);
#endif // SOC_RMT_SUPPORT_TX_SYNCHRO #endif // SOC_RMT_SUPPORT_TX_SYNCHRO
@@ -25,7 +24,6 @@ void rmt_hal_deinit(rmt_hal_context_t *hal)
rmt_ll_enable_interrupt(hal->regs, UINT32_MAX, false); // disable all interupt events rmt_ll_enable_interrupt(hal->regs, UINT32_MAX, false); // disable all interupt events
rmt_ll_clear_interrupt_status(hal->regs, UINT32_MAX); // clear all pending events rmt_ll_clear_interrupt_status(hal->regs, UINT32_MAX); // clear all pending events
rmt_ll_power_down_mem(hal->regs, true); // turn off RMTMEM power domain rmt_ll_power_down_mem(hal->regs, true); // turn off RMTMEM power domain
rmt_ll_enable_group_clock(hal->regs, false); // disable clock source
hal->regs = NULL; hal->regs = NULL;
} }

View File

@@ -55,6 +55,10 @@ config SOC_RTC_MEM_SUPPORTED
bool bool
default y default y
config SOC_RMT_SUPPORTED
bool
default y
config SOC_I2C_SUPPORTED config SOC_I2C_SUPPORTED
bool bool
default y default y
@@ -489,15 +493,15 @@ config SOC_RMT_GROUPS
config SOC_RMT_TX_CANDIDATES_PER_GROUP config SOC_RMT_TX_CANDIDATES_PER_GROUP
int int
default 2 default 4
config SOC_RMT_RX_CANDIDATES_PER_GROUP config SOC_RMT_RX_CANDIDATES_PER_GROUP
int int
default 2 default 4
config SOC_RMT_CHANNELS_PER_GROUP config SOC_RMT_CHANNELS_PER_GROUP
int int
default 4 default 8
config SOC_RMT_MEM_WORDS_PER_CHANNEL config SOC_RMT_MEM_WORDS_PER_CHANNEL
int int
@@ -515,6 +519,14 @@ config SOC_RMT_SUPPORT_TX_ASYNC_STOP
bool bool
default y default y
config SOC_RMT_SUPPORT_TX_LOOP_COUNT
bool
default y
config SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP
bool
default y
config SOC_RMT_SUPPORT_TX_SYNCHRO config SOC_RMT_SUPPORT_TX_SYNCHRO
bool bool
default y default y
@@ -527,7 +539,7 @@ config SOC_RMT_SUPPORT_XTAL
bool bool
default y default y
config SOC_RMT_SUPPORT_RC_FAST config SOC_RMT_SUPPORT_DMA
bool bool
default y default y

View File

@@ -213,6 +213,42 @@ typedef enum {
//////////////////////////////////////////////////RMT/////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////RMT///////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of RMT
*/
#if SOC_CLK_TREE_SUPPORTED
#define SOC_RMT_CLKS {SOC_MOD_CLK_PLL_F80M, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_XTAL}
#else
#define SOC_RMT_CLKS {SOC_MOD_CLK_XTAL}
#endif
/**
* @brief Type of RMT clock source
*/
typedef enum {
RMT_CLK_SRC_PLL_F80M = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M as the source clock */
RMT_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */
RMT_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
#if SOC_CLK_TREE_SUPPORTED
RMT_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M as the default choice */
#else
RMT_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the default choice */
#endif
} soc_periph_rmt_clk_src_t;
/**
* @brief Type of RMT clock source, reserved for the legacy RMT driver
*/
typedef enum {
RMT_BASECLK_PLL_F80M = SOC_MOD_CLK_PLL_F80M, /*!< RMT source clock is PLL_F80M */
RMT_BASECLK_XTAL = SOC_MOD_CLK_XTAL, /*!< RMT source clock is XTAL */
#if SOC_CLK_TREE_SUPPORTED
RMT_BASECLK_DEFAULT = SOC_MOD_CLK_PLL_F80M, /*!< RMT source clock default choice is PLL_F80M */
#else
RMT_BASECLK_DEFAULT = SOC_MOD_CLK_XTAL, /*!< RMT source clock default choice is XTAL */
#endif
} soc_periph_rmt_clk_src_legacy_t;
//////////////////////////////////////////////////Temp Sensor/////////////////////////////////////////////////////////// //////////////////////////////////////////////////Temp Sensor///////////////////////////////////////////////////////////
///////////////////////////////////////////////////UART///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////UART/////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,7 @@
#define SOC_RTC_FAST_MEM_SUPPORTED 1 #define SOC_RTC_FAST_MEM_SUPPORTED 1
#define SOC_RTC_MEM_SUPPORTED 1 #define SOC_RTC_MEM_SUPPORTED 1
// #define SOC_I2S_SUPPORTED 1 //TODO: IDF-6508 // #define SOC_I2S_SUPPORTED 1 //TODO: IDF-6508
// #define SOC_RMT_SUPPORTED 1 //TODO: IDF-7476 #define SOC_RMT_SUPPORTED 1
// #define SOC_SDM_SUPPORTED 1 //TODO: IDF-7551 // #define SOC_SDM_SUPPORTED 1 //TODO: IDF-7551
// #define SOC_GPSPI_SUPPORTED 1 //TODO: IDF-7502, TODO: IDF-7503 // #define SOC_GPSPI_SUPPORTED 1 //TODO: IDF-7502, TODO: IDF-7503
// #define SOC_LEDC_SUPPORTED 1 //TODO: IDF-6510 // #define SOC_LEDC_SUPPORTED 1 //TODO: IDF-6510
@@ -271,19 +271,20 @@
/*--------------------------- RMT CAPS ---------------------------------------*/ /*--------------------------- RMT CAPS ---------------------------------------*/
#define SOC_RMT_GROUPS 1U /*!< One RMT group */ #define SOC_RMT_GROUPS 1U /*!< One RMT group */
#define SOC_RMT_TX_CANDIDATES_PER_GROUP 2 /*!< Number of channels that capable of Transmit */ #define SOC_RMT_TX_CANDIDATES_PER_GROUP 4 /*!< Number of channels that capable of Transmit in each group */
#define SOC_RMT_RX_CANDIDATES_PER_GROUP 2 /*!< Number of channels that capable of Receive */ #define SOC_RMT_RX_CANDIDATES_PER_GROUP 4 /*!< Number of channels that capable of Receive in each group */
#define SOC_RMT_CHANNELS_PER_GROUP 4 /*!< Total 4 channels */ #define SOC_RMT_CHANNELS_PER_GROUP 8 /*!< Total 8 channels */
#define SOC_RMT_MEM_WORDS_PER_CHANNEL 48 /*!< Each channel owns 48 words memory (1 word = 4 Bytes) */ #define SOC_RMT_MEM_WORDS_PER_CHANNEL 48 /*!< Each channel owns 48 words memory (1 word = 4 Bytes) */
#define SOC_RMT_SUPPORT_RX_PINGPONG 1 /*!< Support Ping-Pong mode on RX path */ #define SOC_RMT_SUPPORT_RX_PINGPONG 1 /*!< Support Ping-Pong mode on RX path */
#define SOC_RMT_SUPPORT_RX_DEMODULATION 1 /*!< Support signal demodulation on RX path (i.e. remove carrier) */ #define SOC_RMT_SUPPORT_RX_DEMODULATION 1 /*!< Support signal demodulation on RX path (i.e. remove carrier) */
#define SOC_RMT_SUPPORT_TX_ASYNC_STOP 1 /*!< Support stop transmission asynchronously */ #define SOC_RMT_SUPPORT_TX_ASYNC_STOP 1 /*!< Support stop transmission asynchronously */
// #define SOC_RMT_SUPPORT_TX_LOOP_COUNT 1 /*!< Support transmit specified number of cycles in loop mode */ #define SOC_RMT_SUPPORT_TX_LOOP_COUNT 1 /*!< Support transmit specified number of cycles in loop mode */
// #define SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP 1 /*!< Hardware support of auto-stop in loop mode */ //TODO: IDF-7476 #define SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP 1 /*!< Hardware support of auto-stop in loop mode */
#define SOC_RMT_SUPPORT_TX_SYNCHRO 1 /*!< Support coordinate a group of TX channels to start simultaneously */ #define SOC_RMT_SUPPORT_TX_SYNCHRO 1 /*!< Support coordinate a group of TX channels to start simultaneously */
#define SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY 1 /*!< TX carrier can be modulated to data phase only */ #define SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY 1 /*!< TX carrier can be modulated to data phase only */
#define SOC_RMT_SUPPORT_XTAL 1 /*!< Support set XTAL clock as the RMT clock source */ #define SOC_RMT_SUPPORT_XTAL 1 /*!< Support set XTAL clock as the RMT clock source */
#define SOC_RMT_SUPPORT_RC_FAST 1 /*!< Support set RC_FAST as the RMT clock source */ // #define SOC_RMT_SUPPORT_RC_FAST 1 /*!< Support set RC_FAST clock as the RMT clock source */
#define SOC_RMT_SUPPORT_DMA 1 /*!< RMT peripheral can connect to DMA channel */
/*-------------------------- MCPWM CAPS --------------------------------------*/ /*-------------------------- MCPWM CAPS --------------------------------------*/
#define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals) #define SOC_MCPWM_GROUPS (2U) ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)

View File

@@ -17,8 +17,8 @@ PROVIDE ( SPIMEM3 = 0x5008F000 );
PROVIDE ( I2C0 = 0x500C4000 ); PROVIDE ( I2C0 = 0x500C4000 );
PROVIDE ( I2C1 = 0x500C5000 ); PROVIDE ( I2C1 = 0x500C5000 );
PROVIDE ( UHCI0 = 0x500DF000 ); PROVIDE ( UHCI0 = 0x500DF000 );
PROVIDE ( RMT = 0x500D4000 ); PROVIDE ( RMT = 0x500A2000 );
PROVIDE ( RMTMEM = 0x500D4800 ); PROVIDE ( RMTMEM = 0x500A2800 );
PROVIDE ( LEDC = 0x500D3000 ); PROVIDE ( LEDC = 0x500D3000 );
PROVIDE ( TIMERG0 = 0x500C2000 ); PROVIDE ( TIMERG0 = 0x500C2000 );
PROVIDE ( TIMERG1 = 0x500C3000 ); PROVIDE ( TIMERG1 = 0x500C3000 );

View File

@@ -8,5 +8,44 @@
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
const rmt_signal_conn_t rmt_periph_signals = { const rmt_signal_conn_t rmt_periph_signals = {
.groups = {
[0] = {
.module = PERIPH_RMT_MODULE,
.irq = ETS_RMT_INTR_SOURCE,
.channels = {
[0] = {
.tx_sig = RMT_SIG_PAD_OUT0_IDX,
.rx_sig = -1
},
[1] = {
.tx_sig = RMT_SIG_PAD_OUT1_IDX,
.rx_sig = -1
},
[2] = {
.tx_sig = RMT_SIG_PAD_OUT2_IDX,
.rx_sig = -1
},
[3] = {
.tx_sig = RMT_SIG_PAD_OUT3_IDX,
.rx_sig = -1
},
[4] = {
.tx_sig = -1,
.rx_sig = RMT_SIG_PAD_IN0_IDX
},
[5] = {
.tx_sig = -1,
.rx_sig = RMT_SIG_PAD_IN1_IDX
},
[6] = {
.tx_sig = -1,
.rx_sig = RMT_SIG_PAD_IN2_IDX
},
[7] = {
.tx_sig = -1,
.rx_sig = RMT_SIG_PAD_IN3_IDX
}
}
}
}
}; };

View File

@@ -141,7 +141,6 @@ api-reference/peripherals/spi_master.rst
api-reference/peripherals/index.rst api-reference/peripherals/index.rst
api-reference/peripherals/sdmmc_host.rst api-reference/peripherals/sdmmc_host.rst
api-reference/peripherals/uart.rst api-reference/peripherals/uart.rst
api-reference/peripherals/rmt.rst
api-reference/kconfig.rst api-reference/kconfig.rst
api-reference/network/esp_openthread.rst api-reference/network/esp_openthread.rst
api-reference/network/esp_eth.rst api-reference/network/esp_eth.rst

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# RMT Infinite Loop Transmit Example -- Dshot ESC (Electronic Speed Controller) # RMT Infinite Loop Transmit Example -- Dshot ESC (Electronic Speed Controller)
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# IR NEC Encoding and Decoding Example # IR NEC Encoding and Decoding Example
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# RMT Transmit Example -- LED Strip # RMT Transmit Example -- LED Strip
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- | -------- | | ----------------- | -------- | -------- | -------- | -------- | -------- | -------- |
# RMT Transmit Loop Count Example -- Musical Buzzer # RMT Transmit Loop Count Example -- Musical Buzzer

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | | ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# Advanced RMT Transmit & Receive Example -- Simulate 1-Wire Bus # Advanced RMT Transmit & Receive Example -- Simulate 1-Wire Bus

View File

@@ -1,5 +1,5 @@
| Supported Targets | ESP32-C6 | ESP32-H2 | ESP32-S3 | | Supported Targets | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | | ----------------- | -------- | -------- | -------- | -------- |
# RMT Based Stepper Motor Smooth Controller # RMT Based Stepper Motor Smooth Controller