feat(rmt): check if the gpio number is reserved by others

This commit is contained in:
morris
2024-02-20 16:21:25 +08:00
parent c952cfb673
commit 391a187c11
4 changed files with 37 additions and 20 deletions

View File

@@ -205,7 +205,6 @@ esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_conf
esp_err_t rmt_del_channel(rmt_channel_handle_t channel) esp_err_t rmt_del_channel(rmt_channel_handle_t channel)
{ {
ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gpio_reset_pin(channel->gpio_num);
return channel->del(channel); return channel->del(channel);
} }

View File

@@ -24,6 +24,7 @@
#include "esp_pm.h" #include "esp_pm.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_private/gdma.h" #include "esp_private/gdma.h"
#include "esp_private/esp_gpio_reserve.h"
#include "driver/rmt_common.h" #include "driver/rmt_common.h"
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -146,6 +146,9 @@ static void rmt_rx_unregister_from_group(rmt_channel_t *channel, rmt_group_t *gr
static esp_err_t rmt_rx_destroy(rmt_rx_channel_t *rx_channel) static esp_err_t rmt_rx_destroy(rmt_rx_channel_t *rx_channel)
{ {
if (rx_channel->base.gpio_num >= 0) {
gpio_reset_pin(rx_channel->base.gpio_num);
}
if (rx_channel->base.intr) { if (rx_channel->base.intr) {
ESP_RETURN_ON_ERROR(esp_intr_free(rx_channel->base.intr), TAG, "delete interrupt service failed"); ESP_RETURN_ON_ERROR(esp_intr_free(rx_channel->base.intr), TAG, "delete interrupt service failed");
} }
@@ -177,21 +180,23 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
rmt_rx_channel_t *rx_channel = NULL; rmt_rx_channel_t *rx_channel = NULL;
// Check if priority is valid // Check if priority is valid
if (config->intr_priority) { if (config->intr_priority) {
ESP_GOTO_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority); ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err, TAG, "invalid interrupt priority:%d", config->intr_priority); ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
} }
ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num);
ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL);
#if !SOC_RMT_SUPPORT_DMA #if !SOC_RMT_SUPPORT_DMA
ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported"); ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported");
#endif // SOC_RMT_SUPPORT_DMA #endif // SOC_RMT_SUPPORT_DMA
// malloc channel memory // malloc channel memory
uint32_t mem_caps = RMT_MEM_ALLOC_CAPS; uint32_t mem_caps = RMT_MEM_ALLOC_CAPS;
rx_channel = heap_caps_calloc(1, sizeof(rmt_rx_channel_t), mem_caps); rx_channel = heap_caps_calloc(1, sizeof(rmt_rx_channel_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");
// gpio is not configured yet
rx_channel->base.gpio_num = -1;
// create DMA descriptor // create DMA descriptor
size_t num_dma_nodes = 0; size_t num_dma_nodes = 0;
if (config->flags.with_dma) { if (config->flags.with_dma) {
@@ -266,16 +271,16 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#endif #endif
// GPIO Matrix/MUX configuration // GPIO Matrix/MUX configuration
rx_channel->base.gpio_num = config->gpio_num;
gpio_config_t gpio_conf = { gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE, .intr_type = GPIO_INTR_DISABLE,
// also enable the input path is `io_loop_back` is on, this is useful for debug // also enable the input path is `io_loop_back` is on, this is useful for debug
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), .mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0),
.pull_down_en = false, .pull_down_en = false,
.pull_up_en = true, .pull_up_en = true,
.pin_bit_mask = 1ULL << config->gpio_num, .pin_bit_mask = BIT64(config->gpio_num),
}; };
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed"); ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed");
rx_channel->base.gpio_num = config->gpio_num;
esp_rom_gpio_connect_in_signal(config->gpio_num, esp_rom_gpio_connect_in_signal(config->gpio_num,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig, rmt_periph_signals.groups[group_id].channels[channel_id + RMT_RX_CHANNEL_OFFSET_IN_GROUP].rx_sig,
config->flags.invert_in); config->flags.invert_in);

View File

