1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*******************************************************************************
8  * NOTICE
9  * The ll is not public api, don't use in application code.
10  * See readme.md in hal/include/hal/readme.md
11  ******************************************************************************/
12 
13 // The Lowlevel layer for TWAI
14 
15 #pragma once
16 
17 #include <stdint.h>
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include "esp_assert.h"
21 #include "hal/misc.h"
22 #include "hal/assert.h"
23 #include "hal/twai_types.h"
24 #include "soc/twai_periph.h"
25 #include "soc/twai_struct.h"
26 
27 #define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL)
28 
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 
33 /* ------------------------- Defines and Typedefs --------------------------- */
34 
35 #define TWAI_LL_STATUS_RBS      (0x1 << 0)      //Receive Buffer Status
36 #define TWAI_LL_STATUS_DOS      (0x1 << 1)      //Data Overrun Status
37 #define TWAI_LL_STATUS_TBS      (0x1 << 2)      //Transmit Buffer Status
38 #define TWAI_LL_STATUS_TCS      (0x1 << 3)      //Transmission Complete Status
39 #define TWAI_LL_STATUS_RS       (0x1 << 4)      //Receive Status
40 #define TWAI_LL_STATUS_TS       (0x1 << 5)      //Transmit Status
41 #define TWAI_LL_STATUS_ES       (0x1 << 6)      //Error Status
42 #define TWAI_LL_STATUS_BS       (0x1 << 7)      //Bus Status
43 #define TWAI_LL_STATUS_MS       (0x1 << 8)      //Miss Status
44 
45 #define TWAI_LL_INTR_RI         (0x1 << 0)      //Receive Interrupt
46 #define TWAI_LL_INTR_TI         (0x1 << 1)      //Transmit Interrupt
47 #define TWAI_LL_INTR_EI         (0x1 << 2)      //Error Interrupt
48 //Data overrun interrupt not supported in SW due to HW peculiarities
49 #define TWAI_LL_INTR_EPI        (0x1 << 5)      //Error Passive Interrupt
50 #define TWAI_LL_INTR_ALI        (0x1 << 6)      //Arbitration Lost Interrupt
51 #define TWAI_LL_INTR_BEI        (0x1 << 7)      //Bus Error Interrupt
52 
53 /*
54  * The following frame structure has an NEARLY identical bit field layout to
55  * each byte of the TX buffer. This allows for formatting and parsing frames to
56  * be done outside of time critical regions (i.e., ISRs). All the ISR needs to
57  * do is to copy byte by byte to/from the TX/RX buffer. The two reserved bits in
58  * TX buffer are used in the frame structure to store the self_reception and
59  * single_shot flags which in turn indicate the type of transmission to execute.
60  */
61 typedef union {
62     struct {
63         struct {
64             uint8_t dlc: 4;             //Data length code (0 to 8) of the frame
65             uint8_t self_reception: 1;  //This frame should be transmitted using self reception command
66             uint8_t single_shot: 1;     //This frame should be transmitted using single shot command
67             uint8_t rtr: 1;             //This frame is a remote transmission request
68             uint8_t frame_format: 1;    //Format of the frame (1 = extended, 0 = standard)
69         };
70         union {
71             struct {
72                 uint8_t id[2];          //11 bit standard frame identifier
73                 uint8_t data[8];        //Data bytes (0 to 8)
74                 uint8_t reserved8[2];
75             } standard;
76             struct {
77                 uint8_t id[4];          //29 bit extended frame identifier
78                 uint8_t data[8];        //Data bytes (0 to 8)
79             } extended;
80         };
81     };
82     uint8_t bytes[13];
83 } __attribute__((packed)) twai_ll_frame_buffer_t;
84 
85 ESP_STATIC_ASSERT(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes");
86 
87 /* ---------------------------- Peripheral Control Register ----------------- */
88 
89 /**
90  * @brief Enable TWAI module clock
91  *
92  * @param hw Start address of the TWAI registers
93  * @param en true to enable, false to disable
94  */
95 __attribute__((always_inline))
twai_ll_enable_clock(twai_dev_t * hw,bool en)96 static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en)
97 {
98     (void)hw;
99 }
100 
101 /**
102  * @brief Set clock source for TWAI module
103  *
104  * @param hw Start address of the TWAI registers
105  * @param clk_src Clock source
106  */
107 __attribute__((always_inline))
twai_ll_set_clock_source(twai_dev_t * hw,twai_clock_source_t clk_src)108 static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src)
109 {
110     (void)hw;
111     HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB);
112 }
113 
114 /* ---------------------------- Mode Register ------------------------------- */
115 
116 /**
117  * @brief   Enter reset mode
118  *
119  * When in reset mode, the TWAI controller is effectively disconnected from the
120  * TWAI bus and will not participate in any bus activates. Reset mode is required
121  * in order to write the majority of configuration registers.
122  *
123  * @param hw Start address of the TWAI registers
124  *
125  * @note Reset mode is automatically entered on BUS OFF condition
126  */
127 __attribute__((always_inline))
twai_ll_enter_reset_mode(twai_dev_t * hw)128 static inline void twai_ll_enter_reset_mode(twai_dev_t *hw)
129 {
130     hw->mode_reg.rm = 1;
131 }
132 
133 /**
134  * @brief   Exit reset mode
135  *
136  * When not in reset mode, the TWAI controller will take part in bus activities
137  * (e.g., send/receive/acknowledge messages and error frames) depending on the
138  * operating mode.
139  *
140  * @param hw Start address of the TWAI registers
141  *
142  * @note Reset mode must be exit to initiate BUS OFF recovery
143  */
144 __attribute__((always_inline))
twai_ll_exit_reset_mode(twai_dev_t * hw)145 static inline void twai_ll_exit_reset_mode(twai_dev_t *hw)
146 {
147     hw->mode_reg.rm = 0;
148 }
149 
150 /**
151  * @brief   Check if in reset mode
152  * @param hw Start address of the TWAI registers
153  * @return true if in reset mode
154  */
155 __attribute__((always_inline))
twai_ll_is_in_reset_mode(twai_dev_t * hw)156 static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
157 {
158     return hw->mode_reg.rm;
159 }
160 
161 /**
162  * @brief   Set operating mode of TWAI controller
163  *
164  * @param hw Start address of the TWAI registers
165  * @param mode Operating mode
166  *
167  * @note Must be called in reset mode
168  */
169 __attribute__((always_inline))
twai_ll_set_mode(twai_dev_t * hw,twai_mode_t mode)170 static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
171 {
172     if (mode == TWAI_MODE_NORMAL) {           //Normal Operating mode
173         hw->mode_reg.lom = 0;
174         hw->mode_reg.stm = 0;
175     } else if (mode == TWAI_MODE_NO_ACK) {    //Self Test Mode (No Ack)
176         hw->mode_reg.lom = 0;
177         hw->mode_reg.stm = 1;
178     } else if (mode == TWAI_MODE_LISTEN_ONLY) {       //Listen Only Mode
179         hw->mode_reg.lom = 1;
180         hw->mode_reg.stm = 0;
181     }
182 }
183 
184 /* --------------------------- Command Register ----------------------------- */
185 
186 /**
187  * @brief   Set TX command
188  *
189  * Setting the TX command will cause the TWAI controller to attempt to transmit
190  * the frame stored in the TX buffer. The TX buffer will be occupied (i.e.,
191  * locked) until TX completes.
192  *
193  * @param hw Start address of the TWAI registers
194  *
195  * @note Transmit commands should be called last (i.e., after handling buffer
196  *       release and clear data overrun) in order to prevent the other commands
197  *       overwriting this latched TX bit with 0.
198  */
199 __attribute__((always_inline))
twai_ll_set_cmd_tx(twai_dev_t * hw)200 static inline void twai_ll_set_cmd_tx(twai_dev_t *hw)
201 {
202     hw->command_reg.tr = 1;
203 }
204 
205 /**
206  * @brief   Set single shot TX command
207  *
208  * Similar to setting TX command, but the TWAI controller will not automatically
209  * retry transmission upon an error (e.g., due to an acknowledgement error).
210  *
211  * @param hw Start address of the TWAI registers
212  *
213  * @note Transmit commands should be called last (i.e., after handling buffer
214  *       release and clear data overrun) in order to prevent the other commands
215  *       overwriting this latched TX bit with 0.
216  */
217 __attribute__((always_inline))
twai_ll_set_cmd_tx_single_shot(twai_dev_t * hw)218 static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw)
219 {
220     hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request
221 }
222 
223 /**
224  * @brief   Aborts TX
225  *
226  * Frames awaiting TX will be aborted. Frames already being TX are not aborted.
227  * Transmission Complete Status bit is automatically set to 1.
228  * Similar to setting TX command, but the TWAI controller will not automatically
229  * retry transmission upon an error (e.g., due to acknowledge error).
230  *
231  * @param hw Start address of the TWAI registers
232  *
233  * @note Transmit commands should be called last (i.e., after handling buffer
234  *       release and clear data overrun) in order to prevent the other commands
235  *       overwriting this latched TX bit with 0.
236  */
237 __attribute__((always_inline))
twai_ll_set_cmd_abort_tx(twai_dev_t * hw)238 static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw)
239 {
240     hw->command_reg.at = 1;
241 }
242 
243 /**
244  * @brief   Release RX buffer
245  *
246  * Rotates RX buffer to the next frame in the RX FIFO.
247  *
248  * @param hw Start address of the TWAI registers
249  */
250 __attribute__((always_inline))
twai_ll_set_cmd_release_rx_buffer(twai_dev_t * hw)251 static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw)
252 {
253     hw->command_reg.rrb = 1;
254 }
255 
256 /**
257  * @brief   Clear data overrun
258  *
259  * Clears the data overrun status bit
260  *
261  * @param hw Start address of the TWAI registers
262  */
263 __attribute__((always_inline))
twai_ll_set_cmd_clear_data_overrun(twai_dev_t * hw)264 static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw)
265 {
266     hw->command_reg.cdo = 1;
267 }
268 
269 /**
270  * @brief   Set self reception single shot command
271  *
272  * Similar to setting TX command, but the TWAI controller also simultaneously
273  * receive the transmitted frame and is generally used for self testing
274  * purposes. The TWAI controller will not ACK the received message, so consider
275  * using the NO_ACK operating mode.
276  *
277  * @param hw Start address of the TWAI registers
278  *
279  * @note Transmit commands should be called last (i.e., after handling buffer
280  *       release and clear data overrun) in order to prevent the other commands
281  *       overwriting this latched TX bit with 0.
282  */
283 __attribute__((always_inline))
twai_ll_set_cmd_self_rx_request(twai_dev_t * hw)284 static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw)
285 {
286     hw->command_reg.srr = 1;
287 }
288 
289 /**
290  * @brief   Set self reception request command
291  *
292  * Similar to setting the self reception request, but the TWAI controller will
293  * not automatically retry transmission upon an error (e.g., due to and
294  * acknowledgement error).
295  *
296  * @param hw Start address of the TWAI registers
297  *
298  * @note Transmit commands should be called last (i.e., after handling buffer
299  *       release and clear data overrun) in order to prevent the other commands
300  *       overwriting this latched TX bit with 0.
301  */
302 __attribute__((always_inline))
twai_ll_set_cmd_self_rx_single_shot(twai_dev_t * hw)303 static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw)
304 {
305     hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request
306 }
307 
308 /* --------------------------- Status Register ------------------------------ */
309 
310 /**
311  * @brief   Get all status bits
312  *
313  * @param hw Start address of the TWAI registers
314  * @return Status bits
315  */
316 __attribute__((always_inline))
twai_ll_get_status(twai_dev_t * hw)317 static inline uint32_t twai_ll_get_status(twai_dev_t *hw)
318 {
319     return hw->status_reg.val;
320 }
321 
322 /**
323  * @brief   Check if RX FIFO overrun status bit is set
324  *
325  * @param hw Start address of the TWAI registers
326  * @return Overrun status bit
327  */
328 __attribute__((always_inline))
twai_ll_is_fifo_overrun(twai_dev_t * hw)329 static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw)
330 {
331     return hw->status_reg.dos;
332 }
333 
334 /**
335  * @brief   Check if previously TX was successful
336  *
337  * @param hw Start address of the TWAI registers
338  * @return Whether previous TX was successful
339  */
340 __attribute__((always_inline))
twai_ll_is_last_tx_successful(twai_dev_t * hw)341 static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
342 {
343     return hw->status_reg.tcs;
344 }
345 
346 /* -------------------------- Interrupt Register ---------------------------- */
347 
348 /**
349  * @brief   Get currently set interrupts
350  *
351  * Reading the interrupt registers will automatically clear all interrupts
352  * except for the Receive Interrupt.
353  *
354  * @param hw Start address of the TWAI registers
355  * @return Bit mask of set interrupts
356  */
357 __attribute__((always_inline))
twai_ll_get_and_clear_intrs(twai_dev_t * hw)358 static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw)
359 {
360     return hw->interrupt_reg.val;
361 }
362 
363 /* ----------------------- Interrupt Enable Register ------------------------ */
364 
365 /**
366  * @brief   Set which interrupts are enabled
367  *
368  * @param hw Start address of the TWAI registers
369  * @param Bit mask of interrupts to enable
370  *
371  * @note Must be called in reset mode
372  */
373 __attribute__((always_inline))
twai_ll_set_enabled_intrs(twai_dev_t * hw,uint32_t intr_mask)374 static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask)
375 {
376     hw->interrupt_enable_reg.val = intr_mask;
377 }
378 
379 /* ------------------------ Bus Timing Registers --------------------------- */
380 
381 /**
382  * @brief Check if the brp value valid
383  *
384  * @param brp Bit rate prescaler value
385  * @return true or False
386  */
387 __attribute__((always_inline))
twai_ll_check_brp_validation(uint32_t brp)388 static inline bool twai_ll_check_brp_validation(uint32_t brp)
389 {
390     bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX);
391     // should be an even number
392     valid = valid && !(brp & 0x01);
393     return valid;
394 }
395 
396 /**
397  * @brief   Set bus timing
398  *
399  * @param hw Start address of the TWAI registers
400  * @param brp Baud Rate Prescaler
401  * @param sjw Synchronization Jump Width
402  * @param tseg1 Timing Segment 1
403  * @param tseg2 Timing Segment 2
404  * @param triple_sampling Triple Sampling enable/disable
405  *
406  * @note Must be called in reset mode
407  * @note ESP32S3 brp can be any even number between 2 to 32768
408  */
409 __attribute__((always_inline))
twai_ll_set_bus_timing(twai_dev_t * hw,uint32_t brp,uint32_t sjw,uint32_t tseg1,uint32_t tseg2,bool triple_sampling)410 static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling)
411 {
412     hw->bus_timing_0_reg.brp = (brp / 2) - 1;
413     hw->bus_timing_0_reg.sjw = sjw - 1;
414     hw->bus_timing_1_reg.tseg1 = tseg1 - 1;
415     hw->bus_timing_1_reg.tseg2 = tseg2 - 1;
416     hw->bus_timing_1_reg.sam = triple_sampling;
417 }
418 
419 /* ----------------------------- ALC Register ------------------------------- */
420 
421 /**
422  * @brief   Clear Arbitration Lost Capture Register
423  *
424  * Reading the ALC register rearms the Arbitration Lost Interrupt
425  *
426  * @param hw Start address of the TWAI registers
427  */
428 __attribute__((always_inline))
twai_ll_clear_arb_lost_cap(twai_dev_t * hw)429 static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
430 {
431     (void)hw->arbitration_lost_captue_reg.val;
432 }
433 
434 /* ----------------------------- ECC Register ------------------------------- */
435 
436 /**
437  * @brief   Clear Error Code Capture register
438  *
439  * Reading the ECC register rearms the Bus Error Interrupt
440  *
441  * @param hw Start address of the TWAI registers
442  */
443 __attribute__((always_inline))
twai_ll_clear_err_code_cap(twai_dev_t * hw)444 static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
445 {
446     (void)hw->error_code_capture_reg.val;
447 }
448 
449 /* ----------------------------- EWL Register ------------------------------- */
450 
451 /**
452  * @brief   Set Error Warning Limit
453  *
454  * @param hw Start address of the TWAI registers
455  * @param ewl Error Warning Limit
456  *
457  * @note Must be called in reset mode
458  */
459 __attribute__((always_inline))
twai_ll_set_err_warn_lim(twai_dev_t * hw,uint32_t ewl)460 static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl)
461 {
462     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl);
463 }
464 
465 /**
466  * @brief   Get Error Warning Limit
467  *
468  * @param hw Start address of the TWAI registers
469  * @return Error Warning Limit
470  */
471 __attribute__((always_inline))
twai_ll_get_err_warn_lim(twai_dev_t * hw)472 static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw)
473 {
474     return hw->error_warning_limit_reg.val;
475 }
476 
477 /* ------------------------ RX Error Count Register ------------------------- */
478 
479 /**
480  * @brief   Get RX Error Counter
481  *
482  * @param hw Start address of the TWAI registers
483  * @return REC value
484  *
485  * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS
486  *       OFF condition automatically sets the REC to 0.
487  */
488 __attribute__((always_inline))
twai_ll_get_rec(twai_dev_t * hw)489 static inline uint32_t twai_ll_get_rec(twai_dev_t *hw)
490 {
491     return hw->rx_error_counter_reg.val;
492 }
493 
494 /**
495  * @brief   Set RX Error Counter
496  *
497  * @param hw Start address of the TWAI registers
498  * @param rec REC value
499  *
500  * @note Must be called in reset mode
501  */
502 __attribute__((always_inline))
twai_ll_set_rec(twai_dev_t * hw,uint32_t rec)503 static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec)
504 {
505     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec);
506 }
507 
508 /* ------------------------ TX Error Count Register ------------------------- */
509 
510 /**
511  * @brief   Get TX Error Counter
512  *
513  * @param hw Start address of the TWAI registers
514  * @return TEC value
515  *
516  * @note A BUS OFF condition will automatically set this to 128
517  */
518 __attribute__((always_inline))
twai_ll_get_tec(twai_dev_t * hw)519 static inline uint32_t twai_ll_get_tec(twai_dev_t *hw)
520 {
521     return hw->tx_error_counter_reg.val;
522 }
523 
524 /**
525  * @brief   Set TX Error Counter
526  *
527  * @param hw Start address of the TWAI registers
528  * @param tec TEC value
529  *
530  * @note Must be called in reset mode
531  */
532 __attribute__((always_inline))
twai_ll_set_tec(twai_dev_t * hw,uint32_t tec)533 static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec)
534 {
535     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec);
536 }
537 
538 /* ---------------------- Acceptance Filter Registers ----------------------- */
539 
540 /**
541  * @brief   Set Acceptance Filter
542  * @param hw Start address of the TWAI registers
543  * @param code Acceptance Code
544  * @param mask Acceptance Mask
545  * @param single_filter Whether to enable single filter mode
546  *
547  * @note Must be called in reset mode
548  */
549 __attribute__((always_inline))
twai_ll_set_acc_filter(twai_dev_t * hw,uint32_t code,uint32_t mask,bool single_filter)550 static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_t mask, bool single_filter)
551 {
552     uint32_t code_swapped = HAL_SWAP32(code);
553     uint32_t mask_swapped = HAL_SWAP32(mask);
554     for (int i = 0; i < 4; i++) {
555         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.acr[i], byte, ((code_swapped >> (i * 8)) & 0xFF));
556         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.amr[i], byte, ((mask_swapped >> (i * 8)) & 0xFF));
557     }
558     hw->mode_reg.afm = single_filter;
559 }
560 
561 /* ------------------------- TX/RX Buffer Registers ------------------------- */
562 
563 /**
564  * @brief   Copy a formatted TWAI frame into TX buffer for transmission
565  *
566  * @param hw Start address of the TWAI registers
567  * @param tx_frame Pointer to formatted frame
568  *
569  * @note Call twai_ll_format_frame_buffer() to format a frame
570  */
571 __attribute__((always_inline))
twai_ll_set_tx_buffer(twai_dev_t * hw,twai_ll_frame_buffer_t * tx_frame)572 static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame)
573 {
574     //Copy formatted frame into TX buffer
575     for (int i = 0; i < 13; i++) {
576         hw->tx_rx_buffer[i].val = tx_frame->bytes[i];
577     }
578 }
579 
580 /**
581  * @brief   Copy a received frame from the RX buffer for parsing
582  *
583  * @param hw Start address of the TWAI registers
584  * @param rx_frame Pointer to store formatted frame
585  *
586  * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame
587  */
588 __attribute__((always_inline))
twai_ll_get_rx_buffer(twai_dev_t * hw,twai_ll_frame_buffer_t * rx_frame)589 static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame)
590 {
591     //Copy RX buffer registers into frame
592     for (int i = 0; i < 13; i++) {
593         rx_frame->bytes[i] =  HAL_FORCE_READ_U32_REG_FIELD(hw->tx_rx_buffer[i], byte);
594     }
595 }
596 
597 /**
598  * @brief   Format contents of a TWAI frame into layout of TX Buffer
599  *
600  * This function encodes a message into a frame structure. The frame structure
601  * has an identical layout to the TX buffer, allowing the frame structure to be
602  * directly copied into TX buffer.
603  *
604  * @param[in] 11bit or 29bit ID
605  * @param[in] dlc Data length code
606  * @param[in] data Pointer to an 8 byte array containing data. NULL if no data
607  * @param[in] format Type of TWAI frame
608  * @param[in] single_shot Frame will not be retransmitted on failure
609  * @param[in] self_rx Frame will also be simultaneously received
610  * @param[out] tx_frame Pointer to store formatted frame
611  */
612 __attribute__((always_inline))
twai_ll_format_frame_buffer(uint32_t id,uint8_t dlc,const uint8_t * data,uint32_t flags,twai_ll_frame_buffer_t * tx_frame)613 static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data,
614                                               uint32_t flags, twai_ll_frame_buffer_t *tx_frame)
615 {
616     bool is_extd = flags & TWAI_MSG_FLAG_EXTD;
617     bool is_rtr = flags & TWAI_MSG_FLAG_RTR;
618 
619     //Set frame information
620     tx_frame->dlc = dlc;
621     tx_frame->frame_format = is_extd;
622     tx_frame->rtr = is_rtr;
623     tx_frame->self_reception = (flags & TWAI_MSG_FLAG_SELF) ? 1 : 0;
624     tx_frame->single_shot = (flags & TWAI_MSG_FLAG_SS) ? 1 : 0;
625 
626     //Set ID. The ID registers are big endian and left aligned, therefore a bswap will be required
627     if (is_extd) {
628         uint32_t id_temp = HAL_SWAP32((id & TWAI_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i))
629         for (int i = 0; i < 4; i++) {
630             tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF;
631         }
632     } else {
633         uint32_t id_temp =  HAL_SWAP16((id & TWAI_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
634         for (int i = 0; i < 2; i++) {
635             tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF;
636         }
637     }
638 
639     uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data;
640     if (!is_rtr) {  //Only copy data if the frame is a data frame (i.e not a remote frame)
641         for (int i = 0; (i < dlc) && (i < TWAI_FRAME_MAX_DLC); i++) {
642             data_buffer[i] = data[i];
643         }
644     }
645 }
646 
647 /**
648  * @brief   Parse formatted TWAI frame (RX Buffer Layout) into its constituent contents
649  *
650  * @param[in] rx_frame Pointer to formatted frame
651  * @param[out] id 11 or 29bit ID
652  * @param[out] dlc Data length code
653  * @param[out] data Data. Left over bytes set to 0.
654  * @param[out] format Type of TWAI frame
655  */
656 __attribute__((always_inline))
twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t * rx_frame,uint32_t * id,uint8_t * dlc,uint8_t * data,uint32_t * flags)657 static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc,
658                                              uint8_t *data, uint32_t *flags)
659 {
660     //Copy frame information
661     *dlc = rx_frame->dlc;
662     uint32_t flags_temp = 0;
663     flags_temp |= (rx_frame->frame_format) ? TWAI_MSG_FLAG_EXTD : 0;
664     flags_temp |= (rx_frame->rtr) ? TWAI_MSG_FLAG_RTR : 0;
665     flags_temp |= (rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_MSG_FLAG_DLC_NON_COMP : 0;
666     *flags = flags_temp;
667 
668     //Copy ID. The ID registers are big endian and left aligned, therefore a bswap will be required
669     if (rx_frame->frame_format) {
670         uint32_t id_temp = 0;
671         for (int i = 0; i < 4; i++) {
672             id_temp |= rx_frame->extended.id[i] << (8 * i);
673         }
674         id_temp = HAL_SWAP32(id_temp) >> 3;  //((byte[i] << 8*(3-i)) >> 3)
675         *id = id_temp & TWAI_EXTD_ID_MASK;
676     } else {
677         uint32_t id_temp = 0;
678         for (int i = 0; i < 2; i++) {
679             id_temp |= rx_frame->standard.id[i] << (8 * i);
680         }
681         id_temp = HAL_SWAP16(id_temp) >> 5;  //((byte[i] << 8*(1-i)) >> 5)
682         *id = id_temp & TWAI_STD_ID_MASK;
683     }
684 
685     uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
686     //Only copy data if the frame is a data frame (i.e. not a remote frame)
687     int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_FRAME_MAX_DLC : rx_frame->dlc);
688     for (int i = 0; i < data_length; i++) {
689         data[i] = data_buffer[i];
690     }
691     //Set remaining bytes of data to 0
692     for (int i = data_length; i < TWAI_FRAME_MAX_DLC; i++) {
693         data[i] = 0;
694     }
695 }
696 
697 /* ----------------------- RX Message Count Register ------------------------ */
698 
699 /**
700  * @brief   Get RX Message Counter
701  *
702  * @param hw Start address of the TWAI registers
703  * @return RX Message Counter
704  */
705 __attribute__((always_inline))
twai_ll_get_rx_msg_count(twai_dev_t * hw)706 static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
707 {
708     return hw->rx_message_counter_reg.val;
709 }
710 
711 /* ------------------------- Clock Divider Register ------------------------- */
712 
713 /**
714  * @brief   Set CLKOUT Divider and enable/disable
715  *
716  * Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
717  * 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT.
718  *
719  * @param hw Start address of the TWAI registers
720  * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT
721  */
722 __attribute__((always_inline))
twai_ll_set_clkout(twai_dev_t * hw,uint32_t divider)723 static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider)
724 {
725     if (divider >= 2 && divider <= 490) {
726         hw->clock_divider_reg.co = 0;
727         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider_reg, cd, (divider / 2) - 1);
728     } else if (divider == 1) {
729         //Setting the divider reg to max value (255) means a divider of 1
730         hw->clock_divider_reg.co = 0;
731         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider_reg, cd, 255);
732     } else {
733         hw->clock_divider_reg.co = 1;
734         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider_reg, cd, 0);
735     }
736 }
737 
738 #ifdef __cplusplus
739 }
740 #endif
741