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.
This commit is contained in:
Darian Leung 2020-12-03 18:21:43 +08:00
parent c2afe02507
commit fb2d6a44eb
8 changed files with 258 additions and 124 deletions

View File

@ -71,6 +71,29 @@ menu "Driver configurations"
endmenu # SPI Configuration
menu "CAN configuration"
config CAN_ISR_IN_IRAM
bool "Place CAN ISR function into IRAM"
default n
select FREERTOS_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
menu "UART configuration"
config UART_ISR_IN_IRAM

View File

@ -25,6 +25,8 @@
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "esp_pm.h"
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "driver/can.h"
@ -46,7 +48,14 @@
})
#define CAN_SET_FLAG(var, mask) ((var) |= (mask))
#define CAN_RESET_FLAG(var, mask) ((var) &= ~(mask))
#ifdef CONFIG_CAN_ISR_IN_IRAM
#define CAN_ISR_ATTR IRAM_ATTR
#define CAN_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define CAN_TAG "CAN"
#define CAN_ISR_ATTR
#define CAN_MALLOC_CAPS MALLOC_CAP_DEFAULT
#endif //CONFIG_CAN_ISR_IN_IRAM
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
@ -74,6 +83,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;
@ -99,12 +115,13 @@ static can_hal_context_t can_context;
/* -------------------- Interrupt and Alert Handlers ------------------------ */
static void can_alert_handler(uint32_t alert_code, int *alert_req)
CAN_ISR_ATTR static 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);
@ -114,6 +131,7 @@ static void can_alert_handler(uint32_t alert_code, int *alert_req)
ESP_EARLY_LOGI(CAN_TAG, "Alert %d", alert_code);
}
}
#endif //CONFIG_CAN_ISR_IN_IRAM
}
}
@ -241,7 +259,7 @@ static inline void can_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert
}
}
static void can_intr_handler_main(void *arg)
CAN_ISR_ATTR static void can_intr_handler_main(void *arg)
{
BaseType_t task_woken = pdFALSE;
int alert_req = 0;
@ -299,7 +317,7 @@ static void can_intr_handler_main(void *arg)
}
}
/* --------------------------- GPIO functions ------------------------------ */
/* -------------------------- Helper functions ----------------------------- */
static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
{
@ -329,6 +347,94 @@ 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)
{
//Allocates driver object and any dependent SW resources it uses (queues, semaphores etc)
//Create a CAN driver object
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_hal_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_hal_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_hal_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_hal_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_hal_frame_t));
}
p_obj->rx_queue = xQueueCreate(rx_queue_len, sizeof(can_hal_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)
@ -345,32 +451,21 @@ esp_err_t can_driver_install(const can_general_config_t *g_config, const can_tim
#else
CAN_CHECK(t_config->brp >= CAN_BRP_MIN && t_config->brp <= CAN_BRP_MAX, 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_hal_frame_t)) : NULL;
p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_hal_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. All other members are 0 initialized by calloc()
//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->mode = g_config->mode;
p_can_obj_dummy->alerts_enabled = g_config->alerts_enabled;
@ -395,35 +490,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;
}
@ -445,19 +520,12 @@ esp_err_t can_driver_uninstall(void)
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
//Free can driver object
can_free_driver_obj(p_can_obj_dummy);
return ESP_OK;
}

View File

