Skip to content

Commit

Permalink
feat(rmt): check if the gpio number is reserved by others
Browse files Browse the repository at this point in the history
  • Loading branch information
suda-morris committed Mar 9, 2024
1 parent c952cfb commit 391a187
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 20 deletions.
1 change: 0 additions & 1 deletion components/esp_driver_rmt/src/rmt_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gpio_reset_pin(channel->gpio_num);
return channel->del(channel);
}

Expand Down
1 change: 1 addition & 0 deletions components/esp_driver_rmt/src/rmt_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "esp_pm.h"
#include "esp_attr.h"
#include "esp_private/gdma.h"
#include "esp_private/esp_gpio_reserve.h"
#include "driver/rmt_common.h"

#ifdef __cplusplus
Expand Down
23 changes: 14 additions & 9 deletions components/esp_driver_rmt/src/rmt_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
if (rx_channel->base.gpio_num >= 0) {
gpio_reset_pin(rx_channel->base.gpio_num);
}
if (rx_channel->base.intr) {
ESP_RETURN_ON_ERROR(esp_intr_free(rx_channel->base.intr), TAG, "delete interrupt service failed");
}
Expand Down Expand Up @@ -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;
// Check if priority is valid
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_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((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_GOTO_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number");
ESP_GOTO_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_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number %d", config->gpio_num);
ESP_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= 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
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

// malloc channel memory
uint32_t mem_caps = RMT_MEM_ALLOC_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");
// gpio is not configured yet
rx_channel->base.gpio_num = -1;
// create DMA descriptor
size_t num_dma_nodes = 0;
if (config->flags.with_dma) {
Expand Down Expand Up @@ -266,16 +271,16 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#endif

// GPIO Matrix/MUX configuration
rx_channel->base.gpio_num = config->gpio_num;
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
// 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),
.pull_down_en = false,
.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");
rx_channel->base.gpio_num = 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,
config->flags.invert_in);
Expand Down
32 changes: 22 additions & 10 deletions components/esp_driver_rmt/src/rmt_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ static esp_err_t rmt_tx_create_trans_queue(rmt_tx_channel_t *tx_channel, const r

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) {
ESP_RETURN_ON_ERROR(esp_intr_free(tx_channel->base.intr), TAG, "delete interrupt service failed");
}
Expand Down Expand Up @@ -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(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_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(config->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "invalid GPIO number");
ESP_GOTO_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_RETURN_ON_FALSE(config && ret_chan && config->resolution_hz && config->trans_queue_depth, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
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_RETURN_ON_FALSE((config->mem_block_symbols & 0x01) == 0 && config->mem_block_symbols >= 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
// 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_ERR_INVALID_ARG, err, TAG, "mem_block_symbols can't exceed %d",
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, TAG, "mem_block_symbols can't exceed %d",
RMT_DMA_DESC_BUF_MAX_SIZE * RMT_DMA_NODES_PING_PONG / sizeof(rmt_symbol_word_t));
#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

// malloc channel memory
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");
// GPIO configuration is not done yet
tx_channel->base.gpio_num = -1;
// create DMA descriptors
if (config->flags.with_dma) {
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
Expand Down Expand Up @@ -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);

// GPIO Matrix/MUX configuration
tx_channel->base.gpio_num = config->gpio_num;
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
// 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),
.pull_down_en = false,
.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");
// 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,
rmt_periph_signals.groups[group_id].channels[channel_id + RMT_TX_CHANNEL_OFFSET_IN_GROUP].tx_sig,
config->flags.invert_out, false);
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);
atomic_init(&tx_channel->base.fsm, RMT_FSM_INIT);
Expand Down

0 comments on commit 391a187

Please sign in to comment.