@@ -185,6 +185,10 @@ exit:
static esp_err_t rmt_tx_destroy(rmt_tx_channel_t *tx_channel) static esp_err_t rmt_tx_destroy(rmt_tx_channel_t *tx_channel)
{ {
if (tx_channel->base.gpio_num >= 0) {
gpio_reset_pin(tx_channel->base.gpio_num);
esp_gpio_revoke(BIT64(tx_channel->base.gpio_num));
}
if (tx_channel->base.intr) { if (tx_channel->base.intr) {
ESP_RETURN_ON_ERROR(esp_intr_free(tx_channel->base.intr), TAG, "delete interrupt service failed"); ESP_RETURN_ON_ERROR(esp_intr_free(tx_channel->base.intr), TAG, "delete interrupt service failed");
} }
@@ -227,24 +231,26 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); ESP_RETURN_ON_FALSE((config->intr_priority) > 0, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & RMT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority);
} }
ESP_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num);
ESP_GOTO_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL, ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= SOC_RMT_MEM_WORDS_PER_CHANNEL,
ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL); ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols must be even and at least %d", SOC_RMT_MEM_WORDS_PER_CHANNEL);
#if SOC_RMT_SUPPORT_DMA #if SOC_RMT_SUPPORT_DMA
// we only support 2 nodes ping-pong, if the configured memory block size needs more than two DMA descriptors, should treat it as invalid // we only support 2 nodes ping-pong, if the configured memory block size needs more than two DMA descriptors, should treat it as invalid
ESP_GOTO_ON_FALSE(config->mem_block_symbols <= RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t), ESP_RETURN_ON_FALSE(config->mem_block_symbols <= RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t),
ESP_ERR_INVALID_ARG, err, TAG, "mem_block_symbols can't exceed %d", ESP_ERR_INVALID_ARG, TAG, "mem_block_symbols can't exceed %d",
RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t)); RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t));
#else #else
ESP_GOTO_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "DMA not supported"); ESP_RETURN_ON_FALSE(config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA not supported");
#endif #endif
// 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); 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"); ESP_GOTO_ON_FALSE(tx_channel, ESP_ERR_NO_MEM, err, TAG, "no mem for tx channel");
// GPIO configuration is not done yet
tx_channel->base.gpio_num = -1;
// create DMA descriptors // create DMA descriptors
if (config->flags.with_dma) { if (config->flags.with_dma) {
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
@@ -312,20 +318,26 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
rmt_ll_tx_enable_wrap(hal->regs, channel_id, true); rmt_ll_tx_enable_wrap(hal->regs, channel_id, true);
// GPIO Matrix/MUX configuration // GPIO Matrix/MUX configuration
tx_channel->base.gpio_num = config->gpio_num;
gpio_config_t gpio_conf = { gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE, .intr_type = GPIO_INTR_DISABLE,
// also enable the input path if `io_loop_back` is on, this is useful for bi-directional buses // also enable the input path if `io_loop_back` is on, this is useful for bi-directional buses
.mode = (config->flags.io_od_mode ? GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT) | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0), .mode = (config->flags.io_od_mode ? GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT) | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0),
.pull_down_en = false, .pull_down_en = false,
.pull_up_en = true, .pull_up_en = true,
.pin_bit_mask = 1ULL << config->gpio_num, .pin_bit_mask = BIT64(config->gpio_num),
}; };
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed"); ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config GPIO failed");
// reserve the GPIO output path, because we don't expect another peripheral to signal to the same GPIO
uint64_t old_gpio_rsv_mask = esp_gpio_reserve(BIT64(config->gpio_num));
// check if the GPIO is already used by others, RMT TX channel only uses the output path of the GPIO
if (old_gpio_rsv_mask & BIT64(config->gpio_num)) {
ESP_LOGW(TAG, "GPIO %d is not usable, maybe conflict with others", config->gpio_num);
}
esp_rom_gpio_connect_out_signal(config->gpio_num, esp_rom_gpio_connect_out_signal(config->gpio_num,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig, rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
config->flags.invert_out, false); config->flags.invert_out, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_num], PIN_FUNC_GPIO);
tx_channel->base.gpio_num = config->gpio_num;
portMUX_INITIALIZE(&tx_channel->base.spinlock); portMUX_INITIALIZE(&tx_channel->base.spinlock);
atomic_init(&tx_channel->base.fsm, RMT_FSM_INIT); atomic_init(&tx_channel->base.fsm, RMT_FSM_INIT);