@ -43,7 +43,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 Alert flags
@ -70,7 +71,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 (see docs). */
/** @endcond */
@ -103,6 +104,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_TWAI_ISR_IN_IRAM option should be enabled first. */
} can_general_config_t;
/**

View File

@ -41,6 +41,7 @@ if(IDF_TARGET STREQUAL "esp32")
list(APPEND srcs "src/hal/mcpwm_hal.c"
"src/hal/sdio_slave_hal.c"
"src/hal/can_hal.c"
"src/hal/can_hal_iram.c"
)
endif()

View File

@ -21,3 +21,7 @@ entries:
i2c_hal_iram (noflash)
spi_flash_hal_gpspi (noflash)
lldesc (noflash_text)
if CAN_ISR_IN_IRAM = y:
can_hal_iram (noflash)
else:
can_hal_iram (default)

View File

@ -76,69 +76,4 @@ bool can_hal_stop(can_hal_context_t *hal_ctx)
(void) can_ll_get_and_clear_intrs(hal_ctx->dev);
can_ll_set_mode(hal_ctx->dev, CAN_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
return true;
}
uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering)
{
uint32_t events = 0;
//Read interrupt, status
uint32_t interrupts = can_ll_get_and_clear_intrs(hal_ctx->dev);
uint32_t status = can_ll_get_status(hal_ctx->dev);
uint32_t tec = can_ll_get_tec(hal_ctx->dev);
uint32_t rec = can_ll_get_rec(hal_ctx->dev);
//Receive Interrupt set whenever RX FIFO is not empty
if (interrupts & CAN_LL_INTR_RI) {
events |= CAN_HAL_EVENT_RX_BUFF_FRAME;
}
//Transmit interrupt set whenever TX buffer becomes free
if (interrupts & CAN_LL_INTR_TI) {
events |= CAN_HAL_EVENT_TX_BUFF_FREE;
}
//Error Warning Interrupt set whenever Error or Bus Status bit changes
if (interrupts & CAN_LL_INTR_EI) {
if (status & CAN_LL_STATUS_BS) {
//Currently in BUS OFF state
//EWL is exceeded, thus must have entered BUS OFF
//Below EWL. Therefore TEC is counting down in bus recovery
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_BUS_OFF : CAN_HAL_EVENT_BUS_RECOV_PROGRESS;
} else {
//Not in BUS OFF
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL
((bus_recovering) ? //If previously undergoing bus recovery
CAN_HAL_EVENT_BUS_RECOV_CPLT :
CAN_HAL_EVENT_BELOW_EWL);
}
}
//Error Passive Interrupt on transition from error active to passive or vice versa
if (interrupts & CAN_LL_INTR_EPI) {
events |= (tec >= CAN_ERR_PASS_THRESH || rec >= CAN_ERR_PASS_THRESH) ? CAN_HAL_EVENT_ERROR_PASSIVE : CAN_HAL_EVENT_ERROR_ACTIVE;
}
//Arbitration Lost Interrupt triggered on losing arbitration
if (interrupts & CAN_LL_INTR_ALI) {
events |= CAN_HAL_EVENT_ARB_LOST;
}
//Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
if (interrupts & CAN_LL_INTR_BEI) {
events |= CAN_HAL_EVENT_BUS_ERR;
}
return events;
}
void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame)
{
//Copy frame into tx buffer
can_ll_set_tx_buffer(hal_ctx->dev, tx_frame);
//Hit the send command
if (tx_frame->self_reception) {
if (tx_frame->single_shot) {
can_ll_set_cmd_self_rx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_self_rx_request(hal_ctx->dev);
}
} else if (tx_frame->single_shot){
can_ll_set_cmd_tx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_tx(hal_ctx->dev);
}
}
}

View File

@ -0,0 +1,81 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "hal/can_hal.h"
uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering)
{
uint32_t events = 0;
//Read interrupt, status
uint32_t interrupts = can_ll_get_and_clear_intrs(hal_ctx->dev);
uint32_t status = can_ll_get_status(hal_ctx->dev);
uint32_t tec = can_ll_get_tec(hal_ctx->dev);
uint32_t rec = can_ll_get_rec(hal_ctx->dev);
//Receive Interrupt set whenever RX FIFO is not empty
if (interrupts & CAN_LL_INTR_RI) {
events |= CAN_HAL_EVENT_RX_BUFF_FRAME;
}
//Transmit interrupt set whenever TX buffer becomes free
if (interrupts & CAN_LL_INTR_TI) {
events |= CAN_HAL_EVENT_TX_BUFF_FREE;
}
//Error Warning Interrupt set whenever Error or Bus Status bit changes
if (interrupts & CAN_LL_INTR_EI) {
if (status & CAN_LL_STATUS_BS) {
//Currently in BUS OFF state
//EWL is exceeded, thus must have entered BUS OFF
//Below EWL. Therefore TEC is counting down in bus recovery
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_BUS_OFF : CAN_HAL_EVENT_BUS_RECOV_PROGRESS;
} else {
//Not in BUS OFF
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL
((bus_recovering) ? //If previously undergoing bus recovery
CAN_HAL_EVENT_BUS_RECOV_CPLT :
CAN_HAL_EVENT_BELOW_EWL);
}
}
//Error Passive Interrupt on transition from error active to passive or vice versa
if (interrupts & CAN_LL_INTR_EPI) {
events |= (tec >= CAN_ERR_PASS_THRESH || rec >= CAN_ERR_PASS_THRESH) ? CAN_HAL_EVENT_ERROR_PASSIVE : CAN_HAL_EVENT_ERROR_ACTIVE;
}
//Arbitration Lost Interrupt triggered on losing arbitration
if (interrupts & CAN_LL_INTR_ALI) {
events |= CAN_HAL_EVENT_ARB_LOST;
}
//Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
if (interrupts & CAN_LL_INTR_BEI) {
events |= CAN_HAL_EVENT_BUS_ERR;
}
return events;
}
void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame)
{
//Copy frame into tx buffer
can_ll_set_tx_buffer(hal_ctx->dev, tx_frame);
//Hit the send command
if (tx_frame->self_reception) {
if (tx_frame->single_shot) {
can_ll_set_cmd_self_rx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_self_rx_request(hal_ctx->dev);
}
} else if (tx_frame->single_shot){
can_ll_set_cmd_tx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_tx(hal_ctx->dev);
}
}

View File

@ -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 ---------------------------------