feat(rmt): support sleep retention by regdma on esp32c5

This commit is contained in:
morris 2024-09-20 15:58:25 +08:00
parent 7752c1b051
commit 491901d7e4
15 changed files with 99 additions and 49 deletions

View File

@ -42,9 +42,8 @@ typedef struct {
uint32_t invert_in: 1; /*!< Whether to invert the incoming RMT channel signal */
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
uint32_t backup_before_sleep: 1; /*!< If set, the driver will backup/restore the RMT registers before/after entering/exist sleep mode.
By this approach, the system can power off RMT's power domain.
This can save power, but at the expense of more RAM being consumed */
uint32_t allow_pd: 1; /*!< If set, driver allows the power domain to be powered off when system enters sleep mode.
This can save power, but at the expense of more RAM being consumed to save register context. */
} flags; /*!< RX channel config flags */
} rmt_rx_channel_config_t;

View File

@ -44,9 +44,8 @@ typedef struct {
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
uint32_t io_loop_back: 1; /*!< The signal output from the GPIO will be fed to the input path as well */
uint32_t io_od_mode: 1; /*!< Configure the GPIO as open-drain mode */
uint32_t backup_before_sleep: 1; /*!< If set, the driver will backup/restore the RMT registers before/after entering/exist sleep mode.
By this approach, the system can power off RMT's power domain.
This can save power, but at the expense of more RAM being consumed */
uint32_t allow_pd: 1; /*!< If set, driver allows the power domain to be powered off when system enters sleep mode.
This can save power, but at the expense of more RAM being consumed to save register context. */
} flags; /*!< TX channel config flags */
} rmt_tx_channel_config_t;

View File

