Skip to content

Commit

Permalink
Merge branch 'feature/rmt_receive_in_isr_v5.2' into 'release/v5.2'
Browse files Browse the repository at this point in the history
feat(rmt): support calling rmt_receive in ISR callback (v5.2)

See merge request espressif/esp-idf!26996
  • Loading branch information
suda-morris committed Nov 13, 2023
2 parents 99f06b7 + 2e8cc61 commit 621acc4
Show file tree
Hide file tree
Showing 17 changed files with 112 additions and 38 deletions.
1 change: 1 addition & 0 deletions components/driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ if(CONFIG_SOC_RMT_SUPPORTED)
"rmt/rmt_rx.c"
"rmt/rmt_tx.c"
"deprecated/rmt_legacy.c")
list(APPEND ldfragments "rmt/linker.lf")
endif()

# SDIO Slave related source files
Expand Down
9 changes: 9 additions & 0 deletions components/driver/rmt/Kconfig.rmt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ menu "RMT Configuration"
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_RECV_FUNC_IN_IRAM
bool "Place RMT receive function into IRAM"
default n
select GDMA_CTRL_FUNC_IN_IRAM if SOC_RMT_SUPPORT_DMA # RMT needs to start the GDMA in the receive function
help
Place RMT receive function into IRAM,
so that the receive function can be IRAM-safe and able to be called when the flash cache is disabled.
Enabling this option can improve driver performance as well.

