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 "sdkconfig.h"
22 #include "hal/misc.h"
23 #include "hal/assert.h"
24 #include "hal/twai_types.h"
25 #include "soc/twai_periph.h"
26 #include "soc/twai_struct.h"
27 
28 #define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL)
29 
30 #define TWAI_LL_BRP_DIV_THRESH       128
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 /* ------------------------- Defines and Typedefs --------------------------- */
37 
38 #define TWAI_LL_STATUS_RBS      (0x1 << 0)      //Receive Buffer Status
39 #define TWAI_LL_STATUS_DOS      (0x1 << 1)      //Data Overrun Status
40 #define TWAI_LL_STATUS_TBS      (0x1 << 2)      //Transmit Buffer Status
41 #define TWAI_LL_STATUS_TCS      (0x1 << 3)      //Transmission Complete Status
42 #define TWAI_LL_STATUS_RS       (0x1 << 4)      //Receive Status
43 #define TWAI_LL_STATUS_TS       (0x1 << 5)      //Transmit Status
44 #define TWAI_LL_STATUS_ES       (0x1 << 6)      //Error Status
45 #define TWAI_LL_STATUS_BS       (0x1 << 7)      //Bus Status
46 
47 #define TWAI_LL_INTR_RI         (0x1 << 0)      //Receive Interrupt
48 #define TWAI_LL_INTR_TI         (0x1 << 1)      //Transmit Interrupt
49 #define TWAI_LL_INTR_EI         (0x1 << 2)      //Error Interrupt
50 //Data overrun interrupt not supported in SW due to HW peculiarities
51 #define TWAI_LL_INTR_EPI        (0x1 << 5)      //Error Passive Interrupt
52 #define TWAI_LL_INTR_ALI        (0x1 << 6)      //Arbitration Lost Interrupt
53 #define TWAI_LL_INTR_BEI        (0x1 << 7)      //Bus Error Interrupt
54 
55 /*
56  * The following frame structure has an NEARLY identical bit field layout to
57  * each byte of the TX buffer. This allows for formatting and parsing frames to
58  * be done outside of time critical regions (i.e., ISRs). All the ISR needs to
59  * do is to copy byte by byte to/from the TX/RX buffer. The two reserved bits in
60  * TX buffer are used in the frame structure to store the self_reception and
61  * single_shot flags which in turn indicate the type of transmission to execute.
62  */
63 typedef union {
64     struct {
65         struct {
66             uint8_t dlc: 4;             //Data length code (0 to 8) of the frame
67             uint8_t self_reception: 1;  //This frame should be transmitted using self reception command
68             uint8_t single_shot: 1;     //This frame should be transmitted using single shot command
69             uint8_t rtr: 1;             //This frame is a remote transmission request
70             uint8_t frame_format: 1;    //Format of the frame (1 = extended, 0 = standard)
71         };
72         union {
73             struct {
74                 uint8_t id[2];          //11 bit standard frame identifier
75                 uint8_t data[8];        //Data bytes (0 to 8)
76                 uint8_t reserved8[2];
77             } standard;
78             struct {
79                 uint8_t id[4];          //29 bit extended frame identifier
80                 uint8_t data[8];        //Data bytes (0 to 8)
81             } extended;
82         };
83     };
84     uint8_t bytes[13];
85 } __attribute__((packed)) twai_ll_frame_buffer_t;
86 
87 ESP_STATIC_ASSERT(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes");
88 
89 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
90 /**
91  * Some errata workarounds will require a hardware reset of the peripheral. Thus
92  * certain registers must be saved before the reset, and then restored after the
93  * reset. This structure is used to hold some of those registers.
94  */
95 typedef struct {
96     uint8_t mode_reg;
97     uint8_t interrupt_enable_reg;
98     uint8_t bus_timing_0_reg;
99     uint8_t bus_timing_1_reg;
100     uint8_t error_warning_limit_reg;
101     uint8_t acr_reg[4];
102     uint8_t amr_reg[4];
103     uint8_t rx_error_counter_reg;
104     uint8_t tx_error_counter_reg;
105     uint8_t clock_divider_reg;
106 } __attribute__((packed)) twai_ll_reg_save_t;
107 #endif  //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
108 
109 #ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
110 typedef enum {
111     TWAI_LL_ERR_BIT = 0,
112     TWAI_LL_ERR_FORM,
113     TWAI_LL_ERR_STUFF,
114     TWAI_LL_ERR_OTHER,
115     TWAI_LL_ERR_MAX,
116 } twai_ll_err_type_t;
117 
118 typedef enum {
119     TWAI_LL_ERR_DIR_TX = 0,
120     TWAI_LL_ERR_DIR_RX,
121     TWAI_LL_ERR_DIR_MAX,
122 } twai_ll_err_dir_t;
123 
124 typedef enum {
125     TWAI_LL_ERR_SEG_SOF = 0,
126     TWAI_LL_ERR_SEG_ID_28_21 = 2,
127     TWAI_LL_ERR_SEG_SRTR = 4,
128     TWAI_LL_ERR_SEG_IDE = 5,
129     TWAI_LL_ERR_SEG_ID_20_18 = 6,
130     TWAI_LL_ERR_SEG_ID_17_13 = 7,
131     TWAI_LL_ERR_SEG_CRC_SEQ = 8,
132     TWAI_LL_ERR_SEG_R0 = 9,
133     TWAI_LL_ERR_SEG_DATA = 10,
134     TWAI_LL_ERR_SEG_DLC = 11,
135     TWAI_LL_ERR_SEG_RTR = 12,
136     TWAI_LL_ERR_SEG_R1 = 13,
137     TWAI_LL_ERR_SEG_ID_4_0 = 14,
138     TWAI_LL_ERR_SEG_ID_12_5 = 15,
139     TWAI_LL_ERR_SEG_ACT_FLAG = 17,
140     TWAI_LL_ERR_SEG_INTER = 18,
141     TWAI_LL_ERR_SEG_SUPERPOS = 19,
142     TWAI_LL_ERR_SEG_PASS_FLAG = 22,
143     TWAI_LL_ERR_SEG_ERR_DELIM = 23,
144     TWAI_LL_ERR_SEG_CRC_DELIM = 24,
145     TWAI_LL_ERR_SEG_ACK_SLOT = 25,
146     TWAI_LL_ERR_SEG_EOF = 26,
147     TWAI_LL_ERR_SEG_ACK_DELIM = 27,
148     TWAI_LL_ERR_SEG_OVRLD_FLAG = 28,
149     TWAI_LL_ERR_SEG_MAX = 29,
150 } twai_ll_err_seg_t;
151 #endif
152 
153 /* ---------------------------- Peripheral Control Register ----------------- */
154 
155 /**
156  * @brief Enable TWAI module clock
157  *
158  * @param hw Start address of the TWAI registers
159  * @param en true to enable, false to disable
160  */
161 __attribute__((always_inline))
twai_ll_enable_clock(twai_dev_t * hw,bool en)162 static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en)
163 {
164     (void)hw;
165 }
166 
167 /**
168  * @brief Set clock source for TWAI module
169  *
170  * @param hw Start address of the TWAI registers
171  * @param clk_src Clock source
172  */
173 __attribute__((always_inline))
twai_ll_set_clock_source(twai_dev_t * hw,twai_clock_source_t clk_src)174 static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src)
175 {
176     (void)hw;
177     HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB);
178 }
179 
180 /* ---------------------------- Mode Register ------------------------------- */
181 
182 /**
183  * @brief   Enter reset mode
184  *
185  * When in reset mode, the TWAI controller is effectively disconnected from the
186  * TWAI bus and will not participate in any bus activates. Reset mode is required
187  * in order to write the majority of configuration registers.
188  *
189  * @param hw Start address of the TWAI registers
190  *
191  * @note Reset mode is automatically entered on BUS OFF condition
192  */
193 __attribute__((always_inline))
twai_ll_enter_reset_mode(twai_dev_t * hw)194 static inline void twai_ll_enter_reset_mode(twai_dev_t *hw)
195 {
196     hw->mode_reg.rm = 1;
197 }
198 
199 /**
200  * @brief   Exit reset mode
201  *
202  * When not in reset mode, the TWAI controller will take part in bus activities
203  * (e.g., send/receive/acknowledge messages and error frames) depending on the
204  * operating mode.
205  *
206  * @param hw Start address of the TWAI registers
207  *
208  * @note Reset mode must be exit to initiate BUS OFF recovery
209  */
210 __attribute__((always_inline))
twai_ll_exit_reset_mode(twai_dev_t * hw)211 static inline void twai_ll_exit_reset_mode(twai_dev_t *hw)
212 {
213     hw->mode_reg.rm = 0;
214 }
215 
216 /**
217  * @brief   Check if in reset mode
218  * @param hw Start address of the TWAI registers
219  * @return true if in reset mode
220  */
221 __attribute__((always_inline))
twai_ll_is_in_reset_mode(twai_dev_t * hw)222 static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
223 {
224     return hw->mode_reg.rm;
225 }
226 
227 /**
228  * @brief   Set operating mode of TWAI controller
229  *
230  * @param hw Start address of the TWAI registers
231  * @param mode Operating mode
232  *
233  * @note Must be called in reset mode
234  */
235 __attribute__((always_inline))
twai_ll_set_mode(twai_dev_t * hw,twai_mode_t mode)236 static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
237 {
238     if (mode == TWAI_MODE_NORMAL) {           //Normal Operating mode
239         hw->mode_reg.lom = 0;
240         hw->mode_reg.stm = 0;
241     } else if (mode == TWAI_MODE_NO_ACK) {    //Self Test Mode (No Ack)
242         hw->mode_reg.lom = 0;
243         hw->mode_reg.stm = 1;
244     } else if (mode == TWAI_MODE_LISTEN_ONLY) {       //Listen Only Mode
245         hw->mode_reg.lom = 1;
246         hw->mode_reg.stm = 0;
247     }
248 }
249 
250 /* --------------------------- Command Register ----------------------------- */
251 
252 /**
253  * @brief   Set TX command
254  *
255  * Setting the TX command will cause the TWAI controller to attempt to transmit
256  * the frame stored in the TX buffer. The TX buffer will be occupied (i.e.,
257  * locked) until TX completes.
258  *
259  * @param hw Start address of the TWAI registers
260  *
261  * @note Transmit commands should be called last (i.e., after handling buffer
262  *       release and clear data overrun) in order to prevent the other commands
263  *       overwriting this latched TX bit with 0.
264  */
265 __attribute__((always_inline))
twai_ll_set_cmd_tx(twai_dev_t * hw)266 static inline void twai_ll_set_cmd_tx(twai_dev_t *hw)
267 {
268     hw->command_reg.tr = 1;
269 }
270 
271 /**
272  * @brief   Set single shot TX command
273  *
274  * Similar to setting TX command, but the TWAI controller will not automatically
275  * retry transmission upon an error (e.g., due to an acknowledgement error).
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_tx_single_shot(twai_dev_t * hw)284 static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw)
285 {
286     hw->command_reg.val = 0x03;     //Writing to TR and AT simultaneously
287 }
288 
289 /**
290  * @brief   Aborts TX
291  *
292  * Frames awaiting TX will be aborted. Frames already being TX are not aborted.
293  * Transmission Complete Status bit is automatically set to 1.
294  * Similar to setting TX command, but the TWAI controller will not automatically
295  * retry transmission upon an error (e.g., due to acknowledge error).
296  *
297  * @param hw Start address of the TWAI registers
298  *
299  * @note Transmit commands should be called last (i.e., after handling buffer
300  *       release and clear data overrun) in order to prevent the other commands
301  *       overwriting this latched TX bit with 0.
302  */
303 __attribute__((always_inline))
twai_ll_set_cmd_abort_tx(twai_dev_t * hw)304 static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw)
305 {
306     hw->command_reg.at = 1;
307 }
308 
309 /**
310  * @brief   Release RX buffer
311  *
312  * Rotates RX buffer to the next frame in the RX FIFO.
313  *
314  * @param hw Start address of the TWAI registers
315  */
316 __attribute__((always_inline))
twai_ll_set_cmd_release_rx_buffer(twai_dev_t * hw)317 static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw)
318 {
319     hw->command_reg.rrb = 1;
320 }
321 
322 /**
323  * @brief   Clear data overrun
324  *
325  * Clears the data overrun status bit
326  *
327  * @param hw Start address of the TWAI registers
328  */
329 __attribute__((always_inline))
twai_ll_set_cmd_clear_data_overrun(twai_dev_t * hw)330 static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw)
331 {
332     hw->command_reg.cdo = 1;
333 }
334 
335 /**
336  * @brief   Set self reception single shot command
337  *
338  * Similar to setting TX command, but the TWAI controller also simultaneously
339  * receive the transmitted frame and is generally used for self testing
340  * purposes. The TWAI controller will not ACK the received message, so consider
341  * using the NO_ACK operating mode.
342  *
343  * @param hw Start address of the TWAI registers
344  *
345  * @note Transmit commands should be called last (i.e., after handling buffer
346  *       release and clear data overrun) in order to prevent the other commands
347  *       overwriting this latched TX bit with 0.
348  */
349 __attribute__((always_inline))
twai_ll_set_cmd_self_rx_request(twai_dev_t * hw)350 static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw)
351 {
352     hw->command_reg.srr = 1;
353 }
354 
355 /**
356  * @brief   Set self reception request command
357  *
358  * Similar to setting the self reception request, but the TWAI controller will
359  * not automatically retry transmission upon an error (e.g., due to and
360  * acknowledgement error).
361  *
362  * @param hw Start address of the TWAI registers
363  *
364  * @note Transmit commands should be called last (i.e., after handling buffer
365  *       release and clear data overrun) in order to prevent the other commands
366  *       overwriting this latched TX bit with 0.
367  */
368 __attribute__((always_inline))
twai_ll_set_cmd_self_rx_single_shot(twai_dev_t * hw)369 static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw)
370 {
371     hw->command_reg.val = 0x12;
372 }
373 
374 /* --------------------------- Status Register ------------------------------ */
375 
376 /**
377  * @brief   Get all status bits
378  *
379  * @param hw Start address of the TWAI registers
380  * @return Status bits
381  */
382 __attribute__((always_inline))
twai_ll_get_status(twai_dev_t * hw)383 static inline uint32_t twai_ll_get_status(twai_dev_t *hw)
384 {
385     return hw->status_reg.val;
386 }
387 
388 /**
389  * @brief   Check if RX FIFO overrun status bit is set
390  *
391  * @param hw Start address of the TWAI registers
392  * @return Overrun status bit
393  */
394 __attribute__((always_inline))
twai_ll_is_fifo_overrun(twai_dev_t * hw)395 static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw)
396 {
397     return hw->status_reg.dos;
398 }
399 
400 /**
401  * @brief   Check if previously TX was successful
402  *
403  * @param hw Start address of the TWAI registers
404  * @return Whether previous TX was successful
405  */
406 __attribute__((always_inline))
twai_ll_is_last_tx_successful(twai_dev_t * hw)407 static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
408 {
409     return hw->status_reg.tcs;
410 }
411 
412 /* -------------------------- Interrupt Register ---------------------------- */
413 
414 /**
415  * @brief   Get currently set interrupts
416  *
417  * Reading the interrupt registers will automatically clear all interrupts
418  * except for the Receive Interrupt.
419  *
420  * @param hw Start address of the TWAI registers
421  * @return Bit mask of set interrupts
422  */
423 __attribute__((always_inline))
twai_ll_get_and_clear_intrs(twai_dev_t * hw)424 static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw)
425 {
426     return hw->interrupt_reg.val;
427 }
428 
429 /* ----------------------- Interrupt Enable Register ------------------------ */
430 
431 /**
432  * @brief   Set which interrupts are enabled
433  *
434  * @param hw Start address of the TWAI registers
435  * @param Bit mask of interrupts to enable
436  *
437  * @note Must be called in reset mode
438  */
439 __attribute__((always_inline))
twai_ll_set_enabled_intrs(twai_dev_t * hw,uint32_t intr_mask)440 static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask)
441 {
442 #if SOC_TWAI_BRP_DIV_SUPPORTED
443     //ESP32 Rev 2 or later has brp div field. Need to mask it out
444     hw->interrupt_enable_reg.val = (hw->interrupt_enable_reg.val & 0x10) | intr_mask;
445 #else
446     hw->interrupt_enable_reg.val = intr_mask;
447 #endif
448 }
449 
450 /* ------------------------ Bus Timing Registers --------------------------- */
451 
452 /**
453  * @brief Check if the brp value valid
454  *
455  * @param brp Bit rate prescaler value
456  * @return true or False
457  */
458 __attribute__((always_inline))
twai_ll_check_brp_validation(uint32_t brp)459 static inline bool twai_ll_check_brp_validation(uint32_t brp)
460 {
461     bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX);
462     // should be an even number
463     valid = valid && !(brp & 0x01);
464     if (brp > TWAI_LL_BRP_DIV_THRESH) {
465         // should be multiple of 4
466         valid = valid && !(brp & 0x03);
467     }
468     return valid;
469 }
470 
471 /**
472  * @brief   Set bus timing
473  *
474  * @param hw Start address of the TWAI registers
475  * @param brp Baud Rate Prescaler
476  * @param sjw Synchronization Jump Width
477  * @param tseg1 Timing Segment 1
478  * @param tseg2 Timing Segment 2
479  * @param triple_sampling Triple Sampling enable/disable
480  *
481  * @note Must be called in reset mode
482  * @note ESP32 rev 2 or later can support a x2 brp by setting a brp_div bit,
483  *       allowing the brp to go from a maximum of 128 to 256.
484  */
485 __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)486 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)
487 {
488 #if SOC_TWAI_BRP_DIV_SUPPORTED
489     if (brp > TWAI_LL_BRP_DIV_THRESH) {
490         //Need to set brp_div bit
491         hw->interrupt_enable_reg.brp_div = 1;
492         brp /= 2;
493     } else {
494         hw->interrupt_enable_reg.brp_div = 0;
495     }
496 #endif
497     hw->bus_timing_0_reg.brp = (brp / 2) - 1;
498     hw->bus_timing_0_reg.sjw = sjw - 1;
499     hw->bus_timing_1_reg.tseg1 = tseg1 - 1;
500     hw->bus_timing_1_reg.tseg2 = tseg2 - 1;
501     hw->bus_timing_1_reg.sam = triple_sampling;
502 }
503 
504 /* ----------------------------- ALC Register ------------------------------- */
505 
506 /**
507  * @brief   Clear Arbitration Lost Capture Register
508  *
509  * Reading the ALC register rearms the Arbitration Lost Interrupt
510  *
511  * @param hw Start address of the TWAI registers
512  */
513 __attribute__((always_inline))
twai_ll_clear_arb_lost_cap(twai_dev_t * hw)514 static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
515 {
516     (void)hw->arbitration_lost_captue_reg.val;
517 }
518 
519 /* ----------------------------- ECC Register ------------------------------- */
520 
521 /**
522  * @brief   Clear Error Code Capture register
523  *
524  * Reading the ECC register rearms the Bus Error Interrupt
525  *
526  * @param hw Start address of the TWAI registers
527  */
528 __attribute__((always_inline))
twai_ll_clear_err_code_cap(twai_dev_t * hw)529 static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
530 {
531     (void)hw->error_code_capture_reg.val;
532 }
533 
534 #ifdef CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID
twai_ll_parse_err_code_cap(twai_dev_t * hw,twai_ll_err_type_t * type,twai_ll_err_dir_t * dir,twai_ll_err_seg_t * seg)535 static inline void twai_ll_parse_err_code_cap(twai_dev_t *hw,
536                                               twai_ll_err_type_t *type,
537                                               twai_ll_err_dir_t *dir,
538                                               twai_ll_err_seg_t *seg)
539 {
540     uint32_t ecc = hw->error_code_capture_reg.val;
541     *type = (twai_ll_err_type_t) ((ecc >> 6) & 0x3);
542     *dir = (twai_ll_err_dir_t) ((ecc >> 5) & 0x1);
543     *seg = (twai_ll_err_seg_t) (ecc & 0x1F);
544 }
545 #endif
546 
547 /* ----------------------------- EWL Register ------------------------------- */
548 
549 /**
550  * @brief   Set Error Warning Limit
551  *
552  * @param hw Start address of the TWAI registers
553  * @param ewl Error Warning Limit
554  *
555  * @note Must be called in reset mode
556  */
557 __attribute__((always_inline))
twai_ll_set_err_warn_lim(twai_dev_t * hw,uint32_t ewl)558 static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl)
559 {
560     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl);
561 }
562 
563 /**
564  * @brief   Get Error Warning Limit
565  *
566  * @param hw Start address of the TWAI registers
567  * @return Error Warning Limit
568  */
569 __attribute__((always_inline))
twai_ll_get_err_warn_lim(twai_dev_t * hw)570 static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw)
571 {
572     return hw->error_warning_limit_reg.val;
573 }
574 
575 /* ------------------------ RX Error Count Register ------------------------- */
576 
577 /**
578  * @brief   Get RX Error Counter
579  *
580  * @param hw Start address of the TWAI registers
581  * @return REC value
582  *
583  * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS
584  *       OFF condition automatically sets the REC to 0.
585  */
586 __attribute__((always_inline))
twai_ll_get_rec(twai_dev_t * hw)587 static inline uint32_t twai_ll_get_rec(twai_dev_t *hw)
588 {
589     return hw->rx_error_counter_reg.val;
590 }
591 
592 /**
593  * @brief   Set RX Error Counter
594  *
595  * @param hw Start address of the TWAI registers
596  * @param rec REC value
597  *
598  * @note Must be called in reset mode
599  */
600 __attribute__((always_inline))
twai_ll_set_rec(twai_dev_t * hw,uint32_t rec)601 static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec)
602 {
603     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec);
604 }
605 
606 /* ------------------------ TX Error Count Register ------------------------- */
607 
608 /**
609  * @brief   Get TX Error Counter
610  *
611  * @param hw Start address of the TWAI registers
612  * @return TEC value
613  *
614  * @note A BUS OFF condition will automatically set this to 128
615  */
616 __attribute__((always_inline))
twai_ll_get_tec(twai_dev_t * hw)617 static inline uint32_t twai_ll_get_tec(twai_dev_t *hw)
618 {
619     return hw->tx_error_counter_reg.val;
620 }
621 
622 /**
623  * @brief   Set TX Error Counter
624  *
625  * @param hw Start address of the TWAI registers
626  * @param tec TEC value
627  *
628  * @note Must be called in reset mode
629  */
630 __attribute__((always_inline))
twai_ll_set_tec(twai_dev_t * hw,uint32_t tec)631 static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec)
632 {
633     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec);
634 }
635 
636 /* ---------------------- Acceptance Filter Registers ----------------------- */
637 
638 /**
639  * @brief   Set Acceptance Filter
640  * @param hw Start address of the TWAI registers
641  * @param code Acceptance Code
642  * @param mask Acceptance Mask
643  * @param single_filter Whether to enable single filter mode
644  *
645  * @note Must be called in reset mode
646  */
647 __attribute__((always_inline))
twai_ll_set_acc_filter(twai_dev_t * hw,uint32_t code,uint32_t mask,bool single_filter)648 static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_t mask, bool single_filter)
649 {
650     uint32_t code_swapped = HAL_SWAP32(code);
651     uint32_t mask_swapped = HAL_SWAP32(mask);
652     for (int i = 0; i < 4; i++) {
653         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.acr[i], byte, ((code_swapped >> (i * 8)) & 0xFF));
654         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.amr[i], byte, ((mask_swapped >> (i * 8)) & 0xFF));
655     }
656     hw->mode_reg.afm = single_filter;
657 }
658 
659 /* ------------------------- TX/RX Buffer Registers ------------------------- */
660 
661 /**
662  * @brief   Copy a formatted TWAI frame into TX buffer for transmission
663  *
664  * @param hw Start address of the TWAI registers
665  * @param tx_frame Pointer to formatted frame
666  *
667  * @note Call twai_ll_format_frame_buffer() to format a frame
668  */
669 __attribute__((always_inline))
twai_ll_set_tx_buffer(twai_dev_t * hw,twai_ll_frame_buffer_t * tx_frame)670 static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame)
671 {
672     //Copy formatted frame into TX buffer
673     for (int i = 0; i < 13; i++) {
674         hw->tx_rx_buffer[i].val = tx_frame->bytes[i];
675     }
676 }
677 
678 /**
679  * @brief   Copy a received frame from the RX buffer for parsing
680  *
681  * @param hw Start address of the TWAI registers
682  * @param rx_frame Pointer to store formatted frame
683  *
684  * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame
685  */
686 __attribute__((always_inline))
twai_ll_get_rx_buffer(twai_dev_t * hw,twai_ll_frame_buffer_t * rx_frame)687 static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame)
688 {
689     //Copy RX buffer registers into frame
690     for (int i = 0; i < 13; i++) {
691         rx_frame->bytes[i] =  HAL_FORCE_READ_U32_REG_FIELD(hw->tx_rx_buffer[i], byte);
692     }
693 }
694 
695 /**
696  * @brief   Format contents of a TWAI frame into layout of TX Buffer
697  *
698  * This function encodes a message into a frame structure. The frame structure
699  * has an identical layout to the TX buffer, allowing the frame structure to be
700  * directly copied into TX buffer.
701  *
702  * @param[in] 11bit or 29bit ID
703  * @param[in] dlc Data length code
704  * @param[in] data Pointer to an 8 byte array containing data. NULL if no data
705  * @param[in] format Type of TWAI frame
706  * @param[in] single_shot Frame will not be retransmitted on failure
707  * @param[in] self_rx Frame will also be simultaneously received
708  * @param[out] tx_frame Pointer to store formatted frame
709  */
710 __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)711 static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data,
712                                               uint32_t flags, twai_ll_frame_buffer_t *tx_frame)
713 {
714     bool is_extd = flags & TWAI_MSG_FLAG_EXTD;
715     bool is_rtr = flags & TWAI_MSG_FLAG_RTR;
716 
717     //Set frame information
718     tx_frame->dlc = dlc;
719     tx_frame->frame_format = is_extd;
720     tx_frame->rtr = is_rtr;
721     tx_frame->self_reception = (flags & TWAI_MSG_FLAG_SELF) ? 1 : 0;
722     tx_frame->single_shot = (flags & TWAI_MSG_FLAG_SS) ? 1 : 0;
723 
724     //Set ID. The ID registers are big endian and left aligned, therefore a bswap will be required
725     if (is_extd) {
726         uint32_t id_temp = HAL_SWAP32((id & TWAI_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i))
727         for (int i = 0; i < 4; i++) {
728             tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF;
729         }
730     } else {
731         uint32_t id_temp =  HAL_SWAP16((id & TWAI_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
732         for (int i = 0; i < 2; i++) {
733             tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF;
734         }
735     }
736 
737     uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data;
738     if (!is_rtr) {  //Only copy data if the frame is a data frame (i.e not RTR)
739         for (int i = 0; (i < dlc) && (i < TWAI_FRAME_MAX_DLC); i++) {
740             data_buffer[i] = data[i];
741         }
742     }
743 }
744 
745 /**
746  * @brief   Parse formatted TWAI frame (RX Buffer Layout) into its constituent contents
747  *
748  * @param[in] rx_frame Pointer to formatted frame
749  * @param[out] id 11 or 29bit ID
750  * @param[out] dlc Data length code
751  * @param[out] data Data. Left over bytes set to 0.
752  * @param[out] format Type of TWAI frame
753  */
754 __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)755 static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc,
756                                              uint8_t *data, uint32_t *flags)
757 {
758     //Copy frame information
759     *dlc = rx_frame->dlc;
760     uint32_t flags_temp = 0;
761     flags_temp |= (rx_frame->frame_format) ? TWAI_MSG_FLAG_EXTD : 0;
762     flags_temp |= (rx_frame->rtr) ? TWAI_MSG_FLAG_RTR : 0;
763     flags_temp |= (rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_MSG_FLAG_DLC_NON_COMP : 0;
764     *flags = flags_temp;
765 
766     //Copy ID. The ID registers are big endian and left aligned, therefore a bswap will be required
767     if (rx_frame->frame_format) {
768         uint32_t id_temp = 0;
769         for (int i = 0; i < 4; i++) {
770             id_temp |= rx_frame->extended.id[i] << (8 * i);
771         }
772         id_temp = HAL_SWAP32(id_temp) >> 3;  //((byte[i] << 8*(3-i)) >> 3)
773         *id = id_temp & TWAI_EXTD_ID_MASK;
774     } else {
775         uint32_t id_temp = 0;
776         for (int i = 0; i < 2; i++) {
777             id_temp |= rx_frame->standard.id[i] << (8 * i);
778         }
779         id_temp = HAL_SWAP16(id_temp) >> 5;  //((byte[i] << 8*(1-i)) >> 5)
780         *id = id_temp & TWAI_STD_ID_MASK;
781     }
782 
783     uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
784     //Only copy data if the frame is a data frame (i.e. not a remote frame)
785     int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_FRAME_MAX_DLC : rx_frame->dlc);
786     for (int i = 0; i < data_length; i++) {
787         data[i] = data_buffer[i];
788     }
789     //Set remaining bytes of data to 0
790     for (int i = data_length; i < TWAI_FRAME_MAX_DLC; i++) {
791         data[i] = 0;
792     }
793 }
794 
795 /* ----------------------- RX Message Count Register ------------------------ */
796 
797 /**
798  * @brief   Get RX Message Counter
799  *
800  * @param hw Start address of the TWAI registers
801  * @return RX Message Counter
802  */
803 __attribute__((always_inline))
twai_ll_get_rx_msg_count(twai_dev_t * hw)804 static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
805 {
806     return hw->rx_message_counter_reg.val;
807 }
808 
809 /* ------------------------- Clock Divider Register ------------------------- */
810 
811 /**
812  * @brief   Set CLKOUT Divider and enable/disable
813  *
814  * Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
815  * 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT.
816  *
817  * @param hw Start address of the TWAI registers
818  * @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT
819  */
820 __attribute__((always_inline))
twai_ll_set_clkout(twai_dev_t * hw,uint32_t divider)821 static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider)
822 {
823     if (divider >= 2 && divider <= 14) {
824         hw->clock_divider_reg.co = 0;
825         hw->clock_divider_reg.cd = (divider / 2) - 1;
826     } else if (divider == 1) {
827         //Setting the divider reg to max value (7) means a divider of 1
828         hw->clock_divider_reg.co = 0;
829         hw->clock_divider_reg.cd = 7;
830     } else {
831         hw->clock_divider_reg.co = 1;
832         hw->clock_divider_reg.cd = 0;
833     }
834 }
835 
836 /**
837  * @brief   Set register address mapping to extended mode
838  *
839  * Extended mode register address mapping consists of more registers and extra
840  * features.
841  *
842  * @param hw Start address of the TWAI registers
843  *
844  * @note Must be called before setting any configuration
845  * @note Must be called in reset mode
846  */
847 __attribute__((always_inline))
twai_ll_enable_extended_reg_layout(twai_dev_t * hw)848 static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw)
849 {
850     hw->clock_divider_reg.cm = 1;
851 }
852 
853 /* ------------------------- Register Save/Restore -------------------------- */
854 
855 #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
856 /**
857  * @brief   Saves the current values of the TWAI controller's registers
858  *
859  * This function saves the current values of the some of the TWAI controller's
860  * registers in preparation for a hardware reset of the controller.
861  *
862  * @param hw Start address of the TWAI registers
863  * @param reg_save Pointer to structure to store register values
864  * @note Must be called in reset mode so that config registers become accessible.
865  * @note Some registers are cleared on entering reset mode so must be saved
866  *       separate from this function.
867  */
868 __attribute__((always_inline))
twai_ll_save_reg(twai_dev_t * hw,twai_ll_reg_save_t * reg_save)869 static inline void twai_ll_save_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save)
870 {
871     reg_save->mode_reg = (uint8_t) hw->mode_reg.val;
872     reg_save->interrupt_enable_reg = (uint8_t) hw->interrupt_enable_reg.val;
873     reg_save->bus_timing_0_reg = (uint8_t) hw->bus_timing_0_reg.val;
874     reg_save->bus_timing_1_reg = (uint8_t) hw->bus_timing_1_reg.val;
875     reg_save->error_warning_limit_reg = (uint8_t) hw->error_warning_limit_reg.val;
876     for (int i = 0; i < 4; i++) {
877         reg_save->acr_reg[i] = HAL_FORCE_READ_U32_REG_FIELD(hw->acceptance_filter.acr[i], byte);
878         reg_save->amr_reg[i] = HAL_FORCE_READ_U32_REG_FIELD(hw->acceptance_filter.amr[i], byte);
879     }
880     reg_save->rx_error_counter_reg = (uint8_t) hw->rx_error_counter_reg.val;
881     reg_save->tx_error_counter_reg = (uint8_t) hw->tx_error_counter_reg.val;
882     reg_save->clock_divider_reg = (uint8_t) hw->clock_divider_reg.val;
883 }
884 
885 /**
886  * @brief   Restores the previous values of the TWAI controller's registers
887  *
888  * This function restores the previous values of some of the TWAI controller's
889  * registers following a hardware reset of the controller.
890  *
891  * @param hw Start address of the TWAI registers
892  * @param reg_save Pointer to structure to storing register values to restore
893  * @note Must be called in reset mode so that config registers become accessible
894  * @note Some registers are read only thus cannot be restored
895  */
896 __attribute__((always_inline))
twai_ll_restore_reg(twai_dev_t * hw,twai_ll_reg_save_t * reg_save)897 static inline void twai_ll_restore_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save)
898 {
899     hw->mode_reg.val = reg_save->mode_reg;
900     hw->interrupt_enable_reg.val = reg_save->interrupt_enable_reg;
901     hw->bus_timing_0_reg.val = reg_save->bus_timing_0_reg;
902     hw->bus_timing_1_reg.val = reg_save->bus_timing_1_reg;
903     hw->error_warning_limit_reg.val = reg_save->error_warning_limit_reg;
904     for (int i = 0; i < 4; i++) {
905         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.acr[i], byte, reg_save->acr_reg[i]);
906         HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.amr[i], byte, reg_save->amr_reg[i]);
907     }
908     hw->rx_error_counter_reg.val = reg_save->rx_error_counter_reg;
909     hw->tx_error_counter_reg.val = reg_save->tx_error_counter_reg;
910     hw->clock_divider_reg.val = reg_save->clock_divider_reg;
911 }
912 #endif  //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
913 
914 #ifdef __cplusplus
915 }
916 #endif
917