I'm trying to achieve ultra-low latency handling of TWAI (CAN bus) messages on an ESP32-S3 by placing the ISR in IRAM. I've followed the documentation on -Placing ISR into IRAM- and enabled CONFIG_TWAI_ISR_IN_IRAM in my project configuration.
https://docs.espressif.com/projects/esp ... /twai.html
Here's my current implementation:
Code: Select all
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/twai.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_attr.h"
#include "esp_private/periph_ctrl.h"
#include "soc/soc.h"
#include "soc/interrupts.h"
#include "esp_intr_types.h"
// CAN Configuration
#define CAN_TX_GPIO_NUM GPIO_NUM_5
#define CAN_RX_GPIO_NUM GPIO_NUM_4
#define TWAI_RX_QUEUE_LENGTH 10
static const char *TAG = "CANBUS";
static QueueHandle_t twai_rx_queue = NULL;
static intr_handle_t twai_intr_handle = NULL;
// === IRAM ISR Handler ===
static void IRAM_ATTR twai_isr_handler(void *arg) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
twai_message_t msg;
while (twai_receive(&msg, 0) == ESP_OK) {
xQueueSendFromISR(twai_rx_queue, &msg, &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
// === Task to Process Messages from ISR ===
void twai_receive_task(void *arg) {
twai_message_t msg;
while (1) {
if (xQueueReceive(twai_rx_queue, &msg, portMAX_DELAY) == pdTRUE) {
ESP_LOGI(TAG, "RX ID: 0x%"PRIu32" DLC: %d", msg.identifier, msg.data_length_code);
for (int i = 0; i < msg.data_length_code; i++) {
printf("0x%02X ", msg.data[i]);
}
printf("\n");
}
}
}
// === TWAI Init ===
void twai_init(void) {
// Initialize configuration structures using macro initializers
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(CAN_TX_GPIO_NUM, CAN_RX_GPIO_NUM, TWAI_MODE_NORMAL);
g_config.controller_id = 0;
g_config.mode = TWAI_MODE_NORMAL;
g_config.tx_io = CAN_TX_GPIO_NUM;
g_config.rx_io = CAN_RX_GPIO_NUM;
g_config.clkout_io = TWAI_IO_UNUSED;
g_config.bus_off_io = TWAI_IO_UNUSED;
g_config.tx_queue_len = 5;
g_config.rx_queue_len = 5;
g_config.alerts_enabled = TWAI_ALERT_NONE;
g_config.clkout_divider = 0;
g_config.intr_flags = ESP_INTR_FLAG_IRAM;
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_125KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// Install TWAI driver
ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(TAG, "Driver installed");
// SAFE ONLY IF YOU CONFIRMED ETS_TWAI_INTR_SOURCE EXISTS ON YOUR BUILD
ESP_ERROR_CHECK(esp_intr_alloc(ETS_TWAI_INTR_SOURCE, ESP_INTR_FLAG_IRAM, twai_isr_handler, NULL, &twai_intr_handle));
// Start TWAI driver
esp_err_t ret = twai_start();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start driver: %s", esp_err_to_name(ret));
twai_driver_uninstall();
}
}
void app_main() {
twai_rx_queue = xQueueCreate(TWAI_RX_QUEUE_LENGTH, sizeof(twai_message_t));
if (!twai_rx_queue) {
ESP_LOGE(TAG, "Failed to create TWAI RX queue");
return;
}
twai_init();
xTaskCreatePinnedToCore(twai_receive_task, "twai_receive_task", 4096, NULL, 10, NULL, 1);
// Keep the task alive
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
- E (2531) intr_alloc: No free interrupt inputs for TWAI interrupt (flags 0x40E)
- CONFIG_TWAI_ISR_IN_IRAM=y is set in my sdkconfig
- The ISR handler is properly marked with IRAM_ATTR
- I'm using ESP-IDF v5.3.3