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