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