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