From 540b401e95d1bc6699202be4ac83fed72dbe2ceb Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Wed, 2 Dec 2020 21:35:07 +0800 Subject: [PATCH 1/4] CAN: Fix size of RX msg count field on the esp32 This commit fixes the size of the RX message count register field on the esp32. --- components/soc/esp32/include/soc/can_struct.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/soc/esp32/include/soc/can_struct.h b/components/soc/esp32/include/soc/can_struct.h index 81ab02ca07..a2c1ea63fc 100644 --- a/components/soc/esp32/include/soc/can_struct.h +++ b/components/soc/esp32/include/soc/can_struct.h @@ -147,8 +147,8 @@ typedef struct { typedef union { struct { - uint32_t rx_message_counter: 5; /* RMC[4:0] RX Message Counter */ - uint32_t reserved27: 27; /* Internal Reserved */ + uint32_t rx_message_counter: 7; /* RMC[6:0] RX Message Counter */ + uint32_t reserved25: 25; /* Internal Reserved */ }; uint32_t val; } can_rx_msg_cnt_reg_t; From 35511d0e8705084b5befc179f9ff046ff1965751 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Fri, 20 Dec 2019 21:57:26 +0800 Subject: [PATCH 2/4] can: Fix semaphore take in critical section This commit fixes can_reconfigure_alerts() which could lead to a call to xSemaphoreTake() whilst inside a critical section. --- components/driver/can.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/driver/can.c b/components/driver/can.c index ddfd1e5d68..e70430053b 100644 --- a/components/driver/can.c +++ b/components/driver/can.c @@ -953,15 +953,16 @@ esp_err_t can_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait) esp_err_t can_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts) { CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE); + CAN_ENTER_CRITICAL(); - uint32_t cur_alerts; - can_read_alerts(&cur_alerts, 0); //Clear any unhandled alerts + //Clear any unhandled alerts + if (current_alerts != NULL) { + *current_alerts = p_can_obj->alerts_triggered;; + } + p_can_obj->alerts_triggered = 0; p_can_obj->alerts_enabled = alerts_enabled; //Update enabled alerts CAN_EXIT_CRITICAL(); - if (current_alerts != NULL) { - *current_alerts = cur_alerts; - } return ESP_OK; } From 3574ca6156b6e71039b017f2777b0cf8138ce56e Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Fri, 20 Dec 2019 19:45:27 +0800 Subject: [PATCH 3/4] can: Fix critical section ESP_LOG functions This commit removes any function calls within the CAN driver that result in a call to ESP_LOG whilst inside a critical section. These function calls are either moved outside critical sections (e.g., intr_alloc and gpio functions), or substituted (e.g., assert()). --- components/driver/can.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/driver/can.c b/components/driver/can.c index e70430053b..f1ea0a69ee 100644 --- a/components/driver/can.c +++ b/components/driver/can.c @@ -719,13 +719,14 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim can_config_error(DRIVER_DEFAULT_EWL, DRIVER_DEFAULT_REC, DRIVER_DEFAULT_TEC); can_config_acceptance_filter(f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter); can_config_clk_out(g_config->clkout_divider); - //Allocate GPIO and Interrupts - can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io); (void) can_get_interrupt_reason(); //Read interrupt reg to clear it before allocating ISR - ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle)); //Todo: Allow interrupt to be registered to specified CPU CAN_EXIT_CRITICAL(); + //Allocate GPIO and Interrupts + can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io); + ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle)); + #ifdef CONFIG_PM_ENABLE ESP_ERROR_CHECK(esp_pm_lock_acquire(p_can_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz #endif @@ -770,13 +771,13 @@ esp_err_t can_driver_uninstall() (void) can_get_interrupt_reason(); (void) can_get_arbitration_lost_capture(); (void) can_get_error_code_capture(); - - ESP_ERROR_CHECK(esp_intr_free(p_can_obj->isr_handle)); //Free interrupt periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral p_can_obj_dummy = p_can_obj; //Use dummy to shorten critical section p_can_obj = NULL; CAN_EXIT_CRITICAL(); + ESP_ERROR_CHECK(esp_intr_free(p_can_obj_dummy->isr_handle)); //Free interrupt + //Delete queues, semaphores, and power management locks if (p_can_obj_dummy->tx_queue != NULL) { vQueueDelete(p_can_obj_dummy->tx_queue); From fcabc257a5a8d5e61a789011c8c77813ab3b7c38 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Mon, 23 Nov 2020 23:41:09 +0800 Subject: [PATCH 4/4] CAN: ISR runs when cache is disabled This commit adds the feature where the CAN ISR will continue to run even if the cache is disabled. Whilst cache is disabled, any received messages will go into the RX queue, and any pending TX messages in the TX queue will be transmitted. This feature should be enabled using the CONFIG_CAN_ISR_IN_IRAM option. --- components/driver/Kconfig | 23 +++ components/driver/can.c | 221 ++++++++++++++-------- components/driver/include/driver/can.h | 6 +- docs/en/api-reference/peripherals/can.rst | 22 ++- 4 files changed, 189 insertions(+), 83 deletions(-) diff --git a/components/driver/Kconfig b/components/driver/Kconfig index f00d7e8099..5cdd719607 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -71,4 +71,27 @@ menu "Driver configurations" endmenu # SPI Configuration + menu "CAN Configuration" + + config CAN_ISR_IN_IRAM + bool "Place CAN ISR function into IRAM" + default n + select SUPPORT_STATIC_ALLOCATION + # We need to enable FREERTOS_SUPPORT_STATIC_ALLOCATION because the + # CAN driver requires the use of FreeRTOS Queues and Semaphores from + # the driver ISR. These Queues and Semaphores need to be placed in + # DRAM thus FreeRTOS static allocation API is required. + help + Place the CAN ISR in to IRAM. This will allow the ISR to avoid + cache misses, and also be able to run whilst the cache is disabled + (such as when writing to SPI Flash). + Note that if this option is enabled: + - Users should also set the ESP_INTR_FLAG_IRAM in the driver + configuration structure when installing the driver (see docs for + specifics). + - Alert logging (i.e., setting of the CAN_ALERT_AND_LOG flag) + will have no effect. + + endmenu # CAN Configuration + endmenu # Driver configurations diff --git a/components/driver/can.c b/components/driver/can.c index f1ea0a69ee..e38c467af6 100644 --- a/components/driver/can.c +++ b/components/driver/can.c @@ -21,6 +21,8 @@ #include "esp_log.h" #include "esp_intr_alloc.h" #include "esp_pm.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" #include "soc/dport_reg.h" #include "soc/can_struct.h" #include "driver/gpio.h" @@ -42,7 +44,16 @@ }) #define CAN_SET_FLAG(var, mask) ((var) |= (mask)) #define CAN_RESET_FLAG(var, mask) ((var) &= ~(mask)) +#ifdef CONFIG_CAN_ISR_IN_IRAM +#define CAN_INLINE_ATTR __attribute__((always_inline)) +#define CAN_ISR_ATTR IRAM_ATTR +#define CAN_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#else +#define CAN_INLINE_ATTR #define CAN_TAG "CAN" +#define CAN_ISR_ATTR +#define CAN_MALLOC_CAPS MALLOC_CAP_DEFAULT +#endif /* * Baud Rate Prescaler Divider config/values. The BRP_DIV bit is located in the @@ -135,6 +146,13 @@ typedef struct { uint32_t bus_error_count; intr_handle_t isr_handle; //TX and RX +#ifdef CONFIG_CAN_ISR_IN_IRAM + void *tx_queue_buff; + void *tx_queue_struct; + void *rx_queue_buff; + void *rx_queue_struct; + void *semphr_struct; +#endif QueueHandle_t tx_queue; QueueHandle_t rx_queue; int tx_msg_count; @@ -158,7 +176,7 @@ static portMUX_TYPE can_spinlock = portMUX_INITIALIZER_UNLOCKED; /* ------------------- Configuration Register Functions---------------------- */ -static inline esp_err_t can_enter_reset_mode() +static inline CAN_INLINE_ATTR esp_err_t can_enter_reset_mode() { /* Enter reset mode (required to write to configuration registers). Reset mode also prevents all CAN activity on the current module and is automatically @@ -183,7 +201,7 @@ static inline void can_config_pelican() CAN.clock_divider_reg.can_mode = 1; } -static inline void can_config_mode(can_mode_t mode) +static CAN_ISR_ATTR void can_config_mode(can_mode_t mode) { //Configure CAN mode of operation can_mode_reg_t mode_reg; @@ -281,12 +299,12 @@ static inline void can_config_clk_out(uint32_t divider) /* ---------------------- Runtime Register Functions------------------------- */ -static inline void can_set_command(uint8_t commands) +static inline CAN_INLINE_ATTR void can_set_command(uint8_t commands) { CAN.command_reg.val = commands; } -static void can_set_tx_buffer_and_transmit(can_frame_t *frame) +static CAN_ISR_ATTR void can_set_tx_buffer_and_transmit(can_frame_t *frame) { //Copy frame structure into TX buffer registers for (int i = 0; i < FRAME_MAX_LEN; i++) { @@ -303,29 +321,29 @@ static void can_set_tx_buffer_and_transmit(can_frame_t *frame) can_set_command(command); } -static inline uint32_t can_get_status() +static inline CAN_INLINE_ATTR uint32_t can_get_status() { return CAN.status_reg.val; } -static inline uint32_t can_get_interrupt_reason() +static inline CAN_INLINE_ATTR uint32_t can_get_interrupt_reason() { return CAN.interrupt_reg.val; } -static inline uint32_t can_get_arbitration_lost_capture() +static inline CAN_INLINE_ATTR uint32_t can_get_arbitration_lost_capture() { return CAN.arbitration_lost_captue_reg.val; //Todo: ALC read only to re-arm arb lost interrupt. Add function to decode ALC } -static inline uint32_t can_get_error_code_capture() +static inline CAN_INLINE_ATTR uint32_t can_get_error_code_capture() { return CAN.error_code_capture_reg.val; //Todo: ECC read only to re-arm bus error interrupt. Add function to decode ECC } -static inline void can_get_error_counters(uint32_t *tx_error_cnt, uint32_t *rx_error_cnt) +static inline CAN_INLINE_ATTR void can_get_error_counters(uint32_t *tx_error_cnt, uint32_t *rx_error_cnt) { if (tx_error_cnt != NULL) { *tx_error_cnt = CAN.tx_error_counter_reg.byte; @@ -335,7 +353,7 @@ static inline void can_get_error_counters(uint32_t *tx_error_cnt, uint32_t *rx_e } } -static inline void can_get_rx_buffer_and_clear(can_frame_t *frame) +static CAN_ISR_ATTR void can_get_rx_buffer_and_clear(can_frame_t *frame) { //Copy RX buffer registers into frame structure for (int i = 0; i < FRAME_MAX_LEN; i++) { @@ -345,19 +363,20 @@ static inline void can_get_rx_buffer_and_clear(can_frame_t *frame) can_set_command(CMD_RELEASE_RX_BUFF); } -static inline uint32_t can_get_rx_message_counter() +static inline CAN_INLINE_ATTR uint32_t can_get_rx_message_counter() { return CAN.rx_message_counter_reg.val; } /* -------------------- Interrupt and Alert Handlers ------------------------ */ -static void can_alert_handler(uint32_t alert_code, int *alert_req) +static CAN_ISR_ATTR void can_alert_handler(uint32_t alert_code, int *alert_req) { if (p_can_obj->alerts_enabled & alert_code) { //Signify alert has occurred CAN_SET_FLAG(p_can_obj->alerts_triggered, alert_code); *alert_req = 1; +#ifndef CONFIG_CAN_ISR_IN_IRAM //Only log if ISR is not in IRAM if (p_can_obj->alerts_enabled & CAN_ALERT_AND_LOG) { if (alert_code >= ALERT_LOG_LEVEL_ERROR) { ESP_EARLY_LOGE(CAN_TAG, "Alert %d", alert_code); @@ -367,10 +386,11 @@ static void can_alert_handler(uint32_t alert_code, int *alert_req) ESP_EARLY_LOGI(CAN_TAG, "Alert %d", alert_code); } } +#endif } } -static void can_intr_handler_err_warn(can_status_reg_t *status, BaseType_t *task_woken, int *alert_req) +static CAN_ISR_ATTR void can_intr_handler_err_warn(can_status_reg_t *status, BaseType_t *task_woken, int *alert_req) { if (status->bus) { if (status->error) { @@ -408,7 +428,7 @@ static void can_intr_handler_err_warn(can_status_reg_t *status, BaseType_t *task } } -static void can_intr_handler_err_passive(int *alert_req) +static inline CAN_INLINE_ATTR void can_intr_handler_err_passive(int *alert_req) { uint32_t tec, rec; can_get_error_counters(&tec, &rec); @@ -423,7 +443,7 @@ static void can_intr_handler_err_passive(int *alert_req) } } -static void can_intr_handler_bus_err(int *alert_req) +static inline CAN_INLINE_ATTR void can_intr_handler_bus_err(int *alert_req) { // ECC register is read to re-arm bus error interrupt. ECC is not used (void) can_get_error_code_capture(); @@ -431,7 +451,7 @@ static void can_intr_handler_bus_err(int *alert_req) can_alert_handler(CAN_ALERT_BUS_ERROR, alert_req); } -static void can_intr_handler_arb_lost(int *alert_req) +static inline CAN_INLINE_ATTR void can_intr_handler_arb_lost(int *alert_req) { //ALC register is read to re-arm arb lost interrupt. ALC is not used (void) can_get_arbitration_lost_capture(); @@ -439,7 +459,7 @@ static void can_intr_handler_arb_lost(int *alert_req) can_alert_handler(CAN_ALERT_ARB_LOST, alert_req); } -static void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req) +static inline CAN_INLINE_ATTR CAN_ISR_ATTR void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req) { can_rx_msg_cnt_reg_t msg_count_reg; msg_count_reg.val = can_get_rx_message_counter(); @@ -459,7 +479,7 @@ static void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req) //Todo: Check for data overrun of RX FIFO, then trigger alert } -static void can_intr_handler_tx(can_status_reg_t *status, int *alert_req) +static CAN_ISR_ATTR void can_intr_handler_tx(can_status_reg_t *status, int *alert_req) { //Handle previously transmitted frame if (status->tx_complete) { @@ -489,7 +509,7 @@ static void can_intr_handler_tx(can_status_reg_t *status, int *alert_req) } } -static void can_intr_handler_main(void *arg) +static CAN_ISR_ATTR void can_intr_handler_main(void *arg) { BaseType_t task_woken = pdFALSE; int alert_req = 0; @@ -542,7 +562,7 @@ static void can_intr_handler_main(void *arg) } } -/* ---------------------- Frame and GPIO functions ------------------------- */ +/* -------------------------- Helper functions ----------------------------- */ static void can_format_frame(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, can_frame_t *tx_frame) { @@ -635,6 +655,93 @@ static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, } } +static void can_free_driver_obj(can_obj_t *p_obj) +{ + //Free driver object and any dependent SW resources it uses (queues, semaphores etc) +#ifdef CONFIG_PM_ENABLE + if (p_obj->pm_lock != NULL) { + ESP_ERROR_CHECK(esp_pm_lock_delete(p_obj->pm_lock)); + } +#endif + //Delete queues and semaphores + if (p_obj->tx_queue != NULL) { + vQueueDelete(p_obj->tx_queue); + } + if (p_obj->rx_queue != NULL) { + vQueueDelete(p_obj->rx_queue); + } + if (p_obj->alert_semphr != NULL) { + vSemaphoreDelete(p_obj->alert_semphr); + } +#ifdef CONFIG_CAN_ISR_IN_IRAM + //Free memory used by static queues and semaphores. free() allows freeing NULL pointers + free(p_obj->tx_queue_buff); + free(p_obj->tx_queue_struct); + free(p_obj->rx_queue_buff); + free(p_obj->rx_queue_struct); + free(p_obj->semphr_struct); +#endif //CONFIG_CAN_ISR_IN_IRAM + free(p_obj); +} + +static can_obj_t *can_alloc_driver_obj(uint32_t tx_queue_len, uint32_t rx_queue_len) +{ + can_obj_t *p_obj = heap_caps_calloc(1, sizeof(can_obj_t), CAN_MALLOC_CAPS); + if (p_obj == NULL) { + return NULL; + } +#ifdef CONFIG_CAN_ISR_IN_IRAM + //Allocate memory for queues and semaphores in DRAM + if (tx_queue_len > 0) { + p_obj->tx_queue_buff = heap_caps_calloc(tx_queue_len, sizeof(can_frame_t), CAN_MALLOC_CAPS); + p_obj->tx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), CAN_MALLOC_CAPS); + if (p_obj->tx_queue_buff == NULL || p_obj->tx_queue_struct == NULL) { + goto cleanup; + } + } + p_obj->rx_queue_buff = heap_caps_calloc(rx_queue_len, sizeof(can_frame_t), CAN_MALLOC_CAPS); + p_obj->rx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), CAN_MALLOC_CAPS); + p_obj->semphr_struct = heap_caps_calloc(1, sizeof(StaticSemaphore_t), CAN_MALLOC_CAPS); + if (p_obj->rx_queue_buff == NULL || p_obj->rx_queue_struct == NULL || p_obj->semphr_struct == NULL) { + goto cleanup; + } + //Create static queues and semaphores + if (tx_queue_len > 0) { + p_obj->tx_queue = xQueueCreateStatic(tx_queue_len, sizeof(can_frame_t), p_obj->tx_queue_buff, p_obj->tx_queue_struct); + if (p_obj->tx_queue == NULL) { + goto cleanup; + } + } + p_obj->rx_queue = xQueueCreateStatic(rx_queue_len, sizeof(can_frame_t), p_obj->rx_queue_buff, p_obj->rx_queue_struct); + p_obj->alert_semphr = xSemaphoreCreateBinaryStatic(p_obj->semphr_struct); + if (p_obj->rx_queue == NULL || p_obj->alert_semphr == NULL) { + goto cleanup; + } + +#else //CONFIG_CAN_ISR_IN_IRAM + if (tx_queue_len > 0) { + p_obj->tx_queue = xQueueCreate(tx_queue_len, sizeof(can_frame_t)); + } + p_obj->rx_queue = xQueueCreate(rx_queue_len, sizeof(can_frame_t)); + p_obj->alert_semphr = xSemaphoreCreateBinary(); + if ((tx_queue_len > 0 && p_obj->tx_queue == NULL) || p_obj->rx_queue == NULL || p_obj->alert_semphr == NULL) { + goto cleanup; + } +#endif //CONFIG_CAN_ISR_IN_IRAM + +#ifdef CONFIG_PM_ENABLE + esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "can", &(p_obj->pm_lock)); + if (pm_err != ESP_OK ) { + goto cleanup; + } +#endif + return p_obj; + +cleanup: + can_free_driver_obj(p_obj); + return NULL; +} + /* ---------------------------- Public Functions ---------------------------- */ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_timing_config_t *t_config, const can_filter_config_t *f_config) @@ -652,43 +759,26 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim #else CAN_CHECK(BRP_CHECK_NO_DIV(t_config->brp), ESP_ERR_INVALID_ARG); #endif +#ifndef CONFIG_CAN_ISR_IN_IRAM + CAN_CHECK(!(g_config->intr_flags & ESP_INTR_FLAG_IRAM), ESP_ERR_INVALID_ARG); +#endif + CAN_ENTER_CRITICAL(); + CAN_CHECK_FROM_CRIT(p_can_obj == NULL, ESP_ERR_INVALID_STATE); + CAN_EXIT_CRITICAL(); + esp_err_t ret; can_obj_t *p_can_obj_dummy; - //Create a CAN object - p_can_obj_dummy = calloc(1, sizeof(can_obj_t)); + //Create a CAN object (including queues and semaphores) + p_can_obj_dummy = can_alloc_driver_obj(g_config->tx_queue_len, g_config->rx_queue_len); CAN_CHECK(p_can_obj_dummy != NULL, ESP_ERR_NO_MEM); - //Initialize queues, semaphores, and power management locks - p_can_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_frame_t)) : NULL; - p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t)); - p_can_obj_dummy->alert_semphr = xSemaphoreCreateBinary(); - if ((g_config->tx_queue_len > 0 && p_can_obj_dummy->tx_queue == NULL) || - p_can_obj_dummy->rx_queue == NULL || p_can_obj_dummy->alert_semphr == NULL) { - ret = ESP_ERR_NO_MEM; - goto err; - } -#ifdef CONFIG_PM_ENABLE - esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "can", &(p_can_obj_dummy->pm_lock)); - if (pm_err != ESP_OK ) { - ret = pm_err; - goto err; - } -#endif - - //Initialize flags and variables + //Initialize flags and variables. All other members are already set to zero by can_alloc_driver_obj() p_can_obj_dummy->control_flags = CTRL_FLAG_STOPPED; p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0; p_can_obj_dummy->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0; - p_can_obj_dummy->tx_msg_count = 0; - p_can_obj_dummy->rx_msg_count = 0; - p_can_obj_dummy->tx_failed_count = 0; - p_can_obj_dummy->rx_missed_count = 0; - p_can_obj_dummy->arb_lost_count = 0; - p_can_obj_dummy->bus_error_count = 0; p_can_obj_dummy->alerts_enabled = g_config->alerts_enabled; - p_can_obj_dummy->alerts_triggered = 0; //Initialize CAN peripheral registers, and allocate interrupt CAN_ENTER_CRITICAL(); @@ -725,35 +815,15 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim //Allocate GPIO and Interrupts can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io); - ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle)); + ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, g_config->intr_flags, can_intr_handler_main, NULL, &p_can_obj->isr_handle)); #ifdef CONFIG_PM_ENABLE ESP_ERROR_CHECK(esp_pm_lock_acquire(p_can_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz #endif return ESP_OK; //CAN module is still in reset mode, users need to call can_start() afterwards - err: - //Cleanup CAN object and return error - if (p_can_obj_dummy != NULL) { - if (p_can_obj_dummy->tx_queue != NULL) { - vQueueDelete(p_can_obj_dummy->tx_queue); - p_can_obj_dummy->tx_queue = NULL; - } - if (p_can_obj_dummy->rx_queue != NULL) { - vQueueDelete(p_can_obj_dummy->rx_queue); - p_can_obj_dummy->rx_queue = NULL; - } - if (p_can_obj_dummy->alert_semphr != NULL) { - vSemaphoreDelete(p_can_obj_dummy->alert_semphr); - p_can_obj_dummy->alert_semphr = NULL; - } -#ifdef CONFIG_PM_ENABLE - if (p_can_obj_dummy->pm_lock != NULL) { - ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock)); - } -#endif - free(p_can_obj_dummy); - } +err: + can_free_driver_obj(p_can_obj_dummy); return ret; } @@ -777,20 +847,11 @@ esp_err_t can_driver_uninstall() CAN_EXIT_CRITICAL(); ESP_ERROR_CHECK(esp_intr_free(p_can_obj_dummy->isr_handle)); //Free interrupt - - //Delete queues, semaphores, and power management locks - if (p_can_obj_dummy->tx_queue != NULL) { - vQueueDelete(p_can_obj_dummy->tx_queue); - } - vQueueDelete(p_can_obj_dummy->rx_queue); - vSemaphoreDelete(p_can_obj_dummy->alert_semphr); #ifdef CONFIG_PM_ENABLE //Release and delete power management lock ESP_ERROR_CHECK(esp_pm_lock_release(p_can_obj_dummy->pm_lock)); - ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock)); #endif - free(p_can_obj_dummy); //Free can driver object - + can_free_driver_obj(p_can_obj_dummy); return ESP_OK; } diff --git a/components/driver/include/driver/can.h b/components/driver/include/driver/can.h index 28d42077fb..f4315cfc1d 100644 --- a/components/driver/include/driver/can.h +++ b/components/driver/include/driver/can.h @@ -37,7 +37,8 @@ extern "C" { #define CAN_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \ .clkout_io = CAN_IO_UNUSED, .bus_off_io = CAN_IO_UNUSED, \ .tx_queue_len = 5, .rx_queue_len = 5, \ - .alerts_enabled = CAN_ALERT_NONE, .clkout_divider = 0, } + .alerts_enabled = CAN_ALERT_NONE, .clkout_divider = 0, \ + .intr_flags = ESP_INTR_FLAG_LEVEL1} /** * @brief Initializer macros for timing configuration structure @@ -91,7 +92,7 @@ extern "C" { #define CAN_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. CAN controller can no longer influence bus */ #define CAN_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */ #define CAN_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */ -#define CAN_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur */ +#define CAN_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled if CONFIG_TWAI_ISR_IN_IRAM is enabled. */ /** * @brief Message flags @@ -151,6 +152,7 @@ typedef struct { uint32_t rx_queue_len; /**< Number of messages RX queue can hold */ 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_CAN_ISR_IN_IRAM option should be enabled first. */ } can_general_config_t; /** diff --git a/docs/en/api-reference/peripherals/can.rst b/docs/en/api-reference/peripherals/can.rst index ce190de6ad..5b61ba20d8 100644 --- a/docs/en/api-reference/peripherals/can.rst +++ b/docs/en/api-reference/peripherals/can.rst @@ -157,7 +157,10 @@ The CAN driver contains an alert feature which is used to notify the application The **error warning limit** can be used to preemptively warn the application of bus errors before the error passive state is reached. By default the CAN driver sets the **error warning limit** to **96**. The ``CAN_ALERT_ABOVE_ERR_WARN`` is raised when the TEC or REC becomes larger then or equal to the error warning limit. The ``CAN_ALERT_BELOW_ERR_WARN`` is raised when both TEC and REC return back to values below **96**. .. note:: - When enabling alerts, the ``CAN_ALERT_AND_LOG`` flag can be used to cause the CAN driver to log any raised alerts to UART. The ``CAN_ALERT_ALL`` and ``CAN_ALERT_NONE`` macros can also be used to enable/disable all alerts during configuration/reconfiguration. + When enabling alerts, the ``CAN_ALERT_AND_LOG`` flag can be used to cause the CAN driver to log any raised alerts to UART. However, alert logging is disabled and ``CAN_ALERT_AND_LOG`` if the :ref:`CONFIG_CAN_ISR_IN_IRAM` option is enabled (see :ref:`placing-isr-into-iram`). + +.. note:: + The ``CAN_ALERT_ALL`` and ``CAN_ALERT_NONE`` macros can also be used to enable/disable all alerts during configuration/reconfiguration. Bit Timing ^^^^^^^^^^ @@ -223,6 +226,23 @@ Disabling TX Queue The TX queue can be disabled during configuration by setting the ``tx_queue_len`` member of :cpp:type:`can_general_config_t` to ``0``. This will allow applications that do not require message transmission to save a small amount of memory when using the CAN driver. +.. _placing-isr-into-iram: + +Placing ISR into IRAM +^^^^^^^^^^^^^^^^^^^^^ + +The CAN driver's ISR (Interrupt Service Routine) can be placed into IRAM so that the ISR can still run whilst the cache is disabled. Placing the ISR into IRAM may be necessary to maintain the CAN driver's functionality during lengthy cache disabling operations (such as SPI Flash writes, OTA updates etc). Whilst the cache is disabled, the ISR will continue to: + +- Read received messages from the RX buffer and place them into the driver's RX queue. +- Load messages pending transmission from the driver's TX queue and write them into the TX buffer. + +To place the CAN driver's ISR, users must do the following: + +- Enable the :ref:`CONFIG_CAN_ISR_IN_IRAM` option using ``idf.py menuconfig``. +- When calling :cpp:func:`can_driver_install`, the `intr_flags` member of :cpp:type:`can_general_config_t` should set the :c:macro:`ESP_INTR_FLAG_IRAM` set. + +.. note:: + When the :ref:`CONFIG_CAN_ISR_IN_IRAM` option is enabled, the CAN driver will no longer log any alerts (i.e., the ``CAN_ALERT_AND_LOG`` flag will not have any effect). .. -------------------------------- CAN Driver ---------------------------------