|
|
@@ -23,6 +23,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/twai.h"
|
|
|
@@ -44,7 +46,14 @@
|
|
|
})
|
|
|
#define TWAI_SET_FLAG(var, mask) ((var) |= (mask))
|
|
|
#define TWAI_RESET_FLAG(var, mask) ((var) &= ~(mask))
|
|
|
+#ifdef CONFIG_TWAI_ISR_IN_IRAM
|
|
|
+#define TWAI_ISR_ATTR IRAM_ATTR
|
|
|
+#define TWAI_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
|
|
+#else
|
|
|
#define TWAI_TAG "TWAI"
|
|
|
+#define TWAI_ISR_ATTR
|
|
|
+#define TWAI_MALLOC_CAPS MALLOC_CAP_DEFAULT
|
|
|
+#endif //CONFIG_TWAI_ISR_IN_IRAM
|
|
|
|
|
|
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
|
|
|
|
|
|
@@ -72,6 +81,13 @@ typedef struct {
|
|
|
uint32_t bus_error_count;
|
|
|
intr_handle_t isr_handle;
|
|
|
//TX and RX
|
|
|
+#ifdef CONFIG_TWAI_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;
|
|
|
@@ -97,12 +113,13 @@ static twai_hal_context_t twai_context;
|
|
|
|
|
|
/* -------------------- Interrupt and Alert Handlers ------------------------ */
|
|
|
|
|
|
-static void twai_alert_handler(uint32_t alert_code, int *alert_req)
|
|
|
+TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req)
|
|
|
{
|
|
|
if (p_twai_obj->alerts_enabled & alert_code) {
|
|
|
//Signify alert has occurred
|
|
|
TWAI_SET_FLAG(p_twai_obj->alerts_triggered, alert_code);
|
|
|
*alert_req = 1;
|
|
|
+#ifndef CONFIG_TWAI_ISR_IN_IRAM //Only log if ISR is not in IRAM
|
|
|
if (p_twai_obj->alerts_enabled & TWAI_ALERT_AND_LOG) {
|
|
|
if (alert_code >= ALERT_LOG_LEVEL_ERROR) {
|
|
|
ESP_EARLY_LOGE(TWAI_TAG, "Alert %d", alert_code);
|
|
|
@@ -112,6 +129,7 @@ static void twai_alert_handler(uint32_t alert_code, int *alert_req)
|
|
|
ESP_EARLY_LOGI(TWAI_TAG, "Alert %d", alert_code);
|
|
|
}
|
|
|
}
|
|
|
+#endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -239,7 +257,7 @@ static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *aler
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void twai_intr_handler_main(void *arg)
|
|
|
+TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg)
|
|
|
{
|
|
|
BaseType_t task_woken = pdFALSE;
|
|
|
int alert_req = 0;
|
|
|
@@ -297,7 +315,7 @@ static void twai_intr_handler_main(void *arg)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* --------------------------- GPIO functions ------------------------------ */
|
|
|
+/* -------------------------- Helper functions ----------------------------- */
|
|
|
|
|
|
static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
|
|
|
{
|
|
|
@@ -327,6 +345,94 @@ static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void twai_free_driver_obj(twai_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_TWAI_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_TWAI_ISR_IN_IRAM
|
|
|
+ free(p_obj);
|
|
|
+}
|
|
|
+
|
|
|
+static twai_obj_t *twai_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 TWAI driver object
|
|
|
+ twai_obj_t *p_obj = heap_caps_calloc(1, sizeof(twai_obj_t), TWAI_MALLOC_CAPS);
|
|
|
+ if (p_obj == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+#ifdef CONFIG_TWAI_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(twai_hal_frame_t), TWAI_MALLOC_CAPS);
|
|
|
+ p_obj->tx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), TWAI_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(twai_hal_frame_t), TWAI_MALLOC_CAPS);
|
|
|
+ p_obj->rx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), TWAI_MALLOC_CAPS);
|
|
|
+ p_obj->semphr_struct = heap_caps_calloc(1, sizeof(StaticSemaphore_t), TWAI_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(twai_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(twai_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_TWAI_ISR_IN_IRAM
|
|
|
+ if (tx_queue_len > 0) {
|
|
|
+ p_obj->tx_queue = xQueueCreate(tx_queue_len, sizeof(twai_hal_frame_t));
|
|
|
+ }
|
|
|
+ p_obj->rx_queue = xQueueCreate(rx_queue_len, sizeof(twai_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_TWAI_ISR_IN_IRAM
|
|
|
+
|
|
|
+#ifdef CONFIG_PM_ENABLE
|
|
|
+ esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "twai", &(p_obj->pm_lock));
|
|
|
+ if (pm_err != ESP_OK ) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ return p_obj;
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ twai_free_driver_obj(p_obj);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
/* ---------------------------- Public Functions ---------------------------- */
|
|
|
|
|
|
esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config)
|
|
|
@@ -343,33 +449,21 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
|
|
|
#else
|
|
|
TWAI_CHECK(t_config->brp >= TWAI_BRP_MIN && t_config->brp <= TWAI_BRP_MAX, ESP_ERR_INVALID_ARG);
|
|
|
#endif
|
|
|
-
|
|
|
+#ifndef CONFIG_TWAI_ISR_IN_IRAM
|
|
|
+ TWAI_CHECK(!(g_config->intr_flags & ESP_INTR_FLAG_IRAM), ESP_ERR_INVALID_ARG);
|
|
|
+#endif
|
|
|
+ TWAI_ENTER_CRITICAL();
|
|
|
+ TWAI_CHECK_FROM_CRIT(p_twai_obj == NULL, ESP_ERR_INVALID_STATE);
|
|
|
+ TWAI_EXIT_CRITICAL();
|
|
|
|
|
|
esp_err_t ret;
|
|
|
twai_obj_t *p_twai_obj_dummy;
|
|
|
|
|
|
- //Create a TWAI object
|
|
|
- p_twai_obj_dummy = calloc(1, sizeof(twai_obj_t));
|
|
|
+ //Create a TWAI object (including queues and semaphores)
|
|
|
+ p_twai_obj_dummy = twai_alloc_driver_obj(g_config->tx_queue_len, g_config->rx_queue_len);
|
|
|
TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM);
|
|
|
|
|
|
- //Initialize queues, semaphores, and power management locks
|
|
|
- p_twai_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(twai_hal_frame_t)) : NULL;
|
|
|
- p_twai_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(twai_hal_frame_t));
|
|
|
- p_twai_obj_dummy->alert_semphr = xSemaphoreCreateBinary();
|
|
|
- if ((g_config->tx_queue_len > 0 && p_twai_obj_dummy->tx_queue == NULL) ||
|
|
|
- p_twai_obj_dummy->rx_queue == NULL || p_twai_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, "twai", &(p_twai_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 twai_alloc_driver_obj()
|
|
|
p_twai_obj_dummy->control_flags = CTRL_FLAG_STOPPED;
|
|
|
p_twai_obj_dummy->mode = g_config->mode;
|
|
|
p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled;
|
|
|
@@ -394,35 +488,16 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
|
|
|
|
|
|
//Allocate GPIO and Interrupts
|
|
|
twai_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_TWAI_INTR_SOURCE, 0, twai_intr_handler_main, NULL, &p_twai_obj->isr_handle));
|
|
|
+ ESP_ERROR_CHECK(esp_intr_alloc(ETS_TWAI_INTR_SOURCE, g_config->intr_flags, twai_intr_handler_main, NULL, &p_twai_obj->isr_handle));
|
|
|
|
|
|
#ifdef CONFIG_PM_ENABLE
|
|
|
ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz
|
|
|
#endif
|
|
|
return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards
|
|
|
|
|
|
- err:
|
|
|
- //Cleanup TWAI object and return error
|
|
|
- if (p_twai_obj_dummy != NULL) {
|
|
|
- if (p_twai_obj_dummy->tx_queue != NULL) {
|
|
|
- vQueueDelete(p_twai_obj_dummy->tx_queue);
|
|
|
- p_twai_obj_dummy->tx_queue = NULL;
|
|
|
- }
|
|
|
- if (p_twai_obj_dummy->rx_queue != NULL) {
|
|
|
- vQueueDelete(p_twai_obj_dummy->rx_queue);
|
|
|
- p_twai_obj_dummy->rx_queue = NULL;
|
|
|
- }
|
|
|
- if (p_twai_obj_dummy->alert_semphr != NULL) {
|
|
|
- vSemaphoreDelete(p_twai_obj_dummy->alert_semphr);
|
|
|
- p_twai_obj_dummy->alert_semphr = NULL;
|
|
|
- }
|
|
|
-#ifdef CONFIG_PM_ENABLE
|
|
|
- if (p_twai_obj_dummy->pm_lock != NULL) {
|
|
|
- ESP_ERROR_CHECK(esp_pm_lock_delete(p_twai_obj_dummy->pm_lock));
|
|
|
- }
|
|
|
-#endif
|
|
|
- free(p_twai_obj_dummy);
|
|
|
- }
|
|
|
+err:
|
|
|
+ //Free can driver object
|
|
|
+ twai_free_driver_obj(p_twai_obj_dummy);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -444,19 +519,12 @@ esp_err_t twai_driver_uninstall(void)
|
|
|
|
|
|
ESP_ERROR_CHECK(esp_intr_free(p_twai_obj_dummy->isr_handle)); //Free interrupt
|
|
|
|
|
|
- //Delete queues, semaphores, and power management locks
|
|
|
- if (p_twai_obj_dummy->tx_queue != NULL) {
|
|
|
- vQueueDelete(p_twai_obj_dummy->tx_queue);
|
|
|
- }
|
|
|
- vQueueDelete(p_twai_obj_dummy->rx_queue);
|
|
|
- vSemaphoreDelete(p_twai_obj_dummy->alert_semphr);
|
|
|
#ifdef CONFIG_PM_ENABLE
|
|
|
//Release and delete power management lock
|
|
|
ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock));
|
|
|
- ESP_ERROR_CHECK(esp_pm_lock_delete(p_twai_obj_dummy->pm_lock));
|
|
|
#endif
|
|
|
- free(p_twai_obj_dummy); //Free can driver object
|
|
|
-
|
|
|
+ //Free can driver object
|
|
|
+ twai_free_driver_obj(p_twai_obj_dummy);
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
|