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