config RMT_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
Expand Down
2 changes: 2 additions & 0 deletions components/driver/rmt/include/driver/rmt_rx.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
*
* @note This function is non-blocking, it initiates a new receive job and then returns.
* User should check the received data from the `on_recv_done` callback that registered by `rmt_rx_register_event_callbacks()`.
* @note This function can also be called in ISR context.
* @note If you want this function to work even when the flash cache is disabled, please enable the `CONFIG_RMT_RECV_FUNC_IN_IRAM` option.
*
* @param[in] rx_channel RMT RX channel that created by `rmt_new_rx_channel()`
* @param[in] buffer The buffer to store the received RMT symbols
Expand Down
7 changes: 7 additions & 0 deletions components/driver/rmt/linker.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[mapping:rmt_driver]
archive: libdriver.a
entries:
if RMT_RECV_FUNC_IN_IRAM = y:
rmt_rx: rmt_receive (noflash)
if SOC_RMT_SUPPORT_DMA = y:
rmt_rx: rmt_rx_mount_dma_buffer (noflash)
2 changes: 1 addition & 1 deletion components/driver/rmt/rmt_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
extern "C" {
#endif

#if CONFIG_RMT_ISR_IRAM_SAFE
#if CONFIG_RMT_ISR_IRAM_SAFE || CONFIG_RMT_RECV_FUNC_IN_IRAM
#define RMT_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define RMT_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
Expand Down
37 changes: 20 additions & 17 deletions components/driver/rmt/rmt_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,36 +344,36 @@ esp_err_t rmt_rx_register_event_callbacks(rmt_channel_handle_t channel, const rm

esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_size, const rmt_receive_config_t *config)
{
ESP_RETURN_ON_FALSE(channel && buffer && buffer_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(channel->direction == RMT_CHANNEL_DIRECTION_RX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
ESP_RETURN_ON_FALSE_ISR(channel && buffer && buffer_size && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(channel->direction == RMT_CHANNEL_DIRECTION_RX, ESP_ERR_INVALID_ARG, TAG, "invalid channel direction");
rmt_rx_channel_t *rx_chan = __containerof(channel, rmt_rx_channel_t, base);

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_ISR(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_LL_LEVEL_INT_MEM, 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");
ESP_RETURN_ON_FALSE_ISR(((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_ISR((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) {
ESP_RETURN_ON_FALSE(buffer_size <= rx_chan->num_dma_nodes * RMT_DMA_DESC_BUF_MAX_SIZE,
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity");
ESP_RETURN_ON_FALSE_ISR(buffer_size <= rx_chan->num_dma_nodes * RMT_DMA_DESC_BUF_MAX_SIZE,
ESP_ERR_INVALID_ARG, TAG, "buffer size exceeds DMA capacity");
}
rmt_group_t *group = channel->group;
rmt_hal_context_t *hal = &group->hal;
int channel_id = channel->channel_id;

uint32_t filter_reg_value = ((uint64_t)group->resolution_hz * config->signal_range_min_ns) / 1000000000UL;
uint32_t idle_reg_value = ((uint64_t)channel->resolution_hz * config->signal_range_max_ns) / 1000000000UL;
ESP_RETURN_ON_FALSE(filter_reg_value <= RMT_LL_MAX_FILTER_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_min_ns too big");
ESP_RETURN_ON_FALSE(idle_reg_value <= RMT_LL_MAX_IDLE_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_max_ns too big");
ESP_RETURN_ON_FALSE_ISR(filter_reg_value <= RMT_LL_MAX_FILTER_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_min_ns too big");
ESP_RETURN_ON_FALSE_ISR(idle_reg_value <= RMT_LL_MAX_IDLE_VALUE, ESP_ERR_INVALID_ARG, TAG, "signal_range_max_ns too big");

// check if we're in a proper state to start the receiver
rmt_fsm_t expected_fsm = RMT_FSM_ENABLE;
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT),
ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");
ESP_RETURN_ON_FALSE_ISR(atomic_compare_exchange_strong(&channel->fsm, &expected_fsm, RMT_FSM_RUN_WAIT),
ESP_ERR_INVALID_STATE, TAG, "channel not in enable state");

// fill in the transaction descriptor
rmt_rx_trans_desc_t *t = &rx_chan->trans_desc;
Expand All @@ -391,7 +391,7 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
}

rx_chan->mem_off = 0;
portENTER_CRITICAL(&channel->spinlock);
portENTER_CRITICAL_SAFE(&channel->spinlock);
// reset memory writer offset
rmt_ll_rx_reset_pointer(hal->regs, channel_id);
rmt_ll_rx_set_mem_owner(hal->regs, channel_id, RMT_LL_MEM_OWNER_HW);
Expand All @@ -401,7 +401,7 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
rmt_ll_rx_set_idle_thres(hal->regs, channel_id, idle_reg_value);
// turn on RMT RX machine
rmt_ll_rx_enable(hal->regs, channel_id, true);
portEXIT_CRITICAL(&channel->spinlock);
portEXIT_CRITICAL_SAFE(&channel->spinlock);

// saying we're in running state, this state will last until the receiving is done
// i.e., we will switch back to the enable state in the receive done interrupt handler
Expand Down Expand Up @@ -585,6 +585,9 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
}
trans_desc->copy_dest_off += copy_size;
trans_desc->received_symbol_num += copy_size / sizeof(rmt_symbol_word_t);
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
atomic_store(&channel->fsm, RMT_FSM_ENABLE);

// notify the user with receive RMT symbols
if (rx_chan->on_recv_done) {
rmt_rx_done_event_data_t edata = {
Expand All @@ -595,8 +598,6 @@ static bool IRAM_ATTR rmt_isr_handle_rx_done(rmt_rx_channel_t *rx_chan)
need_yield = true;
}
}
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
atomic_store(&channel->fsm, RMT_FSM_ENABLE);
return need_yield;
}

Expand Down Expand Up @@ -699,6 +700,9 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
Cache_Invalidate_Addr(invalidate_map, (uint32_t)trans_desc->buffer, trans_desc->buffer_size);
#endif

// switch back to the enable state, then user can call `rmt_receive` to start a new receive
atomic_store(&channel->fsm, RMT_FSM_ENABLE);

if (rx_chan->on_recv_done) {
rmt_rx_done_event_data_t edata = {
.received_symbols = trans_desc->buffer,
Expand All @@ -708,8 +712,7 @@ static bool IRAM_ATTR rmt_dma_rx_eof_cb(gdma_channel_handle_t dma_chan, gdma_eve
need_yield = true;
}
}
// switch back to the enable state, then user can call `rmt_receive` to start a new receive
atomic_store(&channel->fsm, RMT_FSM_ENABLE);

return need_yield;
}
#endif // SOC_RMT_SUPPORT_DMA
47 changes: 29 additions & 18 deletions components/driver/test_apps/rmt/main/test_rmt_iram.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,43 @@ TEST_CASE("rmt tx iram safe", "[rmt]")
#endif
}

#define TEST_RMT_SYMBOLS 2

static void IRAM_ATTR test_simulate_input_post_cache_disable(void *args)
{
int gpio_num = (int)args;
// simulate input signal, should only be recognized as one RMT symbol
gpio_set_level(gpio_num, 0);
esp_rom_delay_us(50);
gpio_set_level(gpio_num, 1);
esp_rom_delay_us(50);
gpio_set_level(gpio_num, 0);
esp_rom_delay_us(20000);
// simulate input signal, should only be recognized as two RMT symbols
for (int i = 0; i < TEST_RMT_SYMBOLS; i++) {
gpio_set_level(gpio_num, 0);
esp_rom_delay_us(50);
gpio_set_level(gpio_num, 1);
esp_rom_delay_us(50);
gpio_set_level(gpio_num, 0);
esp_rom_delay_us(20000);
}
}

typedef struct {
TaskHandle_t task_to_notify;
size_t received_symbol_num;
rmt_receive_config_t rx_config;
rmt_symbol_word_t remote_codes[128];
} test_nec_rx_user_data_t;

IRAM_ATTR
static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t high_task_wakeup = pdFALSE;
test_nec_rx_user_data_t *test_user_data = (test_nec_rx_user_data_t *)user_data;
test_user_data->received_symbol_num = edata->num_symbols;
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
test_user_data->received_symbol_num += edata->num_symbols;
// should receive one RMT symbol at a time
if (edata->num_symbols == 1) {
if (test_user_data->received_symbol_num == TEST_RMT_SYMBOLS) {
vTaskNotifyGiveFromISR(test_user_data->task_to_notify, &high_task_wakeup);
} else {
rmt_receive(channel, test_user_data->remote_codes, sizeof(test_user_data->remote_codes), &test_user_data->rx_config);
}
}
return high_task_wakeup == pdTRUE;
}

Expand All @@ -137,27 +150,25 @@ static void test_rmt_rx_iram_safe(size_t mem_block_symbols, bool with_dma, rmt_c
};
test_nec_rx_user_data_t test_user_data = {
.task_to_notify = xTaskGetCurrentTaskHandle(),
.received_symbol_num = 0,
.rx_config = {
.signal_range_min_ns = 1250,
.signal_range_max_ns = 12000000,
},
};
TEST_ESP_OK(rmt_rx_register_event_callbacks(rx_channel, &cbs, &test_user_data));

printf("enable rx channel\r\n");
TEST_ESP_OK(rmt_enable(rx_channel));

rmt_symbol_word_t remote_codes[128];

rmt_receive_config_t receive_config = {
.signal_range_min_ns = 1250,
.signal_range_max_ns = 12000000,
};

// ready to receive
TEST_ESP_OK(rmt_receive(rx_channel, remote_codes, sizeof(remote_codes), &receive_config));
TEST_ESP_OK(rmt_receive(rx_channel, test_user_data.remote_codes, sizeof(test_user_data.remote_codes), &test_user_data.rx_config));

// disable the flash cache, and simulate input signal by GPIO
unity_utils_run_cache_disable_stub(test_simulate_input_post_cache_disable, 0);

TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000)));
TEST_ASSERT_EQUAL(1, test_user_data.received_symbol_num);
TEST_ASSERT_EQUAL(TEST_RMT_SYMBOLS, test_user_data.received_symbol_num);

printf("disable rx channels\r\n");
TEST_ESP_OK(rmt_disable(rx_channel));
Expand Down
1 change: 1 addition & 0 deletions components/driver/test_apps/rmt/sdkconfig.ci.iram_safe
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_RMT_ISR_IRAM_SAFE=y
CONFIG_RMT_RECV_FUNC_IN_IRAM=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
# place non-ISR FreeRTOS functions in Flash
Expand Down
3 changes: 3 additions & 0 deletions components/hal/esp32/include/hal/rmt_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
* @param channel RMT RX channel number
* @param thres Time length threshold
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->conf_ch[channel].conf0, idle_thres, thres);
Expand All @@ -429,6 +430,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
* @param channel RMT RX chanenl number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->conf_ch[channel].conf1.rx_filter_en = enable;
Expand All @@ -441,6 +443,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
* @param channel RMT RX channel number
* @param thres Filter threshold
*/
__attribute__((always_inline))
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->conf_ch[channel].conf1, rx_filter_thres, thres);
Expand Down
4 changes: 4 additions & 0 deletions components/hal/esp32c3/include/hal/rmt_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
* @param dev Peripheral instance address
* @param channel RMT RX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
{
dev->rx_conf[channel].conf1.mem_wr_rst = 1;
Expand Down Expand Up @@ -513,6 +514,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
* @param channel RMT RX channel number
* @param thres Time length threshold
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_set_idle_thres(rmt_dev_t *dev, uint32_t channel, uint32_t thres)
{
dev->rx_conf[channel].conf0.idle_thres = thres;
Expand All @@ -538,6 +540,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
* @param channel RMT RX chanenl number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->rx_conf[channel].conf1.rx_filter_en = enable;
Expand All @@ -550,6 +553,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
* @param channel RMT RX channel number
* @param thres Filter threshold
*/
__attribute__((always_inline))
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->rx_conf[channel].conf1, rx_filter_thres, thres);
Expand Down
4 changes: 4 additions & 0 deletions components/hal/esp32c6/include/hal/rmt_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
* @param dev Peripheral instance address
* @param channel RMT RX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
{
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
Expand Down Expand Up @@ -519,6 +520,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
* @param channel RMT RX channel number
* @param thres Time length threshold
*/
__attribute__((always_inline))
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;
Expand All @@ -544,6 +546,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
* @param channel RMT RX chanenl number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
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;
Expand All @@ -556,6 +559,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
* @param channel RMT RX channel number
* @param thres Filter threshold
*/
__attribute__((always_inline))
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);
Expand Down
4 changes: 4 additions & 0 deletions components/hal/esp32h2/include/hal/rmt_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ static inline void rmt_ll_rx_set_channel_clock_div(rmt_dev_t *dev, uint32_t chan
* @param dev Peripheral instance address
* @param channel RMT RX channel number
*/
__attribute__((always_inline))
static inline void rmt_ll_rx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
{
dev->chmconf[channel].conf1.mem_wr_rst_chm = 1;
Expand Down Expand Up @@ -516,6 +517,7 @@ static inline void rmt_ll_rx_set_mem_blocks(rmt_dev_t *dev, uint32_t channel, ui
* @param channel RMT RX channel number
* @param thres Time length threshold
*/
__attribute__((always_inline))
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;
Expand All @@ -541,6 +543,7 @@ static inline void rmt_ll_rx_set_mem_owner(rmt_dev_t *dev, uint32_t channel, rmt
* @param channel RMT RX chanenl number
* @param enable True to enable, False to disable
*/
__attribute__((always_inline))
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;
Expand All @@ -553,6 +556,7 @@ static inline void rmt_ll_rx_enable_filter(rmt_dev_t *dev, uint32_t channel, boo
* @param channel RMT RX channel number
* @param thres Filter threshold
*/
__attribute__((always_inline))
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);
Expand Down
Loading

0 comments on commit 621acc4

Please sign in to comment.