@ -74,6 +74,24 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
rmt_ll_enable_bus_clock(group_id, true);
rmt_ll_reset_register(group_id);
}
#if RMT_USE_RETENTION_LINK
sleep_retention_module_t module = RMT_LL_SLEEP_RETENTION_MODULE_ID(group_id);
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = rmt_create_sleep_retention_link_cb,
.arg = group,
},
},
.depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM)
};
if (sleep_retention_module_init(module, &init_param) == ESP_OK) {
group->sleep_retention_module = module;
} else {
// even though the sleep retention module init failed, RMT driver should still work, so just warning here
ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id);
}
#endif // RMT_USE_RETENTION_LINK
// hal layer initialize
rmt_hal_init(&group->hal);
}
@ -87,24 +105,6 @@ rmt_group_t *rmt_acquire_group_handle(int group_id)
_lock_release(&s_platform.mutex);
if (new_group) {
#if RMT_USE_RETENTION_LINK
sleep_retention_module_t module = RMT_LL_SLEEP_RETENTION_MODULE_ID(group_id);
sleep_retention_module_init_param_t init_param = {
.cbs = {
.create = {
.handle = rmt_create_sleep_retention_link_cb,
.arg = group,
},
},
.depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM)
};
if (sleep_retention_module_init(module, &init_param) == ESP_OK) {
group->sleep_retention_module = module;
} else {
// even though the sleep retention module init failed, RMT driver should still work, so just warning here
ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id);
}
#endif // RMT_USE_RETENTION_LINK
ESP_LOGD(TAG, "new group(%d) at %p, occupy=%"PRIx32, group_id, group, group->occupy_mask);
}
return group;
@ -307,8 +307,7 @@ static esp_err_t rmt_create_sleep_retention_link_cb(void *arg)
esp_err_t err = sleep_retention_entries_create(rmt_reg_retention_info[group_id].regdma_entry_array,
rmt_reg_retention_info[group_id].array_size,
REGDMA_LINK_PRI_RMT, module);
ESP_RETURN_ON_ERROR(err, TAG, "create retention link failed");
return ESP_OK;
return err;
}
void rmt_create_retention_module(rmt_group_t *group)
@ -319,7 +318,7 @@ void rmt_create_retention_module(rmt_group_t *group)
if (group->retention_link_created == false) {
if (sleep_retention_module_allocate(module) != ESP_OK) {
// even though the sleep retention module create failed, RMT driver should still work, so just warning here
ESP_LOGW(TAG, "create retention module failed, power domain can't turn off");
ESP_LOGW(TAG, "create retention link failed, power domain can't be turned off");
} else {
group->retention_link_created = true;
}

View File

@ -197,7 +197,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#endif // SOC_RMT_SUPPORT_DMA
#if !SOC_RMT_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.backup_before_sleep == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#endif // SOC_RMT_SUPPORT_SLEEP_RETENTION
// malloc channel memory
@ -240,7 +240,7 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
int group_id = group->group_id;
#if RMT_USE_RETENTION_LINK
if (config->flags.backup_before_sleep != 0) {
if (config->flags.allow_pd != 0) {
rmt_create_retention_module(group);
}
#endif // RMT_USE_RETENTION_LINK
@ -400,7 +400,7 @@ esp_err_t rmt_receive(rmt_channel_handle_t channel, void *buffer, size_t buffer_
// check buffer alignment
uint32_t align_check_mask = mem_alignment - 1;
ESP_RETURN_ON_FALSE_ISR((((uintptr_t)buffer & align_check_mask) == 0) && ((buffer_size & align_check_mask) == 0), ESP_ERR_INVALID_ARG,
ESP_RETURN_ON_FALSE_ISR(((((uintptr_t)buffer) & align_check_mask) == 0) && (((buffer_size) & align_check_mask) == 0), ESP_ERR_INVALID_ARG,
TAG, "buffer address or size are not %zu bytes aligned", mem_alignment);
rmt_group_t *group = channel->group;

View File

@ -266,7 +266,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
#endif
#if !SOC_RMT_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.backup_before_sleep == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#endif // SOC_RMT_SUPPORT_SLEEP_RETENTION
// malloc channel memory
@ -302,7 +302,7 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
int group_id = group->group_id;
#if RMT_USE_RETENTION_LINK
if (config->flags.backup_before_sleep != 0) {
if (config->flags.allow_pd != 0) {
rmt_create_retention_module(group);
}
#endif // RMT_USE_RETENTION_LINK

View File

@ -20,7 +20,6 @@
#include "test_util_rmt_encoders.h"
#include "test_board.h"
#if SOC_RMT_SUPPORT_SLEEP_RETENTION // TODO: IDF-10917
typedef struct {
TaskHandle_t task_to_notify;
size_t received_symbol_num;
@ -43,9 +42,9 @@ static bool test_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx
/**
* @brief Test the RMT driver can still work after light sleep
*
* @param back_up_before_sleep Whether to back up RMT registers before sleep
* @param allow_pd Whether to allow power down the peripheral in light sleep
*/
static void test_rmt_tx_rx_sleep_retention(bool back_up_before_sleep)
static void test_rmt_tx_rx_sleep_retention(bool allow_pd)
{
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),
@ -58,7 +57,7 @@ static void test_rmt_tx_rx_sleep_retention(bool back_up_before_sleep)
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.io_loop_back = true,
.flags.backup_before_sleep = back_up_before_sleep,
.flags.allow_pd = allow_pd,
};
printf("install rx channel\r\n");
rmt_channel_handle_t rx_channel = NULL;
@ -79,7 +78,7 @@ static void test_rmt_tx_rx_sleep_retention(bool back_up_before_sleep)
.trans_queue_depth = 4,
.gpio_num = TEST_RMT_GPIO_NUM_A,
.flags.io_loop_back = true,
.flags.backup_before_sleep = back_up_before_sleep,
.flags.allow_pd = allow_pd,
};
printf("install tx channel\r\n");
rmt_channel_handle_t tx_channel = NULL;
@ -111,13 +110,12 @@ static void test_rmt_tx_rx_sleep_retention(bool back_up_before_sleep)
printf("check if the sleep happened as expected\r\n");
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_RMT_SUPPORT_SLEEP_RETENTION
if (back_up_before_sleep) {
printf("sleep_ctx.sleep_flags=%lx\r\n", sleep_ctx.sleep_flags);
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, sleep_ctx.sleep_flags & PMU_SLEEP_PD_TOP);
}
// check if the power domain also is powered down
TEST_ASSERT_EQUAL(back_up_before_sleep ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
esp_sleep_set_sleep_context(NULL);
// enable both channels, pm lock will be acquired, so no light sleep will event happen during the transaction
TEST_ESP_OK(rmt_enable(tx_channel));
TEST_ESP_OK(rmt_enable(rx_channel));
@ -147,6 +145,7 @@ static void test_rmt_tx_rx_sleep_retention(bool back_up_before_sleep)
TEST_CASE("rmt tx+rx after light sleep", "[rmt]")
{
test_rmt_tx_rx_sleep_retention(false);
#if SOC_RMT_SUPPORT_SLEEP_RETENTION
test_rmt_tx_rx_sleep_retention(true);
}
#endif
}

View File

@ -18,6 +18,7 @@
#include "hal/rmt_types.h"
#include "soc/rmt_struct.h"
#include "soc/pcr_struct.h"
#include "soc/retention_periph_defs.h"
#ifdef __cplusplus
extern "C" {
@ -37,6 +38,8 @@ extern "C" {
#define RMT_LL_MAX_FILTER_VALUE 255
#define RMT_LL_MAX_IDLE_VALUE 32767
#define RMT_LL_SLEEP_RETENTION_MODULE_ID(group_id) (SLEEP_RETENTION_MODULE_RMT0)
typedef enum {
RMT_LL_MEM_OWNER_SW = 0,
RMT_LL_MEM_OWNER_HW = 1,

View File

@ -795,6 +795,10 @@ config SOC_RMT_SUPPORT_XTAL
bool
default y
config SOC_RMT_SUPPORT_SLEEP_RETENTION
bool
default y
config SOC_MCPWM_GROUPS
int
default 1

View File

@ -334,6 +334,7 @@
#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_XTAL 1 /*!< Support set XTAL clock as the RMT clock source */
#define SOC_RMT_SUPPORT_SLEEP_RETENTION 1 /*!< The sleep retention feature can help back up RMT registers before sleep */
// #define SOC_RMT_SUPPORT_RC_FAST 1 /*!< Support set RC_FAST as the RMT clock source */
/*-------------------------- MCPWM CAPS --------------------------------------*/

View File

@ -5,6 +5,7 @@
*/
#include "soc/rmt_periph.h"
#include "soc/rmt_reg.h"
#include "soc/gpio_sig_map.h"
const rmt_signal_conn_t rmt_periph_signals = {
@ -32,3 +33,34 @@ const rmt_signal_conn_t rmt_periph_signals = {
}
}
};
/**
* RMT Registers to be saved during sleep retention
* - Channel configuration registers, e.g.: RMT_CH0CONF0_REG, RMT_CH3CONF0_REG, RMT_CH3CONF1_REG, RMT_CH0_TX_LIM_REG, RMT_CH3_RX_LIM_REG
* - TX synchronization registers, e.g.: RMT_TX_SIM_REG
* - Interrupt enable registers, e.g.: RMT_INT_ENA_REG
* - Carrier duty registers, e.g.: RMT_CH0CARRIER_DUTY_REG, RMT_CH3_RX_CARRIER_RM_REG
* - Global configuration registers, e.g.: RMT_SYS_CONF_REG
*/
#define RMT_RETENTION_REGS_CNT 17
#define RMT_RETENTION_REGS_BASE (DR_REG_RMT_BASE + 0x10)
static const uint32_t rmt_regs_map[4] = {0xffd03f, 0x0, 0x0, 0x0};
static const regdma_entries_config_t rmt_regdma_entries[] = {
// backup stage: save configuration registers
// restore stage: restore the configuration registers
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_RMT_LINK(0x00),
RMT_RETENTION_REGS_BASE, RMT_RETENTION_REGS_BASE,
RMT_RETENTION_REGS_CNT, 0, 0,
rmt_regs_map[0], rmt_regs_map[1],
rmt_regs_map[2], rmt_regs_map[3]),
.owner = ENTRY(0) | ENTRY(2),
},
};
const rmt_reg_retention_info_t rmt_reg_retention_info[SOC_RMT_GROUPS] = {
[0] = {
.regdma_entry_array = rmt_regdma_entries,
.array_size = ARRAY_SIZE(rmt_regdma_entries)
},
};

View File

@ -46,6 +46,8 @@ const rmt_signal_conn_t rmt_periph_signals = {
#define RMT_RETENTION_REGS_BASE (DR_REG_RMT_BASE + 0x10)
static const uint32_t rmt_regs_map[4] = {0xffd03f, 0x0, 0x0, 0x0};
static const regdma_entries_config_t rmt_regdma_entries[] = {
// backup stage: save configuration registers
// restore stage: restore the configuration registers
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_RMT_LINK(0x00),
RMT_RETENTION_REGS_BASE, RMT_RETENTION_REGS_BASE,

View File

@ -46,6 +46,8 @@ const rmt_signal_conn_t rmt_periph_signals = {
#define RMT_RETENTION_REGS_BASE (DR_REG_RMT_BASE + 0x10)
static const uint32_t rmt_regs_map[4] = {0xffd03f, 0x0, 0x0, 0x0};
static const regdma_entries_config_t rmt_regdma_entries[] = {
// backup stage: save configuration registers
// restore stage: restore the configuration registers
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_RMT_LINK(0x00),
RMT_RETENTION_REGS_BASE, RMT_RETENTION_REGS_BASE,

View File

@ -62,6 +62,8 @@ const rmt_signal_conn_t rmt_periph_signals = {
#define RMT_RETENTION_REGS_BASE (DR_REG_RMT_BASE + 0x20)
static const uint32_t rmt_regs_map[4] = {0xff400fff, 0x3ff, 0x0, 0x0};
static const regdma_entries_config_t rmt_regdma_entries[] = {
// backup stage: save configuration registers
// restore stage: restore the configuration registers
[0] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_RMT_LINK(0x00),
RMT_RETENTION_REGS_BASE, RMT_RETENTION_REGS_BASE,

View File

@ -93,7 +93,7 @@ To install an RMT TX channel, there is a configuration structure that needs to b
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` enables both input and output capabilities on the channel's assigned GPIO. Thus, by binding a TX and RX channel to the same GPIO, loopback can be achieved.
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` configures the channel's assigned GPIO as open-drain. When combined with :cpp:member:`rmt_tx_channel_config_t::io_loop_back`, a bi-directional bus (e.g., 1-wire) can be achieved.
- :cpp:member:`rmt_tx_channel_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`rmt_tx_channel_config_t::intr_priority`. Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`rmt_del_channel` is called.
- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` enables the backup of the RMT registers before entering sleep mode. This option implies a tradeoff between power consumption and memory consumption. If the power consumption is not a concern, you can disable this option to save memory. But if you want to save power, you should enable this option to save the RMT registers before entering sleep mode, and restore them after waking up. This feature depends on specific hardware module, if you enable this flag on an unsupported chip, you will get an error message like ``register back up is not supported``.
- :cpp:member:`rmt_tx_channel_config_t::allow_pd` configures if the driver allows the system to power down the peripheral in light sleep mode. Before entering sleep, the system will backup the RMT register context, which will be restored later when the system exit the sleep mode. Powering down the peripheral can save more power, but at the cost of more memory consumed to save the register context. It's a tradeoff between power consumption and memory consumption. This configuration option relies on specific hardware feature, if you enable it on an unsupported chip, you will see error message like ``not able to power down in light sleep``.
Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function returns an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function returns :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g., DMA backend) is not supported by the hardware, it returns :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
@ -128,7 +128,7 @@ To install an RMT RX channel, there is a configuration structure that needs to b
- :cpp:member:`rmt_rx_channel_config_t::with_dma` enables the DMA backend for the channel. Using the DMA allows a significant amount of the channel's workload to be offloaded from the CPU. However, the DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter a :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_rx_channel_config_t::io_loop_back` enables both input and output capabilities on the channel's assigned GPIO. Thus, by binding a TX and RX channel to the same GPIO, loopback can be achieved.
- :cpp:member:`rmt_rx_channel_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`rmt_rx_channel_config_t::intr_priority`. Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`rmt_del_channel` is called.
- :cpp:member:`rmt_rx_channel_config_t::backup_before_sleep` enables the backup of the RMT registers before entering sleep mode. This option implies an balance between power consumption and memory usage. If the power consumption is not a concern, you can disable this option to save memory. But if you want to save power, you should enable this option to save the RMT registers before entering sleep mode, and restore them after waking up. This feature depends on specific hardware module, if you enable this flag on an unsupported chip, you will get an error message like ``register back up is not supported``.
- :cpp:member:`rmt_rx_channel_config_t::allow_pd` configures if the driver allows the system to power down the peripheral in light sleep mode. Before entering sleep, the system will backup the RMT register context, which will be restored later when the system exit the sleep mode. Powering down the peripheral can save more power, but at the cost of more memory consumed to save the register context. It's a tradeoff between power consumption and memory consumption. This configuration option relies on specific hardware feature, if you enable it on an unsupported chip, you will see error message like ``not able to power down in light sleep``.
Once the :cpp:type:`rmt_rx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_rx_channel` to allocate and initialize an RX channel. This function returns an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function returns :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g., DMA backend) is not supported by the hardware, it returns :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
@ -312,6 +312,10 @@ Calling :cpp:func:`rmt_del_sync_manager` can recycle the sync manager and enable
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
}
// enable the channels
for (int i = 0; i < 2; i++) {
ESP_ERROR_CHECK(rmt_enable(tx_channels[i]));
}
// install sync manager
rmt_sync_manager_handle_t synchro = NULL;
rmt_sync_manager_config_t synchro_config = {
@ -553,11 +557,11 @@ Power Management
When power management is enabled, i.e., :ref:`CONFIG_PM_ENABLE` is on, the system may adjust or disable the clock source before going to sleep. As a result, the time base inside the RMT can't work as expected.
The driver can prevent the above issue by creating a power management lock. The lock type is set based on different clock sources. The driver will acquire the lock in :cpp:func:`rmt_enable`, and release it in :cpp:func:`rmt_disable`. That means, any RMT transactions in between these two functions are guaranteed to work correctly, regardless of the power management strategy. The clock source won't be disabled or adjusted its frequency during this time.
The driver can prevent the above issue by creating a power management lock. The lock type is set based on different clock sources. The driver will acquire the lock in :cpp:func:`rmt_enable`, and release it in :cpp:func:`rmt_disable`. That means, any RMT transactions in between these two functions are guaranteed to work correctly and stable.
.. only:: SOC_RMT_SUPPORT_SLEEP_RETENTION
Besides the potential changes to the clock source, when the power management is enabled, the system can also power down a domain where RMT register located. To ensure the RMT driver can continue work after sleep, we can either backup the RMT registers to the RAM, or just refuse to power down. You can choose what to do in :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` and :cpp:member:`rmt_rx_channel_config_t::backup_before_sleep`. It's a balance between power saving and memory consumption. Set it based on your application requirements.
Besides the potential changes to the clock source, when the power management is enabled, the system can also power down the RMT hardware before sleep. Set :cpp:member:`rmt_tx_channel_config_t::allow_pd` and :cpp:member:`rmt_rx_channel_config_t::allow_pd` to ``true`` to enable the power down feature. RMT registers will be backed up before sleep and restored after wake up. Please note, enabling this option will increase the memory consumption.
.. _rmt-iram-safe:

View File

@ -93,7 +93,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 启用通道所分配的 GPIO 上的输入和输出功能,将发送通道和接收通道绑定到同一个 GPIO 上,从而实现回环功能。
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` 配置通道分配的 GPIO 为开漏模式 (open-drain)。当与 :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 结合使用时,可以实现双向总线,如 1-wire。
- :cpp:member:`rmt_tx_channel_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,驱动将会使用一个中低优先级的中断(优先级可能为 12 或 3否则会使用 :cpp:member:`rmt_tx_channel_config_t::intr_priority` 指定的优先级。请使用优先级序号 (1, 2, 3),而不是 bitmask 的形式((1<<1)(1<<2)(1<<3))。请注意,中断优先级一旦设置,在 :cpp:func:`rmt_del_channel` 被调用之前不可再次修改。
- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` 用于使能在进入睡眠模式前备份 RMT 寄存器。这个选项需要用户在功耗和内存使用之间做取舍。如果功耗不是一个问题,可以禁用这个选项来节省内存。但如果想要节省功耗,应该使能这个选项,在进入睡眠模式前保存 RMT 寄存器,并在唤醒后恢复它们。这个功能依赖于特定的硬件模块,如果你在不支持的芯片上启用这个标志,你会得到一个错误信息,如 ``register back up is not supported``
- :cpp:member:`rmt_tx_channel_config_t::allow_pd` 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 RMT 寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 ``not able to power down in light sleep`` 的错误消息。
将必要参数填充到结构体 :cpp:type:`rmt_tx_channel_config_t` 后,可以调用 :cpp:func:`rmt_new_tx_channel` 来分配和初始化 TX 通道。如果函数运行正确,会返回 RMT 通道句柄;如果 RMT 资源池内缺少空闲通道,会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误;如果硬件不支持 DMA 后端等部分功能,则返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
@ -128,7 +128,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
- :cpp:member:`rmt_rx_channel_config_t::with_dma` 为通道启用 DMA 后端。启用 DMA 后端可以释放 CPU 上的大部分通道工作负载,显著减轻 CPU 负担。但并非所有 ESP 芯片都支持 DMA 后端,在启用此选项前,请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。若所选芯片不支持 DMA 后端,可能会报告 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
- :cpp:member:`rmt_rx_channel_config_t::io_loop_back` 启用通道所分配的 GPIO 上的输入和输出功能,将发送通道和接收通道绑定到同一个 GPIO 上,从而实现回环功能。
- :cpp:member:`rmt_rx_channel_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,驱动将会使用一个中低优先级的中断(优先级可能为 12 或 3否则会使用 :cpp:member:`rmt_rx_channel_config_t::intr_priority` 指定的优先级。请使用优先级序号 (123),而不是 bitmask 的形式((1<<1)(1<<2)(1<<3))。请注意,中断优先级一旦设置,在 :cpp:func:`rmt_del_channel` 被调用之前不可再次修改。
- :cpp:member:`rmt_rx_channel_config_t::backup_before_sleep` 用于使能在进入睡眠模式前备份 RMT 寄存器。这个选项需要用户在功耗和内存使用之间取得平衡。如果功耗不是一个问题,可以禁用这个选项来节省内存。但如果想要节省功耗,应该使能这个选项,在进入睡眠模式前保存 RMT 寄存器,并在唤醒后恢复它们。这个功能依赖于特定的硬件模块,如果你在不支持的芯片上启用这个标志,你会得到一个错误信息,如 ``register back up is not supported``
- :cpp:member:`rmt_rx_channel_config_t::allow_pd` 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 RMT 寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 ``not able to power down in light sleep`` 的错误消息。
将必要参数填充到结构体 :cpp:type:`rmt_rx_channel_config_t` 后,可以调用 :cpp:func:`rmt_new_rx_channel` 来分配和初始化 RX 通道。如果函数运行正确,会返回 RMT 通道句柄;如果 RMT 资源池内缺少空闲通道,会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误;如果硬件不支持 DMA 后端等部分功能,则返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。
@ -312,6 +312,10 @@ RMT 是一种特殊的通信外设,无法像 SPI 和 I2C 那样发送原始字
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
}
// 使能通道
for (int i = 0; i < 2; i++) {
ESP_ERROR_CHECK(rmt_enable(tx_channels[i]));
}
// 安装同步管理器
rmt_sync_manager_handle_t synchro = NULL;
rmt_sync_manager_config_t synchro_config = {
@ -557,7 +561,7 @@ RMT 编码器是 RMT TX 事务的一部分,用于在特定时间生成正确
.. only:: SOC_RMT_SUPPORT_SLEEP_RETENTION
除了时钟源的潜在变化外,当启用电源管理时,系统还可以关闭 RMT 寄存器所在的电源域。为确保 RMT 驱动程序在睡眠后继续工作,用户要么选择将 RMT 相关的寄存器备份到 RAM 中,要么拒绝关闭电源域。你可以根据应用需求在 :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep`:cpp:member:`rmt_rx_channel_config_t::backup_before_sleep` 中设置是否需要启用寄存器备份,在功耗和内存使用之间做权衡
除了时钟源的潜在变化外,当启用电源管理时,系统还可以在睡眠前关闭 RMT 电源。将 :cpp:member:`rmt_tx_channel_config_t::allow_pd`:cpp:member:`rmt_rx_channel_config_t::allow_pd` 设置为 ``true`` 以启用电源关闭功能。RMT 寄存器将在睡眠前备份,并在唤醒后恢复。请注意,启用此选项会增加内存消耗
.. _rmt-iram-safe: