Merge branch 'feat/twai_sleep_retention' into 'master'
twai: sleep retention support Closes IDF-8471, IDF-9772, and IDF-9743 See merge request espressif/esp-idf!34107
This commit is contained in:
@@ -12,6 +12,9 @@
|
||||
#include "driver/twai.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_private/sleep_cpu.h"
|
||||
#include "esp_private/esp_sleep_internal.h"
|
||||
#include "esp_private/esp_pmu.h"
|
||||
|
||||
TEST_CASE("driver_life_cycle", "[twai-loop-back]")
|
||||
{
|
||||
@@ -130,3 +133,90 @@ TEST_CASE("twai_mode_ext_no_ack_250kbps", "[twai-loop-back]")
|
||||
TEST_ESP_OK(twai_driver_uninstall_v2(twai_buses[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void s_test_sleep_retention(bool allow_pd)
|
||||
{
|
||||
// Prepare a TOP PD sleep
|
||||
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
|
||||
#if ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(true));
|
||||
#endif
|
||||
esp_sleep_context_t sleep_ctx;
|
||||
esp_sleep_set_sleep_context(&sleep_ctx);
|
||||
|
||||
twai_handle_t twai_buses[SOC_TWAI_CONTROLLER_NUM] = {0};
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
// bind the TX and RX to the same GPIO to act like a loopback
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
|
||||
g_config.general_flags.sleep_allow_pd = allow_pd;
|
||||
twai_message_t tx_msg = {
|
||||
.identifier = 0x12345,
|
||||
.data_length_code = 6,
|
||||
.data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
|
||||
.self = true, // Transmitted message will also received by the same node
|
||||
.extd = true, // Extended Frame Format (29bit ID)
|
||||
};
|
||||
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
g_config.controller_id = i;
|
||||
g_config.tx_io = i;
|
||||
g_config.rx_io = i;
|
||||
printf("install twai driver %d\r\n", g_config.controller_id);
|
||||
TEST_ESP_OK(twai_driver_install_v2(&g_config, &t_config, &f_config, &twai_buses[i]));
|
||||
}
|
||||
|
||||
printf("Going into sleep...\n");
|
||||
TEST_ESP_OK(esp_light_sleep_start());
|
||||
printf("Waked up!\n");
|
||||
|
||||
// check if the sleep happened as expected
|
||||
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
// check if the power domain also is powered down
|
||||
TEST_ASSERT_EQUAL(allow_pd ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
TEST_ESP_OK(twai_start_v2(twai_buses[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
printf("transmit message from %d\r\n", i);
|
||||
tx_msg.data[5] = SOC_TWAI_CONTROLLER_NUM - i;
|
||||
TEST_ESP_OK(twai_transmit_v2(twai_buses[i], &tx_msg, pdMS_TO_TICKS(1000)));
|
||||
}
|
||||
|
||||
twai_message_t rx_msg;
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
printf("receive message from %d\r\n", i);
|
||||
TEST_ESP_OK(twai_receive_v2(twai_buses[i], &rx_msg, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_TRUE(rx_msg.data_length_code == 6);
|
||||
tx_msg.data[5] = SOC_TWAI_CONTROLLER_NUM - i;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
TEST_ESP_OK(twai_stop_v2(twai_buses[i]));
|
||||
TEST_ESP_OK(twai_driver_uninstall_v2(twai_buses[i]));
|
||||
}
|
||||
|
||||
esp_sleep_set_sleep_context(NULL);
|
||||
#if ESP_SLEEP_POWER_DOWN_CPU
|
||||
TEST_ESP_OK(sleep_cpu_configure(false));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
TEST_CASE("twai_mode_ext_no_ack_250kbps with sleep retention (allow pd)", "[twai-loop-back]")
|
||||
{
|
||||
s_test_sleep_retention(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("twai_mode_ext_no_ack_250kbps with sleep retention (no pd)", "[twai-loop-back]")
|
||||
{
|
||||
s_test_sleep_retention(false);
|
||||
}
|
||||
|
||||
@@ -3,3 +3,5 @@ CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
# primitives for checking sleep internal state
|
||||
CONFIG_ESP_SLEEP_DEBUG=y
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -114,6 +114,11 @@ typedef struct {
|
||||
uint32_t alerts_enabled; /**< Bit field of alerts to enable (see documentation) */
|
||||
uint32_t clkout_divider; /**< CLKOUT divider. Can be 1 or any even number from 2 to 14 (optional, set to 0 if unused) */
|
||||
int intr_flags; /**< Interrupt flags to set the priority of the driver's ISR. Note that to use the ESP_INTR_FLAG_IRAM, the CONFIG_TWAI_ISR_IN_IRAM option should be enabled first. */
|
||||
struct {
|
||||
uint32_t sleep_allow_pd; /**< Set to allow power down. When this flag set, the driver will backup/restore the TWAI registers before/after entering/exist sleep mode.
|
||||
By this approach, the system can power off TWAI's power domain.
|
||||
This can save power, but at the expense of more RAM being consumed. */
|
||||
} general_flags; /**< General flags */
|
||||
} twai_general_config_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "hal/twai_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#if SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
#include "esp_private/sleep_retention.h"
|
||||
#endif
|
||||
|
||||
/* ---------------------------- Definitions --------------------------------- */
|
||||
//Internal Macros
|
||||
@@ -64,6 +67,8 @@
|
||||
#define TWAI_PERI_ATOMIC()
|
||||
#endif
|
||||
|
||||
#define TWAI_USE_RETENTION_LINK (SOC_TWAI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP)
|
||||
|
||||
/* ------------------ Typedefs, structures, and variables ------------------- */
|
||||
|
||||
//Control structure for TWAI driver
|
||||
@@ -96,6 +101,11 @@ typedef struct twai_obj_t {
|
||||
static twai_handle_t g_twai_objs[SOC_TWAI_CONTROLLER_NUM];
|
||||
static portMUX_TYPE g_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
/* -------------------- Sleep Retention ------------------------ */
|
||||
#if TWAI_USE_RETENTION_LINK
|
||||
static esp_err_t s_twai_create_sleep_retention_link_cb(void *obj);
|
||||
#endif
|
||||
|
||||
/* -------------------- Interrupt and Alert Handlers ------------------------ */
|
||||
|
||||
static void twai_alert_handler(twai_obj_t *p_twai_obj, uint32_t alert_code, int *alert_req)
|
||||
@@ -334,6 +344,19 @@ static void twai_free_driver_obj(twai_obj_t *p_obj)
|
||||
if (p_obj->alert_semphr != NULL) {
|
||||
vSemaphoreDeleteWithCaps(p_obj->alert_semphr);
|
||||
}
|
||||
|
||||
#if TWAI_USE_RETENTION_LINK
|
||||
const periph_retention_module_t retention_id = twai_reg_retention_info[p_obj->controller_id].module_id;
|
||||
if (sleep_retention_get_created_modules() & BIT(retention_id)) {
|
||||
assert(sleep_retention_get_inited_modules() & BIT(retention_id));
|
||||
sleep_retention_module_free(retention_id);
|
||||
}
|
||||
if (sleep_retention_get_inited_modules() & BIT(retention_id)) {
|
||||
sleep_retention_module_deinit(retention_id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
heap_caps_free(p_obj);
|
||||
}
|
||||
|
||||
@@ -365,6 +388,9 @@ static esp_err_t twai_alloc_driver_obj(const twai_general_config_t *g_config, tw
|
||||
if (ret != ESP_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
p_obj->controller_id = controller_id;
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
#if SOC_TWAI_CLK_SUPPORT_APB
|
||||
// DFS can change APB frequency. So add lock to prevent sleep and APB freq from changing
|
||||
@@ -384,6 +410,28 @@ static esp_err_t twai_alloc_driver_obj(const twai_general_config_t *g_config, tw
|
||||
#endif //SOC_TWAI_CLK_SUPPORT_APB
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
#if TWAI_USE_RETENTION_LINK
|
||||
sleep_retention_module_t module = twai_reg_retention_info[controller_id].module_id;
|
||||
sleep_retention_module_init_param_t init_param = {
|
||||
.cbs = {
|
||||
.create = {
|
||||
.handle = s_twai_create_sleep_retention_link_cb,
|
||||
.arg = p_obj,
|
||||
},
|
||||
},
|
||||
.depends = BIT(SLEEP_RETENTION_MODULE_CLOCK_SYSTEM)
|
||||
};
|
||||
if (sleep_retention_module_init(module, &init_param) != ESP_OK) {
|
||||
ESP_LOGW(TWAI_TAG, "init sleep retention failed for TWAI%d, power domain may be turned off during sleep", controller_id);
|
||||
}
|
||||
|
||||
if (g_config->general_flags.sleep_allow_pd) {
|
||||
if (sleep_retention_module_allocate(module) != ESP_OK) {
|
||||
ESP_LOGW(TWAI_TAG, "create retention module failed, power domain can't turn off");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*p_twai_obj_ret = p_obj;
|
||||
return ESP_OK;
|
||||
|
||||
@@ -408,6 +456,9 @@ esp_err_t twai_driver_install_v2(const twai_general_config_t *g_config, const tw
|
||||
#endif
|
||||
int controller_id = g_config->controller_id;
|
||||
TWAI_CHECK(g_twai_objs[controller_id] == NULL, ESP_ERR_INVALID_STATE);
|
||||
#if !SOC_TWAI_SUPPORT_SLEEP_RETENTION
|
||||
TWAI_CHECK(!g_config->general_flags.sleep_allow_pd, ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
|
||||
//Get clock source resolution
|
||||
uint32_t clock_source_hz = 0;
|
||||
@@ -437,7 +488,6 @@ esp_err_t twai_driver_install_v2(const twai_general_config_t *g_config, const tw
|
||||
|
||||
//Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj()
|
||||
portMUX_INITIALIZE(&p_twai_obj->spinlock);
|
||||
p_twai_obj->controller_id = controller_id;
|
||||
p_twai_obj->state = TWAI_STATE_STOPPED;
|
||||
p_twai_obj->mode = g_config->mode;
|
||||
p_twai_obj->alerts_enabled = g_config->alerts_enabled;
|
||||
@@ -874,3 +924,14 @@ esp_err_t twai_clear_receive_queue(void)
|
||||
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
|
||||
return twai_clear_receive_queue_v2(g_twai_objs[0]);
|
||||
}
|
||||
|
||||
#if TWAI_USE_RETENTION_LINK
|
||||
static esp_err_t s_twai_create_sleep_retention_link_cb(void *obj)
|
||||
{
|
||||
twai_obj_t *host = (twai_obj_t *)obj;
|
||||
return sleep_retention_entries_create(twai_reg_retention_info[host->controller_id].entry_array,
|
||||
twai_reg_retention_info[host->controller_id].array_size,
|
||||
REGDMA_LINK_PRI_TWAI,
|
||||
twai_reg_retention_info[host->controller_id].module_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user