1 /*
2 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8 #include "sdkconfig.h"
9 #include "freertos/FreeRTOS.h"
10 #include "freertos/task.h"
11 #include "freertos/queue.h"
12 #include "freertos/semphr.h"
13 #include "esp_types.h"
14 #include "esp_log.h"
15 #include "esp_intr_alloc.h"
16 #include "esp_pm.h"
17 #include "esp_attr.h"
18 #include "esp_heap_caps.h"
19 #include "driver/gpio.h"
20 #include "driver/periph_ctrl.h"
21 #include "driver/twai.h"
22 #include "soc/soc_caps.h"
23 #include "soc/twai_periph.h"
24 #include "hal/twai_hal.h"
25 #include "esp_rom_gpio.h"
26
27 /* ---------------------------- Definitions --------------------------------- */
28 //Internal Macros
29 #define TWAI_CHECK(cond, ret_val) ({ \
30 if (!(cond)) { \
31 return (ret_val); \
32 } \
33 })
34 #define TWAI_CHECK_FROM_CRIT(cond, ret_val) ({ \
35 if (!(cond)) { \
36 TWAI_EXIT_CRITICAL(); \
37 return ret_val; \
38 } \
39 })
40 #define TWAI_SET_FLAG(var, mask) ((var) |= (mask))
41 #define TWAI_RESET_FLAG(var, mask) ((var) &= ~(mask))
42 #ifdef CONFIG_TWAI_ISR_IN_IRAM
43 #define TWAI_ISR_ATTR IRAM_ATTR
44 #define TWAI_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
45 #else
46 #define TWAI_TAG "TWAI"
47 #define TWAI_ISR_ATTR
48 #define TWAI_MALLOC_CAPS MALLOC_CAP_DEFAULT
49 #endif //CONFIG_TWAI_ISR_IN_IRAM
50
51 #define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
52
53 #define ALERT_LOG_LEVEL_WARNING TWAI_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW
54 #define ALERT_LOG_LEVEL_ERROR TWAI_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE
55
56 /* ------------------ Typedefs, structures, and variables ------------------- */
57
58 //Control structure for TWAI driver
59 typedef struct {
60 //Control and status members
61 twai_state_t state;
62 twai_mode_t mode;
63 uint32_t rx_missed_count;
64 uint32_t rx_overrun_count;
65 uint32_t tx_failed_count;
66 uint32_t arb_lost_count;
67 uint32_t bus_error_count;
68 intr_handle_t isr_handle;
69 //TX and RX
70 #ifdef CONFIG_TWAI_ISR_IN_IRAM
71 void *tx_queue_buff;
72 void *tx_queue_struct;
73 void *rx_queue_buff;
74 void *rx_queue_struct;
75 void *semphr_struct;
76 #endif
77 QueueHandle_t tx_queue;
78 QueueHandle_t rx_queue;
79 int tx_msg_count;
80 int rx_msg_count;
81 //Alerts
82 SemaphoreHandle_t alert_semphr;
83 uint32_t alerts_enabled;
84 uint32_t alerts_triggered;
85 #ifdef CONFIG_PM_ENABLE
86 //Power Management
87 esp_pm_lock_handle_t pm_lock;
88 #endif
89 } twai_obj_t;
90
91 static twai_obj_t *p_twai_obj = NULL;
92 static portMUX_TYPE twai_spinlock = portMUX_INITIALIZER_UNLOCKED;
93 #define TWAI_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&twai_spinlock)
94 #define TWAI_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&twai_spinlock)
95 #define TWAI_ENTER_CRITICAL() portENTER_CRITICAL(&twai_spinlock)
96 #define TWAI_EXIT_CRITICAL() portEXIT_CRITICAL(&twai_spinlock)
97
98 static twai_hal_context_t twai_context;
99
100 /* -------------------- Interrupt and Alert Handlers ------------------------ */
101
twai_alert_handler(uint32_t alert_code,int * alert_req)102 TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req)
103 {
104 if (p_twai_obj->alerts_enabled & alert_code) {
105 //Signify alert has occurred
106 TWAI_SET_FLAG(p_twai_obj->alerts_triggered, alert_code);
107 *alert_req = 1;
108 #ifndef CONFIG_TWAI_ISR_IN_IRAM //Only log if ISR is not in IRAM
109 if (p_twai_obj->alerts_enabled & TWAI_ALERT_AND_LOG) {
110 if (alert_code >= ALERT_LOG_LEVEL_ERROR) {
111 ESP_EARLY_LOGE(TWAI_TAG, "Alert %d", alert_code);
112 } else if (alert_code >= ALERT_LOG_LEVEL_WARNING) {
113 ESP_EARLY_LOGW(TWAI_TAG, "Alert %d", alert_code);
114 } else {
115 ESP_EARLY_LOGI(TWAI_TAG, "Alert %d", alert_code);
116 }
117 }
118 #endif //CONFIG_TWAI_ISR_IN_IRAM
119 }
120 }
121
twai_handle_rx_buffer_frames(BaseType_t * task_woken,int * alert_req)122 static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
123 {
124 #ifdef SOC_TWAI_SUPPORTS_RX_STATUS
125 uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
126
127 for (uint32_t i = 0; i < msg_count; i++) {
128 twai_hal_frame_t frame;
129 if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
130 //Valid frame copied from RX buffer
131 if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
132 p_twai_obj->rx_msg_count++;
133 twai_alert_handler(TWAI_ALERT_RX_DATA, alert_req);
134 } else { //Failed to send to queue
135 p_twai_obj->rx_missed_count++;
136 twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
137 }
138 } else { //Failed to read from RX buffer because message is overrun
139 p_twai_obj->rx_overrun_count++;
140 twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
141 }
142 }
143 #else //SOC_TWAI_SUPPORTS_RX_STATUS
144 uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
145 bool overrun = false;
146 //Clear all valid RX frames
147 for (int i = 0; i < msg_count; i++) {
148 twai_hal_frame_t frame;
149 if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
150 //Valid frame copied from RX buffer
151 if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
152 p_twai_obj->rx_msg_count++;
153 twai_alert_handler(TWAI_ALERT_RX_DATA, alert_req);
154 } else {
155 p_twai_obj->rx_missed_count++;
156 twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
157 }
158 } else {
159 overrun = true;
160 break;
161 }
162 }
163 //All remaining frames are treated as overrun. Clear them all
164 if (overrun) {
165 p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&twai_context);
166 twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
167 }
168 #endif //SOC_TWAI_SUPPORTS_RX_STATUS
169 }
170
twai_handle_tx_buffer_frame(BaseType_t * task_woken,int * alert_req)171 static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
172 {
173 //Handle previously transmitted frame
174 if (twai_hal_check_last_tx_successful(&twai_context)) {
175 twai_alert_handler(TWAI_ALERT_TX_SUCCESS, alert_req);
176 } else {
177 p_twai_obj->tx_failed_count++;
178 twai_alert_handler(TWAI_ALERT_TX_FAILED, alert_req);
179 }
180
181 //Update TX message count
182 p_twai_obj->tx_msg_count--;
183 assert(p_twai_obj->tx_msg_count >= 0); //Sanity check
184
185 //Check if there are more frames to transmit
186 if (p_twai_obj->tx_msg_count > 0 && p_twai_obj->tx_queue != NULL) {
187 twai_hal_frame_t frame;
188 int res = xQueueReceiveFromISR(p_twai_obj->tx_queue, &frame, task_woken);
189 if (res == pdTRUE) {
190 twai_hal_set_tx_buffer_and_transmit(&twai_context, &frame);
191 } else {
192 assert(false && "failed to get a frame from TX queue");
193 }
194 } else {
195 //No more frames to transmit
196 twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req);
197 }
198 }
199
twai_intr_handler_main(void * arg)200 TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg)
201 {
202 BaseType_t task_woken = pdFALSE;
203 int alert_req = 0;
204 uint32_t events;
205 TWAI_ENTER_CRITICAL_ISR();
206 if (p_twai_obj == NULL) { //In case intr occurs whilst driver is being uninstalled
207 TWAI_EXIT_CRITICAL_ISR();
208 return;
209 }
210 events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt
211
212 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
213 if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
214 twai_hal_prepare_for_reset(&twai_context);
215 periph_module_reset(PERIPH_TWAI_MODULE);
216 twai_hal_recover_from_reset(&twai_context);
217 p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context);
218 twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req);
219 }
220 #endif
221 if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
222 //Note: This event will never occur if there is a periph reset event
223 twai_handle_rx_buffer_frames(&task_woken, &alert_req);
224 }
225 if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
226 twai_handle_tx_buffer_frame(&task_woken, &alert_req);
227 }
228
229 //Handle events that only require alerting (i.e. no handler)
230 if (events & TWAI_HAL_EVENT_BUS_OFF) {
231 p_twai_obj->state = TWAI_STATE_BUS_OFF;
232 twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req);
233 }
234 if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
235 p_twai_obj->state = TWAI_STATE_STOPPED;
236 twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, &alert_req);
237 }
238 if (events & TWAI_HAL_EVENT_BUS_ERR) {
239 p_twai_obj->bus_error_count++;
240 twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req);
241 }
242 if (events & TWAI_HAL_EVENT_ARB_LOST) {
243 p_twai_obj->arb_lost_count++;
244 twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req);
245 }
246 if (events & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
247 //Bus-recovery in progress. TEC has dropped below error warning limit
248 twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req);
249 }
250 if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
251 //Entered error passive
252 twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req);
253 }
254 if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) {
255 //Returned to error active
256 twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req);
257 }
258 if (events & TWAI_HAL_EVENT_ABOVE_EWL) {
259 //TEC or REC surpassed error warning limit
260 twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, &alert_req);
261 }
262 if (events & TWAI_HAL_EVENT_BELOW_EWL) {
263 //TEC and REC are both below error warning
264 twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
265 }
266
267 TWAI_EXIT_CRITICAL_ISR();
268
269 if (p_twai_obj->alert_semphr != NULL && alert_req) {
270 //Give semaphore if alerts were triggered
271 xSemaphoreGiveFromISR(p_twai_obj->alert_semphr, &task_woken);
272 }
273 if (task_woken == pdTRUE) {
274 portYIELD_FROM_ISR();
275 }
276 }
277
278 /* -------------------------- Helper functions ----------------------------- */
279
twai_configure_gpio(gpio_num_t tx,gpio_num_t rx,gpio_num_t clkout,gpio_num_t bus_status)280 static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
281 {
282 //Set TX pin
283 gpio_set_pull_mode(tx, GPIO_FLOATING);
284 esp_rom_gpio_connect_out_signal(tx, TWAI_TX_IDX, false, false);
285 esp_rom_gpio_pad_select_gpio(tx);
286
287 //Set RX pin
288 gpio_set_pull_mode(rx, GPIO_FLOATING);
289 esp_rom_gpio_connect_in_signal(rx, TWAI_RX_IDX, false);
290 esp_rom_gpio_pad_select_gpio(rx);
291 gpio_set_direction(rx, GPIO_MODE_INPUT);
292
293 //Configure output clock pin (Optional)
294 if (clkout >= 0 && clkout < GPIO_NUM_MAX) {
295 gpio_set_pull_mode(clkout, GPIO_FLOATING);
296 esp_rom_gpio_connect_out_signal(clkout, TWAI_CLKOUT_IDX, false, false);
297 esp_rom_gpio_pad_select_gpio(clkout);
298 }
299
300 //Configure bus status pin (Optional)
301 if (bus_status >= 0 && bus_status < GPIO_NUM_MAX) {
302 gpio_set_pull_mode(bus_status, GPIO_FLOATING);
303 esp_rom_gpio_connect_out_signal(bus_status, TWAI_BUS_OFF_ON_IDX, false, false);
304 esp_rom_gpio_pad_select_gpio(bus_status);
305 }
306 }
307
twai_free_driver_obj(twai_obj_t * p_obj)308 static void twai_free_driver_obj(twai_obj_t *p_obj)
309 {
310 //Free driver object and any dependent SW resources it uses (queues, semaphores etc)
311 #ifdef CONFIG_PM_ENABLE
312 if (p_obj->pm_lock != NULL) {
313 ESP_ERROR_CHECK(esp_pm_lock_delete(p_obj->pm_lock));
314 }
315 #endif
316 //Delete queues and semaphores
317 if (p_obj->tx_queue != NULL) {
318 vQueueDelete(p_obj->tx_queue);
319 }
320 if (p_obj->rx_queue != NULL) {
321 vQueueDelete(p_obj->rx_queue);
322 }
323 if (p_obj->alert_semphr != NULL) {
324 vSemaphoreDelete(p_obj->alert_semphr);
325 }
326 #ifdef CONFIG_TWAI_ISR_IN_IRAM
327 //Free memory used by static queues and semaphores. free() allows freeing NULL pointers
328 free(p_obj->tx_queue_buff);
329 free(p_obj->tx_queue_struct);
330 free(p_obj->rx_queue_buff);
331 free(p_obj->rx_queue_struct);
332 free(p_obj->semphr_struct);
333 #endif //CONFIG_TWAI_ISR_IN_IRAM
334 free(p_obj);
335 }
336
twai_alloc_driver_obj(uint32_t tx_queue_len,uint32_t rx_queue_len)337 static twai_obj_t *twai_alloc_driver_obj(uint32_t tx_queue_len, uint32_t rx_queue_len)
338 {
339 //Allocates driver object and any dependent SW resources it uses (queues, semaphores etc)
340 //Create a TWAI driver object
341 twai_obj_t *p_obj = heap_caps_calloc(1, sizeof(twai_obj_t), TWAI_MALLOC_CAPS);
342 if (p_obj == NULL) {
343 return NULL;
344 }
345 #ifdef CONFIG_TWAI_ISR_IN_IRAM
346 //Allocate memory for queues and semaphores in DRAM
347 if (tx_queue_len > 0) {
348 p_obj->tx_queue_buff = heap_caps_calloc(tx_queue_len, sizeof(twai_hal_frame_t), TWAI_MALLOC_CAPS);
349 p_obj->tx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), TWAI_MALLOC_CAPS);
350 if (p_obj->tx_queue_buff == NULL || p_obj->tx_queue_struct == NULL) {
351 goto cleanup;
352 }
353 }
354 p_obj->rx_queue_buff = heap_caps_calloc(rx_queue_len, sizeof(twai_hal_frame_t), TWAI_MALLOC_CAPS);
355 p_obj->rx_queue_struct = heap_caps_calloc(1, sizeof(StaticQueue_t), TWAI_MALLOC_CAPS);
356 p_obj->semphr_struct = heap_caps_calloc(1, sizeof(StaticSemaphore_t), TWAI_MALLOC_CAPS);
357 if (p_obj->rx_queue_buff == NULL || p_obj->rx_queue_struct == NULL || p_obj->semphr_struct == NULL) {
358 goto cleanup;
359 }
360 //Create static queues and semaphores
361 if (tx_queue_len > 0) {
362 p_obj->tx_queue = xQueueCreateStatic(tx_queue_len, sizeof(twai_hal_frame_t), p_obj->tx_queue_buff, p_obj->tx_queue_struct);
363 if (p_obj->tx_queue == NULL) {
364 goto cleanup;
365 }
366 }
367 p_obj->rx_queue = xQueueCreateStatic(rx_queue_len, sizeof(twai_hal_frame_t), p_obj->rx_queue_buff, p_obj->rx_queue_struct);
368 p_obj->alert_semphr = xSemaphoreCreateBinaryStatic(p_obj->semphr_struct);
369 if (p_obj->rx_queue == NULL || p_obj->alert_semphr == NULL) {
370 goto cleanup;
371 }
372 #else //CONFIG_TWAI_ISR_IN_IRAM
373 if (tx_queue_len > 0) {
374 p_obj->tx_queue = xQueueCreate(tx_queue_len, sizeof(twai_hal_frame_t));
375 }
376 p_obj->rx_queue = xQueueCreate(rx_queue_len, sizeof(twai_hal_frame_t));
377 p_obj->alert_semphr = xSemaphoreCreateBinary();
378 if ((tx_queue_len > 0 && p_obj->tx_queue == NULL) || p_obj->rx_queue == NULL || p_obj->alert_semphr == NULL) {
379 goto cleanup;
380 }
381 #endif //CONFIG_TWAI_ISR_IN_IRAM
382
383 #ifdef CONFIG_PM_ENABLE
384 esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "twai", &(p_obj->pm_lock));
385 if (pm_err != ESP_OK ) {
386 goto cleanup;
387 }
388 #endif
389 return p_obj;
390
391 cleanup:
392 twai_free_driver_obj(p_obj);
393 return NULL;
394 }
395
396 /* ---------------------------- Public Functions ---------------------------- */
397
twai_driver_install(const twai_general_config_t * g_config,const twai_timing_config_t * t_config,const twai_filter_config_t * f_config)398 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)
399 {
400 //Check arguments
401 TWAI_CHECK(g_config != NULL, ESP_ERR_INVALID_ARG);
402 TWAI_CHECK(t_config != NULL, ESP_ERR_INVALID_ARG);
403 TWAI_CHECK(f_config != NULL, ESP_ERR_INVALID_ARG);
404 TWAI_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
405 TWAI_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
406 TWAI_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
407 TWAI_CHECK(t_config->brp >= SOC_TWAI_BRP_MIN && t_config->brp <= SOC_TWAI_BRP_MAX, ESP_ERR_INVALID_ARG);
408 #ifndef CONFIG_TWAI_ISR_IN_IRAM
409 TWAI_CHECK(!(g_config->intr_flags & ESP_INTR_FLAG_IRAM), ESP_ERR_INVALID_ARG);
410 #endif
411 TWAI_ENTER_CRITICAL();
412 TWAI_CHECK_FROM_CRIT(p_twai_obj == NULL, ESP_ERR_INVALID_STATE);
413 TWAI_EXIT_CRITICAL();
414
415 esp_err_t ret;
416 twai_obj_t *p_twai_obj_dummy;
417
418 //Create a TWAI object (including queues and semaphores)
419 p_twai_obj_dummy = twai_alloc_driver_obj(g_config->tx_queue_len, g_config->rx_queue_len);
420 TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM);
421
422 //Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj()
423 p_twai_obj_dummy->state = TWAI_STATE_STOPPED;
424 p_twai_obj_dummy->mode = g_config->mode;
425 p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled;
426
427 //Initialize TWAI peripheral registers, and allocate interrupt
428 TWAI_ENTER_CRITICAL();
429 if (p_twai_obj == NULL) {
430 p_twai_obj = p_twai_obj_dummy;
431 } else {
432 //Check if driver is already installed
433 TWAI_EXIT_CRITICAL();
434 ret = ESP_ERR_INVALID_STATE;
435 goto err;
436 }
437 periph_module_reset(PERIPH_TWAI_MODULE);
438 periph_module_enable(PERIPH_TWAI_MODULE); //Enable APB CLK to TWAI peripheral
439 bool init = twai_hal_init(&twai_context);
440 assert(init);
441 (void)init;
442 twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
443 TWAI_EXIT_CRITICAL();
444
445 //Allocate GPIO and Interrupts
446 twai_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
447 ESP_ERROR_CHECK(esp_intr_alloc(ETS_TWAI_INTR_SOURCE, g_config->intr_flags, twai_intr_handler_main, NULL, &p_twai_obj->isr_handle));
448
449 #ifdef CONFIG_PM_ENABLE
450 ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz
451 #endif
452 return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards
453
454 err:
455 twai_free_driver_obj(p_twai_obj_dummy);
456 return ret;
457 }
458
twai_driver_uninstall(void)459 esp_err_t twai_driver_uninstall(void)
460 {
461 twai_obj_t *p_twai_obj_dummy;
462
463 TWAI_ENTER_CRITICAL();
464 //Check state
465 TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
466 TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
467 //Clear registers by reading
468 twai_hal_deinit(&twai_context);
469 periph_module_disable(PERIPH_TWAI_MODULE); //Disable TWAI peripheral
470 p_twai_obj_dummy = p_twai_obj; //Use dummy to shorten critical section
471 p_twai_obj = NULL;
472 TWAI_EXIT_CRITICAL();
473
474 ESP_ERROR_CHECK(esp_intr_free(p_twai_obj_dummy->isr_handle)); //Free interrupt
475
476 #ifdef CONFIG_PM_ENABLE
477 //Release and delete power management lock
478 ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock));
479 #endif
480 //Free can driver object
481 twai_free_driver_obj(p_twai_obj_dummy);
482 return ESP_OK;
483 }
484
twai_start(void)485 esp_err_t twai_start(void)
486 {
487 //Check state
488 TWAI_ENTER_CRITICAL();
489 TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
490 TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED, ESP_ERR_INVALID_STATE);
491
492 //Reset RX queue, RX message count, amd TX queue
493 xQueueReset(p_twai_obj->rx_queue);
494 if (p_twai_obj->tx_queue != NULL) {
495 xQueueReset(p_twai_obj->tx_queue);
496 }
497 p_twai_obj->rx_msg_count = 0;
498 p_twai_obj->tx_msg_count = 0;
499 twai_hal_start(&twai_context, p_twai_obj->mode);
500
501 p_twai_obj->state = TWAI_STATE_RUNNING;
502 TWAI_EXIT_CRITICAL();
503 return ESP_OK;
504 }
505
twai_stop(void)506 esp_err_t twai_stop(void)
507 {
508 //Check state
509 TWAI_ENTER_CRITICAL();
510 TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
511 TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
512
513 twai_hal_stop(&twai_context);
514
515 //Reset TX Queue and message count
516 if (p_twai_obj->tx_queue != NULL) {
517 xQueueReset(p_twai_obj->tx_queue);
518 }
519 p_twai_obj->tx_msg_count = 0;
520 p_twai_obj->state = TWAI_STATE_STOPPED;
521
522 TWAI_EXIT_CRITICAL();
523
524 return ESP_OK;
525 }
526
twai_transmit(const twai_message_t * message,TickType_t ticks_to_wait)527 esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
528 {
529 //Check arguments
530 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
531 TWAI_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
532 TWAI_CHECK((message->data_length_code <= TWAI_FRAME_MAX_DLC) || message->dlc_non_comp, ESP_ERR_INVALID_ARG);
533
534 TWAI_ENTER_CRITICAL();
535 //Check State
536 TWAI_CHECK_FROM_CRIT(!(p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED);
537 TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
538 //Format frame
539 esp_err_t ret = ESP_FAIL;
540 twai_hal_frame_t tx_frame;
541 twai_hal_format_frame(message, &tx_frame);
542
543 //Check if frame can be sent immediately
544 if (p_twai_obj->tx_msg_count == 0) {
545 //No other frames waiting to transmit. Bypass queue and transmit immediately
546 twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
547 p_twai_obj->tx_msg_count++;
548 ret = ESP_OK;
549 }
550 TWAI_EXIT_CRITICAL();
551
552 if (ret != ESP_OK) {
553 if (p_twai_obj->tx_queue == NULL) {
554 //TX Queue is disabled and TX buffer is occupied, message was not sent
555 ret = ESP_FAIL;
556 } else if (xQueueSend(p_twai_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
557 //Copied to TX Queue
558 TWAI_ENTER_CRITICAL();
559 if ((!twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED)) && uxQueueMessagesWaiting(p_twai_obj->tx_queue) > 0) {
560 //If the TX buffer is free but the TX queue is not empty. Check if we need to manually start a transmission
561 if (twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_BUS_OFF) || !twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_RUNNING)) {
562 //TX buffer became free due to bus-off or is no longer running. No need to start a transmission
563 ret = ESP_ERR_INVALID_STATE;
564 } else {
565 //Manually start a transmission
566 int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
567 assert(res == pdTRUE);
568 (void)res;
569 twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
570 p_twai_obj->tx_msg_count++;
571 ret = ESP_OK;
572 }
573 } else {
574 //Frame was copied to queue, waiting to be transmitted
575 p_twai_obj->tx_msg_count++;
576 ret = ESP_OK;
577 }
578 TWAI_EXIT_CRITICAL();
579 } else {
580 //Timed out waiting for free space on TX queue
581 ret = ESP_ERR_TIMEOUT;
582 }
583 }
584 return ret;
585 }
586
twai_receive(twai_message_t * message,TickType_t ticks_to_wait)587 esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait)
588 {
589 //Check arguments and state
590 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
591 TWAI_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
592
593 //Get frame from RX Queue or RX Buffer
594 twai_hal_frame_t rx_frame;
595 if (xQueueReceive(p_twai_obj->rx_queue, &rx_frame, ticks_to_wait) != pdTRUE) {
596 return ESP_ERR_TIMEOUT;
597 }
598
599 TWAI_ENTER_CRITICAL();
600 p_twai_obj->rx_msg_count--;
601 TWAI_EXIT_CRITICAL();
602
603 //Decode frame
604 twai_hal_parse_frame(&rx_frame, message);
605 return ESP_OK;
606 }
607
twai_read_alerts(uint32_t * alerts,TickType_t ticks_to_wait)608 esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait)
609 {
610 //Check arguments and state
611 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
612 TWAI_CHECK(alerts != NULL, ESP_ERR_INVALID_ARG);
613
614 //Wait for an alert to occur
615 if (xSemaphoreTake(p_twai_obj->alert_semphr, ticks_to_wait) == pdTRUE) {
616 TWAI_ENTER_CRITICAL();
617 *alerts = p_twai_obj->alerts_triggered;
618 p_twai_obj->alerts_triggered = 0; //Clear triggered alerts
619 TWAI_EXIT_CRITICAL();
620 return ESP_OK;
621 } else {
622 *alerts = 0;
623 return ESP_ERR_TIMEOUT;
624 }
625 }
626
twai_reconfigure_alerts(uint32_t alerts_enabled,uint32_t * current_alerts)627 esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts)
628 {
629 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
630
631 TWAI_ENTER_CRITICAL();
632 //Clear any unhandled alerts
633 if (current_alerts != NULL) {
634 *current_alerts = p_twai_obj->alerts_triggered;;
635 }
636 p_twai_obj->alerts_triggered = 0;
637 p_twai_obj->alerts_enabled = alerts_enabled; //Update enabled alerts
638 TWAI_EXIT_CRITICAL();
639
640 return ESP_OK;
641 }
642
twai_initiate_recovery(void)643 esp_err_t twai_initiate_recovery(void)
644 {
645 TWAI_ENTER_CRITICAL();
646 //Check state
647 TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
648 TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
649
650 //Reset TX Queue/Counters
651 if (p_twai_obj->tx_queue != NULL) {
652 xQueueReset(p_twai_obj->tx_queue);
653 }
654 p_twai_obj->tx_msg_count = 0;
655
656 //Trigger start of recovery process
657 twai_hal_start_bus_recovery(&twai_context);
658 p_twai_obj->state = TWAI_STATE_RECOVERING;
659 TWAI_EXIT_CRITICAL();
660
661 return ESP_OK;
662 }
663
twai_get_status_info(twai_status_info_t * status_info)664 esp_err_t twai_get_status_info(twai_status_info_t *status_info)
665 {
666 //Check parameters and state
667 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
668 TWAI_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG);
669
670 TWAI_ENTER_CRITICAL();
671 status_info->tx_error_counter = twai_hal_get_tec(&twai_context);
672 status_info->rx_error_counter = twai_hal_get_rec(&twai_context);
673 status_info->msgs_to_tx = p_twai_obj->tx_msg_count;
674 status_info->msgs_to_rx = p_twai_obj->rx_msg_count;
675 status_info->tx_failed_count = p_twai_obj->tx_failed_count;
676 status_info->rx_missed_count = p_twai_obj->rx_missed_count;
677 status_info->rx_overrun_count = p_twai_obj->rx_overrun_count;
678 status_info->arb_lost_count = p_twai_obj->arb_lost_count;
679 status_info->bus_error_count = p_twai_obj->bus_error_count;
680 status_info->state = p_twai_obj->state;
681 TWAI_EXIT_CRITICAL();
682
683 return ESP_OK;
684 }
685
twai_clear_transmit_queue(void)686 esp_err_t twai_clear_transmit_queue(void)
687 {
688 //Check State
689 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
690 TWAI_CHECK(p_twai_obj->tx_queue != NULL, ESP_ERR_NOT_SUPPORTED);
691
692 TWAI_ENTER_CRITICAL();
693 //If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count
694 p_twai_obj->tx_msg_count = twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
695 xQueueReset(p_twai_obj->tx_queue);
696 TWAI_EXIT_CRITICAL();
697
698 return ESP_OK;
699 }
700
twai_clear_receive_queue(void)701 esp_err_t twai_clear_receive_queue(void)
702 {
703 //Check State
704 TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
705
706 TWAI_ENTER_CRITICAL();
707 p_twai_obj->rx_msg_count = 0;
708 xQueueReset(p_twai_obj->rx_queue);
709 TWAI_EXIT_CRITICAL();
710
711 return ESP_OK;
712 }
713