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