diff --git a/include/libopencm3/stm32/fdcan.h b/include/libopencm3/stm32/fdcan.h
index 61ac87d0..f59e9d1b 100644
--- a/include/libopencm3/stm32/fdcan.h
+++ b/include/libopencm3/stm32/fdcan.h
@@ -1,17 +1,7 @@
-/** @defgroup fdcan_defines FDCAN Defines
-
-@ingroup STM32G_defines
-
-@brief libopencm3 Defined Constants and Types for STM32 FD-CAN
-
-@author @htmlonly © @endhtmlonly 2021 Eduard Drusa
-
-LGPL License Terms @ref lgpl_license
-*/
/*
* This file is part of the libopencm3 project.
*
- * Copyright (C) 2021 Eduard Drusa
+ * Copyright (C) 2021 Eduard Drusa
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -27,26 +17,20 @@ LGPL License Terms @ref lgpl_license
* along with this library. If not, see .
*/
-
-
-#ifndef LIBOPENCM3_FDCAN_H
-#define LIBOPENCM3_FDCAN_H
+#pragma once
#include
#include
-/** @{ */
+#if defined(STM32G4)
+# include
+#elif defined(STM32H7)
+# include
+#endif
-/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */
-
-/** @defgroup fdcan_block FDCAN block base addresses
+/** @addtogroup fdcan_defines
* @{
*/
-#define CAN1 FDCAN1_BASE
-#define CAN2 FDCAN2_BASE
-#define CAN3 FDCAN3_BASE
-/**@}*/
-
/** @defgroup fdcan_fifo Named constants for FIFOs
* @{
@@ -55,6 +39,7 @@ LGPL License Terms @ref lgpl_license
#define FDCAN_FIFO1 1
/**@}*/
+#define FDCAN_BLOCK_ID(can_base) (((can_base) - CAN1)/(CAN2 - CAN1))
/** @defgroup FDCAN registers file in each FDCAN block. */
@@ -76,38 +61,29 @@ LGPL License Terms @ref lgpl_license
#define FDCAN_IE(can_base) MMIO32(can_base + 0x0054)
#define FDCAN_ILS(can_base) MMIO32(can_base + 0x0058)
#define FDCAN_ILE(can_base) MMIO32(can_base + 0x005C)
-#define FDCAN_RXGFC(can_base) MMIO32(can_base + 0x0080)
-#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0084)
-#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0088)
+
/** Generic access to Rx FIFO status registers.
* @param can_base FDCAN block base address @ref fdcan_block
* @param fifo_id ID of FIFO, 0 or 1
*/
-#define FDCAN_RXFIS(can_base, fifo_id) MMIO32(can_base + 0x0090 + (8 * fifo_id))
+#define FDCAN_RXFIS(can_base, fifo_id) \
+ MMIO32(can_base + FDCAN_RXFIS_BASE + (FDCAN_RXFI_OFFSET * fifo_id))
+
#define FDCAN_RXF0S(can_base) FDCAN_RXFIS(can_base, 0)
#define FDCAN_RXF1S(can_base) FDCAN_RXFIS(can_base, 1)
+
/** Generic access to Rx FIFO acknowledge registers.
* @param can_base FDCAN block base address @ref fdcan_block
* @param fifo_id ID of FIFO, 0 or 1
*/
-#define FDCAN_RXFIA(can_base, fifo_id) MMIO32(can_base + 0x0094 + (8 * fifo_id))
+#define FDCAN_RXFIA(can_base, fifo_id) MMIO32(can_base + 0x0094 + (FDCAN_RXFI_OFFSET * fifo_id))
#define FDCAN_RXF0A(can_base) FDCAN_RXFIA(can_base, 0)
#define FDCAN_RXF1A(can_base) FDCAN_RXFIA(can_base, 1)
#define FDCAN_TXBC(can_base) MMIO32(can_base + 0x00C0)
#define FDCAN_TXFQS(can_base) MMIO32(can_base + 0x00C4)
-#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00C8)
-#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00CC)
-#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D0)
-#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D4)
-#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00D8)
-#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00DC)
-#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E0)
-#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00E4)
-#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00E8)
-#define FDCAN_CKDIV(can_base) MMIO32(can_base + 0x0100)
/* DAY[7:0]: FDCAN core revision date */
#define FDCAN_CREL_DAY_SHIFT 0
@@ -360,26 +336,6 @@ LGPL License Terms @ref lgpl_license
#define FDCAN_ILE_INT0 (1 << 0)
#define FDCAN_ILE_INT1 (1 << 1)
-#define FDCAN_RXGFC_RRFE (1 << 0)
-#define FDCAN_RXGFC_RRFS (1 << 1)
-/* ANFE[1:0]: Accept non-matching frames w/ extended ID */
-#define FDCAN_RXGFC_ANFE_SHIFT 2
-#define FDCAN_RXGFC_ANFE_MASK 0x3
-
-/* ANFS[1:0]: Accept non-matching frames w/ standard ID */
-#define FDCAN_RXGFC_ANFS_SHIFT 4
-#define FDCAN_RXGFC_ANFS_MASK 0x3
-
-#define FDCAN_RXGFC_F1OM (1 << 8)
-#define FDCAN_RXGFC_F0OM (1 << 9)
-/* LSS[4:0]: List size of standard ID filters */
-#define FDCAN_RXGFC_LSS_SHIFT 16
-#define FDCAN_RXGFC_LSS_MASK 0x1F
-
-/* LSE[3:0]: List size of extended ID filters */
-#define FDCAN_RXGFC_LSE_SHIFT 24
-#define FDCAN_RXGFC_LSE_MASK 0xF
-
/* EIDM[28:0]: Extended ID mask for filtering */
#define FDCAN_XIDAM_EIDM_SHIFT 0
@@ -402,15 +358,12 @@ LGPL License Terms @ref lgpl_license
/* Fill level of Rx FIFOs */
#define FDCAN_RXFIFO_FL_SHIFT 0
-#define FDCAN_RXFIFO_FL_MASK 0xF
/* Get index of Rx FIFOs */
#define FDCAN_RXFIFO_GI_SHIFT 8
-#define FDCAN_RXFIFO_GI_MASK 0x3
/* Put index of Rx FIFOs */
#define FDCAN_RXFIFO_PI_SHIFT 16
-#define FDCAN_RXFIFO_PI_MASK 0x3
#define FDCAN_RXFIFO_FF (1 << 24)
#define FDCAN_RXFIFO_RFL (1 << 25)
@@ -432,7 +385,6 @@ LGPL License Terms @ref lgpl_license
/* Rx FIFOs acknowledge index */
#define FDCAN_RXFIFO_AI_SHIFT 0
-#define FDCAN_RXFIFO_AI_MASK 0x7
/* R0AI[2:0]: Rx FIFO 0 acknowledge index */
#define FDCAN_RXF0A_R0AI_SHIFT FDCAN_RXFIFO_AI_SHIFT
@@ -461,17 +413,14 @@ LGPL License Terms @ref lgpl_license
/* TFFL[2:0]: Tx FIFO free level */
#define FDCAN_TXFQS_TFFL_SHIFT 0
-#define FDCAN_TXFQS_TFFL_MASK 0x7
/* TFGI[1:0]: Tx FIFO get index */
-#define FDCAN_TXFQS_TFGI_SHIFT 0
-#define FDCAN_TXFQS_TFGI_MASK 0x3
+#define FDCAN_TXFQS_TFGI_SHIFT 8
/* TFQPI[1:0]: Tx FIFO put index */
-#define FDCAN_TXFQS_TFQPI_SHIFT 0
-#define FDCAN_TXFQS_TFQPI_MASK 0x3
+#define FDCAN_TXFQS_TFQPI_SHIFT 16
-#define FDCAN_TXFQS_TFQF (1 << 0)
+#define FDCAN_TXFQS_TFQF (1 << 21)
/** @defgroup fdcan_txbrp FDCAN_TXBRP Transmit request pending bits
* @{
@@ -527,7 +476,7 @@ LGPL License Terms @ref lgpl_license
/** @defgroup fdcan_txbcie FDCAN_TXBCIE Transmit cancelled interrupt enable bits
*
* Each bit enables or disables transmit cancelled interrupt for transmit buffer
- * slot.
+ * slot.
* @{
*/
#define FDCAN_TXBCIE_CFIE0 (1 << 0)
@@ -537,15 +486,12 @@ LGPL License Terms @ref lgpl_license
/* EFFL[2:0]: Event FIFO fill level*/
#define FDCAN_TXEFS_EFFL_SHIFT 0
-#define FDCAN_TXEFS_EFFL_MASK 0x7
/* EFG[1:0]: Event FIFO get index */
#define FDCAN_TXEFS_EFGI_SHIFT 8
-#define FDCAN_TXEFS_EFGI_MASK 0x3
/* EFPI[1:0]: Event FIFO put index */
#define FDCAN_TXEFS_EFPI_SHIFT 16
-#define FDCAN_TXEFS_EFPI_MASK 0x3
#define FDCAN_TXEFS_EFF (1 << 24)
#define FDCAN_TXEFS_TEF (1 << 25)
@@ -555,10 +501,6 @@ LGPL License Terms @ref lgpl_license
#define FDCAN_TXEFA_EFAI_MASK 0x3
-/* PDIV[3:0]: Input clock divider */
-#define FDCAN_CKDIV_PDIV_SHIFT 0
-#define FDCAN_CKDIV_PDIV_MASK 0xF
-
/* --- FD-CAN memory block defines------------------------------------------ */
/** Structure describing standard ID filter.
@@ -626,12 +568,6 @@ struct fdcan_standard_filter {
#define FDCAN_SFEC_PRIO_FIFO1 0x6
/**@}*/
-/** Amount of standard filters allocated in Message RAM
- * This number may vary between devices. 28 is value valid
- * for STM32G4
- **/
-#define FDCAN_SFT_MAX_NR 28
-
/* SFEC = 0x7 is unused */
#define FDCAN_SFID1_SHIFT 16
@@ -706,7 +642,7 @@ struct fdcan_extended_filter {
* using id2. */
#define FDCAN_EFT_ID_MASK 0x2
-/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask
+/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask
* set using @ref FDCAN_XIDAM register.
*/
#define FDCAN_EFT_RANGE_NOXIDAM 0x3
@@ -715,12 +651,6 @@ struct fdcan_extended_filter {
#define FDCAN_EFID2_SHIFT 0
#define FDCAN_EFID2_MASK 0x1FFFFFFF
-/** Amount of extended filters allocated in Message RAM
- * This number may vary between devices. 8 is value valid
- * for STM32G4
- **/
-#define FDCAN_EFT_MAX_NR 8
-
/** Structure describing receive FIFO element.
* Receive FIFO element consists of 2 32bit values for header
* and 16 32bit values for message payload.
@@ -792,29 +722,6 @@ struct fdcan_tx_buffer_element {
#define FDCAN_FIFO_RXTS_SHIFT 0
#define FDCAN_FIFO_RXTS_MASK 0xFFFF
-/** Message RAM layout for one FDCAN block.
- * There are as many memory blocks as there are FDCAN blocks
- */
-struct fdcan_message_ram {
- /* List of standard ID filters */
- struct fdcan_standard_filter lfssa[FDCAN_SFT_MAX_NR];
-
- /* List of extended ID filters */
- struct fdcan_extended_filter lfesa[FDCAN_EFT_MAX_NR];
-
- /* Buffer area for two receive FIFOs each having space for three messages */
- struct fdcan_rx_fifo_element rx_fifo[2][3];
-
- /* Buffer area for transmit event buffers */
- struct fdcan_tx_event_element tx_event[3];
-
- /* Buffer area for transmitted messages. May act either as FIFO or as queue
- * depending on configuration
- */
- struct fdcan_tx_buffer_element tx_buffer[3];
-};
-
-
/* --- FD-CAN error returns ------------------------------------------------- */
/** FDCAN error return values
@@ -886,7 +793,18 @@ void fdcan_release_fifo(uint32_t canport, uint8_t fifo);
bool fdcan_available_tx(uint32_t canport);
bool fdcan_available_rx(uint32_t canport, uint8_t fifo);
+int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout);
+struct fdcan_standard_filter *fdcan_get_flssa_addr(uint32_t canport);
+struct fdcan_extended_filter *fdcan_get_flesa_addr(uint32_t canport);
+
+struct fdcan_rx_fifo_element *fdcan_get_rxfifo_addr(uint32_t canport,
+ unsigned fifo_id, unsigned element_id);
+
+struct fdcan_tx_event_element *fdcan_get_txevt_addr(uint32_t canport);
+struct fdcan_tx_buffer_element *fdcan_get_txbuf_addr(uint32_t canport, unsigned element_id);
+void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked);
+uint32_t fdcan_length_to_dlc(uint8_t length);
+uint8_t fdcan_dlc_to_length(uint32_t dlc);
+
END_DECLS
-
-#endif
diff --git a/include/libopencm3/stm32/g4/fdcan.h b/include/libopencm3/stm32/g4/fdcan.h
new file mode 100644
index 00000000..4b94625b
--- /dev/null
+++ b/include/libopencm3/stm32/g4/fdcan.h
@@ -0,0 +1,132 @@
+/** @defgroup fdcan_defines FDCAN Defines
+
+@ingroup STM32G4xx_defines
+
+@author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+
+LGPL License Terms @ref lgpl_license
+*/
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+#pragma once
+
+/** @{ */
+
+/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */
+
+/** @defgroup fdcan_block FDCAN block base addresses
+ * @{
+ */
+#define CAN1 FDCAN1_BASE
+#define CAN2 FDCAN2_BASE
+#define CAN3 FDCAN3_BASE
+/**@}*/
+
+#define CAN_MSG_BASE FDCAN1_RAM_BASE
+
+#define FDCAN_RXFIS_BASE 0x0090
+#define FDCAN_RXFIA_BASE 0x0094
+#define FDCAN_RXFI_OFFSET 0x0008
+
+#define FDCAN_RXGFC(can_base) MMIO32(can_base + 0x0080)
+#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0084)
+#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0088)
+
+#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00C8)
+#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00CC)
+#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D0)
+#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D4)
+#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00D8)
+#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00DC)
+#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E0)
+#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00E4)
+#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00E8)
+
+#define FDCAN_CKDIV(can_base) MMIO32(can_base + 0x0100)
+
+#define FDCAN_RXGFC_RRFE (1 << 0)
+#define FDCAN_RXGFC_RRFS (1 << 1)
+/* ANFE[1:0]: Accept non-matching frames w/ extended ID */
+#define FDCAN_RXGFC_ANFE_SHIFT 2
+#define FDCAN_RXGFC_ANFE_MASK 0x3
+
+/* ANFS[1:0]: Accept non-matching frames w/ standard ID */
+#define FDCAN_RXGFC_ANFS_SHIFT 4
+#define FDCAN_RXGFC_ANFS_MASK 0x3
+
+#define FDCAN_RXGFC_F1OM (1 << 8)
+#define FDCAN_RXGFC_F0OM (1 << 9)
+/* LSS[4:0]: List size of standard ID filters */
+#define FDCAN_RXGFC_LSS_SHIFT 16
+#define FDCAN_RXGFC_LSS_MASK 0x1F
+
+/* LSE[3:0]: List size of extended ID filters */
+#define FDCAN_RXGFC_LSE_SHIFT 24
+#define FDCAN_RXGFC_LSE_MASK 0xF
+
+#define FDCAN_RXFIFO_FL_MASK 0xF
+#define FDCAN_RXFIFO_GI_MASK 0x3
+#define FDCAN_RXFIFO_PI_MASK 0x3
+#define FDCAN_RXFIFO_AI_MASK 0x3
+
+#define FDCAN_TXFQS_TFFL_MASK 0x7
+#define FDCAN_TXFQS_TFGI_MASK 0x3
+#define FDCAN_TXFQS_TFQPI_MASK 0x3
+
+#define FDCAN_TXEFS_EFFL_MASK 0x7
+#define FDCAN_TXEFS_EFGI_MASK 0x3
+#define FDCAN_TXEFS_EFPI_MASK 0x3
+
+/* PDIV[3:0]: Input clock divider */
+#define FDCAN_CKDIV_PDIV_SHIFT 0
+#define FDCAN_CKDIV_PDIV_MASK 0xF
+
+/** Amount of standard filters allocated in Message RAM
+ * This number may vary between devices. 28 is value valid
+ * for STM32G4
+ **/
+#define FDCAN_SFT_MAX_NR 28
+
+/** Amount of extended filters allocated in Message RAM
+ * This number may vary between devices. 8 is value valid
+ * for STM32G4
+ **/
+#define FDCAN_EFT_MAX_NR 8
+
+#define FDCAN_LFSSA_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0000)
+#define FDCAN_LFESA_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0070)
+
+#define FDCAN_RXFIFOS_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x00B0)
+
+#define FDCAN_RXFIFO_OFFSET(can_base, fifo_id) \
+ (FDCAN_RXFIFOS_OFFSET(can_base) + (0x00D8 * (fifo_id)))
+
+#define FDCAN_TXEVT_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0260)
+
+#define FDCAN_TXBUF_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0278)
+
+BEGIN_DECLS
+
+unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id);
+unsigned fdcan_get_txbuf_element_size(uint32_t canport);
+
+END_DECLS
+
+
diff --git a/include/libopencm3/stm32/h7/fdcan.h b/include/libopencm3/stm32/h7/fdcan.h
new file mode 100644
index 00000000..0a5cbe7a
--- /dev/null
+++ b/include/libopencm3/stm32/h7/fdcan.h
@@ -0,0 +1,257 @@
+/** @defgroup fdcan_defines FDCAN Defines
+
+@ingroup STM32H7xx_defines
+
+
+@author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+
+LGPL License Terms @ref lgpl_license
+*/
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+#pragma once
+
+/** @{ */
+
+/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */
+
+/** @defgroup fdcan_block FDCAN block base addresses
+ * @{
+ */
+#define CAN1 FDCAN1_BASE
+#define CAN2 FDCAN2_BASE
+/**@}*/
+
+/* Size of FDCAN peripheral message RAM in bytes */
+#define CAN_MSG_SIZE 0x2800
+
+#define FDCAN_GFC(can_base) MMIO32(can_base + 0x0080)
+
+#define FDCAN_SIDFC(can_base) MMIO32(can_base + 0x0084)
+#define FDCAN_XIDFC(can_base) MMIO32(can_base + 0x0088)
+#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0090)
+
+#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0094)
+#define FDCAN_NDAT1(can_base) MMIO32(can_base + 0x0098)
+#define FDCAN_HDAT2(can_base) MMIO32(can_base + 0x009C)
+
+#define FDCAN_RXFIC_BASE 0x00A0
+#define FDCAN_RXFI_OFFSET 0x0010
+
+#define FDCAN_RXFIC(can_base, fifo_id) \
+ MMIO32((can_base) + FDCAN_RXFIC_BASE + (FDCAN_RXFI_OFFSET * (fifo_id)))
+
+#define FDCAN_RXF0C(can_base) FDCAN_RXFIC(can_base, 0)
+#define FDCAN_RXF1C(can_base) FDCAN_RXFIC(can_base, 1)
+
+#define FDCAN_RXFIS_BASE 0x00A4
+#define FDCAN_RXFIA_BASE 0x00A8
+
+#define FDCAN_RXBC(can_base) MMIO32(can_base + 0x00AC)
+
+#define FDCAN_RXESC(can_base) MMIO32(can_base + 0x00BC)
+#define FDCAN_TXESC(can_base) MMIO32(can_base + 0x00C8)
+#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00CC)
+#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00D0)
+#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D4)
+#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D8)
+#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00DC)
+#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00E0)
+#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E4)
+#define FDCAN_TXEFC(can_base) MMIO32(can_base + 0x00F0)
+#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00F4)
+#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00F8)
+
+#define FDCAN_TTTMC(can_base) MMIO32(can_base + 0x0100)
+#define FDCAN_TTRMC(can_base) MMIO32(can_base + 0x0104)
+#define FDCAN_TTOCF(can_base) MMIO32(can_base + 0x0108)
+#define FDCAN_TTMLM(can_base) MMIO32(can_base + 0x010C)
+#define FDCAN_TURCF(can_base) MMIO32(can_base + 0x0110)
+#define FDCAN_TTOCN(can_base) MMIO32(can_base + 0x0114)
+#define FDCAN_TTGTP(can_base) MMIO32(can_base + 0x0118)
+#define FDCAN_TTTMK(can_base) MMIO32(can_base + 0x011C)
+#define FDCAN_TTTIR(can_base) MMIO32(can_base + 0x0120)
+#define FDCAN_TTIE(can_base) MMIO32(can_base + 0x0124)
+#define FDCAN_TTILS(can_base) MMIO32(can_base + 0x0128)
+#define FDCAN_TTOST(can_base) MMIO32(can_base + 0x012C)
+#define FDCAN_TURNA(can_base) MMIO32(can_base + 0x0130)
+#define FDCAN_TTLGT(can_base) MMIO32(can_base + 0x0134)
+#define FDCAN_TTCTC(can_base) MMIO32(can_base + 0x0138)
+#define FDCAN_TTCPT(can_base) MMIO32(can_base + 0x013C)
+#define FDCAN_TTCSM(can_base) MMIO32(can_base + 0x0140)
+#define FDCAN_TTTS(can_base) MMIO32(can_base + 0x0300)
+
+#define FDCAN_CCU_CCFG MMIO32(CAN_CCU_BASE + 0x0004)
+#define FDCAN_CCU_CREL MMIO32(CAN_CCU_BASE + 0x0000)
+
+#define FDCAN_GFC_RRFE (1 << 0)
+#define FDCAN_GFC_RRFS (1 << 1)
+
+/* ANFE[1:0]: Accept non-matching frames w/ extended ID */
+#define FDCAN_GFC_ANFE_SHIFT 2
+#define FDCAN_GFC_ANFE_MASK 0x3
+
+/* ANFS[1:0]: Accept non-matching frames w/ standard ID */
+#define FDCAN_GFC_ANFS_SHIFT 4
+#define FDCAN_GFC_ANFS_MASK 0x3
+
+#define FDCAN_FXS_MASK 0xFF
+#define FDCAN_FXS_SHIFT 16
+
+/* Position of start address of relocatable object within register */
+#define FDCAN_FXSA_MASK 0x3FFF
+#define FDCAN_FXSA_SHIFT 2
+
+/* LSS[7:0]: List size of standard ID filters */
+#define FDCAN_SIDFC_LSS_MASK FDCAN_FXS_MASK
+#define FDCAN_SIDFC_LSS_SHIFT FDCAN_FXS_SHIFT
+
+/* LFSSA[13:0]: Filter List standard start address */
+#define FDCAN_SIDFC_FLSSA_MASK FDCAN_FXSA_MASK
+#define FDCAN_SIDFC_FLSSA_SHIFT FDCAN_FXSA_SHIFT
+
+/* LSE[7:0]: List size of extended ID filters */
+#define FDCAN_XIDFC_LSE_MASK FDCAN_FXS_MASK
+#define FDCAN_XIDFC_LSE_SHIFT FDCAN_FXS_SHIFT
+
+/* LFSSA[7:0]: Filter List extended start address */
+#define FDCAN_XIDFC_FLESA_MASK FDCAN_FXSA_MASK
+#define FDCAN_XIDFC_FLESA_SHIFT FDCAN_FXSA_SHIFT
+
+/* TFQS[5:0]: Tx FIFO/Queue size */
+#define FDCAN_TXBC_TFQS_MASK 0x3F
+#define FDCAN_TXBC_TFQS_SHIFT 24
+
+/* TBSA[7:0]: Transmit buffer start address */
+#define FDCAN_TXBC_TBSA_MASK FDCAN_FXSA_MASK
+#define FDCAN_TXBC_TBSA_SHIFT FDCAN_FXSA_SHIFT
+
+#define FDCAN_TXEFC_EFS_MASK 0x3F
+#define FDCAN_TXEFC_EFS_SHIFT 16
+
+/* EFSA[7:0]: (Transmit) event FIFO start address */
+#define FDCAN_TXEFC_EFSA_MASK FDCAN_FXSA_MASK
+#define FDCAN_TXEFC_EFSA_SHIFT FDCAN_FXSA_SHIFT
+
+#define FDCAN_RXFIC_FIOM (1 << 31)
+
+#define FDCAN_RXFIC_FIWM_MASK 0x7F
+#define FDCAN_RXFIC_FIWM_SHIFT 24
+
+#define FDCAN_RXFIC_FIS_MASK 0x7F
+#define FDCAN_RXFIC_FIS_SHIFT 16
+
+
+#define FDCAN_RXFIC_FISA_MASK FDCAN_FXSA_MASK
+#define FDCAN_RXFIC_FISA_SHIFT FDCAN_FXSA_SHIFT
+
+#define FDCAN_RXF0C_F0OM FDCAN_RXFIC_FIOM
+
+/* F0WM[6:0]: FIFO0 watermark mode */
+#define FDCAN_RXF0C_F0WM_MASK FDCAN_RXFIC_FIWM_MASK
+#define FDCAN_RXF0C_F0WM_SHIFT FDCAN_RXFIC_FIWM_SHIFT
+
+/* F0S[6:0]: FIFO0 size */
+#define FDCAN_RXF0C_F0S_MASK FDCAN_RXFIC_FIS_MASK
+#define FDCAN_RXF0C_F0S_SHIFT FDCAN_RXFIC_FIS_SHIFT
+
+/* F0SA[13:0]: FIFO0 start address */
+#define FDCAN_RXF0C_F0SA_MASK FDCAN_RXFIC_FISA_MASK
+#define FDCAN_RXF0C_F0SA_SHIFT FDCAN_RXFIC_FISA_SHIFT
+
+#define FDCAN_RXF1C_F1OM FDCAN_RXFIC_FIOM
+
+/* F1WM[6:0]: FIFO1 watermark mode */
+#define FDCAN_RXF1C_F1WM_MASK FDCAN_RXFIC_FIWM_MASK
+#define FDCAN_RXF1C_F1WM_SHIFT FDCAN_RXFIC_FIWM_SHIFT
+
+/* F1S[6:0]: FIFO1 size */
+#define FDCAN_RXF1C_F1S_MASK FDCAN_RXFIC_FIS_MASK
+#define FDCAN_RXF1C_F1S_SHIFT FDCAN_RXFIC_FIS_SHIFT
+
+/* F1SA[13:0]: FIFO1 start address */
+#define FDCAN_RXF1C_F1SA_MASK FDCAN_RXFIC_FISA_MASK
+#define FDCAN_RXF1C_F1SA_SHIFT FDCAN_RXFIC_FISA_SHIFT
+
+/* RBDS[3:0]: RX buffer data field size */
+#define FDCAN_RXESC_RBDS_MASK 0x7
+#define FDCAN_RXESC_RBDS_SHIFT 8
+
+/* F0DS[3:0]: FIFO0 data field size */
+#define FDCAN_RXESC_F0DS_MASK 0x7
+#define FDCAN_RXESC_F0DS_SHIFT 0
+
+/* F1DS[3:0]: FIFO1 data field size */
+#define FDCAN_RXESC_F1DS_MASK 0x7
+#define FDCAN_RXESC_F1DS_SHIFT 4
+
+/* TBDS[3:0]: TX buffer data field size */
+#define FDCAN_TXESC_TBDS_MASK 0x7
+#define FDCAN_TXESC_TBDS_SHIFT 0
+
+#define FDCAN_RXFIFO_FL_MASK 0x7F
+#define FDCAN_RXFIFO_GI_MASK 0x3F
+#define FDCAN_RXFIFO_PI_MASK 0x3F
+
+#define FDCAN_RXFIFO_AI_MASK 0x3F
+
+#define FDCAN_TXFQS_TFFL_MASK 0x3F
+#define FDCAN_TXFQS_TFGI_MASK 0x1F
+#define FDCAN_TXFQS_TFQPI_MASK 0x1F
+
+#define FDCAN_TXEFS_EFFL_MASK 0x3F
+#define FDCAN_TXEFS_EFGI_MASK 0x1F
+#define FDCAN_TXEFS_EFPI_MASK 0x1F
+
+/* PDIV[3:0]: Input clock divider */
+#define FDCAN_CCU_CCFG_CDIV_SHIFT 16
+#define FDCAN_CCU_CCFG_CDIV_MASK 0xF
+
+
+
+#define FDCAN_LFSSA_OFFSET(can_base) \
+ (FDCAN_SIDFC(can_base) & (FDCAN_SIDFC_FLSSA_MASK << FDCAN_SIDFC_FLSSA_SHIFT))
+
+#define FDCAN_LFESA_OFFSET(can_base) \
+ (FDCAN_XIDFC(can_base) & (FDCAN_XIDFC_FLESA_MASK << FDCAN_XIDFC_FLESA_SHIFT))
+
+#define FDCAN_RXFIFO_OFFSET(can_base, fifo_id) \
+ (FDCAN_RXFIC(can_base, fifo_id) & (FDCAN_FXSA_MASK << FDCAN_FXSA_SHIFT))
+
+#define FDCAN_TXBUF_OFFSET(can_base) \
+ (FDCAN_TXBC(can_base) & (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT))
+
+#define FDCAN_TXEVT_OFFSET(can_base) \
+ (FDCAN_TXEFC(can_base) & (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT))
+
+BEGIN_DECLS
+
+void fdcan_init_std_filter_ram(uint32_t canport, uint32_t flssa, uint8_t lss);
+void fdcan_init_ext_filter_ram(uint32_t canport, uint32_t flesa, uint8_t lse);
+void fdcan_init_fifo_ram(uint32_t canport, unsigned fifo_id, uint32_t fxsa, uint8_t fxs);
+void fdcan_init_tx_event_ram(uint32_t canport, uint32_t tesa, uint8_t tes);
+void fdcan_init_tx_buffer_ram(uint32_t canport, uint32_t tbsa, uint8_t tbs);
+unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id);
+unsigned fdcan_get_txbuf_element_size(uint32_t canport);
+int fdcan_set_rx_element_size(uint32_t canport, uint8_t rxbuf, uint8_t rxfifo0, uint8_t rxfifo1);
+int fdcan_set_tx_element_size(uint32_t canport, uint8_t txbuf);
+
+END_DECLS
+
diff --git a/lib/stm32/common/fdcan.c b/lib/stm32/common/fdcan_common.c
similarity index 73%
rename from lib/stm32/common/fdcan.c
rename to lib/stm32/common/fdcan_common.c
index 30e6956a..ef7eeab9 100644
--- a/lib/stm32/common/fdcan.c
+++ b/lib/stm32/common/fdcan_common.c
@@ -1,22 +1,3 @@
-/** @defgroup fdcan_file FDCAN peripheral API
- *
- * @ingroup peripheral_apis
- *
- * @brief libopencm3 STM32 FDCAN
- *
- * @version 1.0.0
- *
- * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa
- *
- * Devices can have up to three FDCAN peripherals residing in one FDCAN block. The peripherals
- * support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
- * bitrate switching is supported. The peripheral has several filters for incoming messages that
- * can be distributed between two FIFOs and three transmit mailboxes. For transmitted messages
- * it is possible to opt for event notification once message is transmitted.
- *
- * LGPL License Terms @ref lgpl_license
-*/
-
/*
* This file is part of the libopencm3 project.
*
@@ -53,11 +34,11 @@
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
* @param [in] set new value of INIT, true means set
* @param [in] timeout Amount of busyloop cycles, function will wait for FDCAN
- * to switch it's state. If set to 0, then function returns immediately.
+ * to switch it's state. If set to 0, then function returns immediately.
* @returns FDCAN_E_OK on success, FDCAN_E_TIMEOUT if INIT bit value
* didn't change before timeout has expired.
*/
-static int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout)
+int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout)
{
uint32_t expected;
uint32_t wait_ack;
@@ -135,43 +116,71 @@ static void fdcan_get_fill_rxfifo(uint32_t canport, uint8_t fifo_id, unsigned *g
& FDCAN_RXFIFO_FL_MASK;
}
-/** Obtain address of FDCAN Message RAM for certain FDCAN block.
+/** Returns standard filter start address in message RAM
*
- * @param [in] canport identification of FDCAN block. See @ref fdcan_block.
- * @return Address of Message RAM for given FDCAN block or null pointer
- * if FDCAN block identification is invalid.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns Base address of standard filter configuration block.
*/
-static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport)
+struct fdcan_standard_filter *fdcan_get_flssa_addr(uint32_t canport)
{
- /* This piece of code may look wrong, after one examines
- * STM32G4 datasheet and/or g4/memorymap.h. There are three
- * memory regions defined for FDCANx_RAM_BASE. They are 0x400
- * bytes apart as per chapter 2.2.2 of [RM0440].
- *
- * It turns out, that these addresses are not in line with what
- * is specified later in chapter 44.3.3 of [RM0440]. There it is
- * stated, that message RAMs are packed and in case of multiple
- * FDCAN blocks, message RAM for n-th FDCAN starts at address
- * end of (n-1)-th block + 4 (explicitly, offset 0x354).
- *
- * It turns out, that this statement is also false! In fact FDCAN
- * message RAMs are packed tightly and n-th block starts immediately
- * after (n-1)-th block ends. Thus offset is going to be computed
- * using formula:
- *
- * FDCAN1_RAM_BASE + (block_id * sizeof(struct fdcan_message_ram))
- */
- if (canport == CAN1) {
- return (struct fdcan_message_ram *) (FDCAN1_RAM_BASE + 0);
- } else if (canport == CAN2) {
- return (struct fdcan_message_ram *)
- (FDCAN1_RAM_BASE + sizeof(struct fdcan_message_ram));
- } else if (canport == CAN3) {
- return (struct fdcan_message_ram *)
- (FDCAN1_RAM_BASE + (2 * sizeof(struct fdcan_message_ram)));
- }
+ struct fdcan_standard_filter *lfssa = (struct fdcan_standard_filter *)
+ (CAN_MSG_BASE + FDCAN_LFSSA_OFFSET(canport));
+ return lfssa;
+}
- return NULL;
+/** Returns extended filter start address in message RAM
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns Base address of extended filter configuration block.
+ */
+struct fdcan_extended_filter *fdcan_get_flesa_addr(uint32_t canport)
+{
+ struct fdcan_extended_filter *lfesa = (struct fdcan_extended_filter *)
+ (CAN_MSG_BASE + FDCAN_LFESA_OFFSET(canport));
+ return lfesa;
+}
+
+/** Returns FIFO start address in message RAM
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of FIFO whose address is requested
+ * @returns Base address of FIFO block.
+ */
+struct fdcan_rx_fifo_element *fdcan_get_rxfifo_addr(uint32_t canport,
+ unsigned fifo_id, unsigned element_id)
+{
+ struct fdcan_rx_fifo_element *rxfifo = (struct fdcan_rx_fifo_element *)
+ (CAN_MSG_BASE + FDCAN_RXFIFO_OFFSET(canport, fifo_id)
+ + (element_id * fdcan_get_fifo_element_size(canport, fifo_id))
+ );
+ return rxfifo;
+}
+
+/** Returns transmit event start address in message RAM
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns Base address of transmit event block.
+ */
+struct fdcan_tx_event_element *fdcan_get_txevt_addr(uint32_t canport)
+{
+ struct fdcan_tx_event_element *rxfifo = (struct fdcan_tx_event_element *)
+ (CAN_MSG_BASE + FDCAN_TXEVT_OFFSET(canport));
+ return rxfifo;
+}
+
+/** Returns transmit buffer start address in message RAM
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns Base address of transmit buffer block.
+ */
+struct fdcan_tx_buffer_element *fdcan_get_txbuf_addr(uint32_t canport, unsigned element_id)
+{
+ struct fdcan_tx_buffer_element *rxfifo = (struct fdcan_tx_buffer_element *)
+ (CAN_MSG_BASE + FDCAN_TXBUF_OFFSET(canport)
+ + (element_id * fdcan_get_txbuf_element_size(canport))
+ );
+
+ return rxfifo;
}
/** Converts frame length to DLC value.
@@ -183,7 +192,7 @@ static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport)
* @returns DLC value representing lengths or 0xFF if length cannot
* be encoded into DLC format (applies only to FDCAN frame lengths)
*/
-static uint32_t fdcan_length_to_dlc(uint8_t length)
+uint32_t fdcan_length_to_dlc(uint8_t length)
{
if (length <= 8) {
return length;
@@ -207,7 +216,7 @@ static uint32_t fdcan_length_to_dlc(uint8_t length)
* @param [in] dlc DLC value
* @returns data payload length in bytes
*/
-static uint8_t fdcan_dlc_to_length(uint32_t dlc)
+uint8_t fdcan_dlc_to_length(uint32_t dlc)
{
if (dlc <= 8) {
return dlc;
@@ -237,13 +246,13 @@ static uint8_t fdcan_dlc_to_length(uint32_t dlc)
* * @ref fdcan_init_filter
* * @ref fdcan_set_test
*
- * You can check if FDCAN block is in INIT mode or it is started using
+ * You can check if FDCAN block is in INIT mode or it is started using
* @ref fdcan_get_init_state.
*
* @param[in] canport CAN register base address. See @ref fdcan_block.
* @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
- * confirming that it entered INIT mode. If set to 0, function will return
- * immediately.
+ * confirming that it entered INIT mode. If set to 0, function will
+ * return immediately.
* @returns Operation error status. See @ref fdcan_error.
*/
int fdcan_init(uint32_t canport, uint32_t timeout)
@@ -309,16 +318,11 @@ void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locke
FDCAN_CCCR(canport) &= ~FDCAN_CCCR_MON;
}
- if (rx_fifo_locked) {
- FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM);
- } else {
- FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM;
- }
-
+ fdcan_set_fifo_locked_mode(canport, rx_fifo_locked);
}
/** Set FDCAN block parameters for FDCAN transmission
- *
+ *
* Enables and configures parameters related to FDCAN transmission. This function
* allows configuration of bitrate switching, FDCAN frame format and fast mode
* timing. This function can only be called if FDCAN block is in INIT mode.
@@ -381,36 +385,6 @@ void fdcan_set_test(uint32_t canport, bool testing, bool loopback)
}
}
-/** Enable FDCAN operation after FDCAN block has been set up.
- *
- * This function will disable FDCAN configuration effectively
- * allowing FDCAN to sync up with the bus. After calling this function
- * it is not possible to reconfigure amount of filter rules, yet
- * it is possible to configure rules themselves. FDCAN block operation
- * state can be checked using @ref fdcan_get_init_state.
- *
- * @param [in] canport FDCAN block base address. See @ref fdcan_block.
- * @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
- * confirming that it left INIT mode. If set to 0, function will return
- * immediately.
- * @returns Operation error status. See @ref fdcan_error.
- * @note If this function returns with timeout, it usually means that
- * FDCAN_clk is not set up properly.
- */
-int fdcan_start(uint32_t canport, uint32_t timeout)
-{
- /* Error here usually means, that FDCAN_clk is not set up
- * correctly, or at all. This usually can't be seen above
- * when INIT is set to 1, because default value for INIT is
- * 1 as long as one has FDCAN_pclk configured properly.
- **/
- if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
- return FDCAN_E_TIMEOUT;
- }
-
- return FDCAN_E_OK;
-}
-
/** Return current FDCAN block operation state.
*
* This function effectively returns value of FDCAN_CCCR's INIT bit.
@@ -422,59 +396,6 @@ int fdcan_get_init_state(uint32_t canport)
return ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT);
}
-/** Configure amount of filters and initialize filtering block.
- *
- * This function allows to configure global amount of filters present.
- * FDCAN block will only ever check as many filters as this function configures.
- * Function will also clear all filter blocks to zero values. This function
- * can be only called after @ref fdcan_init has already been called and
- * @ref fdcan_start has not been called yet as registers holding filter
- * count are write-protected unless FDCAN block is in INIT mode. It is possible
- * to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
- * after FDCAN block has already been started.
- *
- * @param [in] canport FDCAN block base address. See @ref fdcan_block.
- * @param [in] std_filt requested amount of standard ID filter rules (0-28)
- * @param [in] ext_filt requested amount of extended ID filter rules (0-8)
- */
-void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
-{
- struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
-
- /* Only perform initialization of message RAM if there are
- * any filters required
- */
- if (std_filt > 0) {
- FDCAN_RXGFC(canport) =
- (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT))
- | (std_filt << FDCAN_RXGFC_LSS_SHIFT);
-
-
- for (int q = 0; q < FDCAN_SFT_MAX_NR; ++q) {
- ram->lfssa[q].type_id1_conf_id2 = 0;
- }
- } else {
- /* Reset filter count to zero */
- FDCAN_RXGFC(canport) =
- (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT));
- }
-
- if (ext_filt > 0) {
- FDCAN_RXGFC(canport) =
- (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT))
- | (ext_filt << FDCAN_RXGFC_LSE_SHIFT);
-
- for (int q = 0; q < FDCAN_EFT_MAX_NR; ++q) {
- ram->lfesa[q].conf_id1 = 0;
- ram->lfesa[q].type_id2 = 0;
- }
- } else {
- /* Reset filter count to zero */
- FDCAN_RXGFC(canport) =
- (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT));
- }
-}
-
/** Configure filter rule for standard ID frames.
*
* Sets up filter rule for frames having standard ID. Each FDCAN block can
@@ -483,20 +404,20 @@ void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
*
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
* @param [in] nr number of filter to be configured
- * @param [in] id_list_mode Mode in which id1 and id2 are used to match the rule.
- * See @ref fdcan_sft.
+ * @param [in] id_list_mode Mode in which id1 and id2 are used to match the rule.
+ * See @ref fdcan_sft.
* @param [in] id1 standard ID for matching. Used as exact value, lower bound or bit
* pattern depending on matching mode selected
* @param [in] id2 standard ID or bitmask. Used as exact value, upper bound or bit mask
* depending on matching mode selected
- * @param [in] action Action performed if filtering rule matches frame ID.
- * See @ref fdcan_sfec.
+ * @param [in] action Action performed if filtering rule matches frame ID.
+ * See @ref fdcan_sfec.
*/
void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
uint8_t id_list_mode, uint32_t id1, uint32_t id2,
uint8_t action)
{
- struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+ struct fdcan_standard_filter *lfssa = fdcan_get_flssa_addr(canport);
/* id_list_mode and action are passed unguarded. Simply use
* defines and it will be OK. id1 and id2 are masked for
@@ -506,7 +427,7 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
* overflow into flags. This tends to be extremely time
* consuming to debug.
*/
- ram->lfssa[nr].type_id1_conf_id2 =
+ lfssa[nr].type_id1_conf_id2 =
(id_list_mode << FDCAN_SFT_SHIFT)
| (action << FDCAN_SFEC_SHIFT)
| ((id1 & FDCAN_SFID1_MASK) << FDCAN_SFID1_SHIFT)
@@ -524,25 +445,25 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
* @param [in] canport FDCAN block base address. See @ref fdcan_block.
* @param [in] nr number of filter to be configured
* @param [in] id_list_mode mode in which id1 and id2 are used to match the rule.
- * See @ref fdcan_eft.
+ * See @ref fdcan_eft.
* @param [in] id1 extended ID for matching. Used as exact value, lower bound or bit
* pattern depending on matching mode selected
* @param [in] id2 extended ID or bitmask. Used as exact value, upper bound or bit mask
* depending on matching mode selected
* @param [in] action Action performed if filtering rule matches frame ID.
- * See @ref fdcan_efec.
+ * See @ref fdcan_efec.
*/
void fdcan_set_ext_filter(uint32_t canport, uint32_t nr,
uint8_t id_list_mode, uint32_t id1, uint32_t id2,
uint8_t action)
{
- struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+ struct fdcan_extended_filter *lfesa = fdcan_get_flesa_addr(canport);
- ram->lfesa[nr].conf_id1 =
+ lfesa[nr].conf_id1 =
(action << FDCAN_EFEC_SHIFT)
| ((id1 & FDCAN_EFID1_MASK) << FDCAN_EFID1_SHIFT);
- ram->lfesa[nr].type_id2 =
+ lfesa[nr].type_id2 =
(id_list_mode << FDCAN_EFT_SHIFT)
| ((id2 & FDCAN_EFID2_MASK) << FDCAN_EFID2_SHIFT);
}
@@ -572,7 +493,7 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
return mailbox;
}
- struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+ struct fdcan_tx_buffer_element *tx_buffer = fdcan_get_txbuf_addr(canport, mailbox);
/* Early check: if FDCAN message lentgh is > 8, it must be
* a multiple of 4 *and* fdcan format must be enabled.
@@ -584,15 +505,15 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
}
if (ext) {
- ram->tx_buffer[mailbox].identifier_flags = FDCAN_FIFO_XTD
+ tx_buffer->identifier_flags = FDCAN_FIFO_XTD
| ((id & FDCAN_FIFO_EID_MASK) << FDCAN_FIFO_EID_SHIFT);
} else {
- ram->tx_buffer[mailbox].identifier_flags =
+ tx_buffer->identifier_flags =
(id & FDCAN_FIFO_SID_MASK) << FDCAN_FIFO_SID_SHIFT;
}
if (rtr) {
- ram->tx_buffer[mailbox].identifier_flags |= FDCAN_FIFO_RTR;
+ tx_buffer->identifier_flags |= FDCAN_FIFO_RTR;
}
if (fdcan_fmt) {
@@ -603,11 +524,11 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
flags |= FDCAN_FIFO_BRS;
}
- ram->tx_buffer[mailbox].evt_fmt_dlc_res =
+ tx_buffer->evt_fmt_dlc_res =
(dlc << FDCAN_FIFO_DLC_SHIFT) | flags;
for (int q = 0; q < length; q += 4) {
- ram->tx_buffer[mailbox].data[q / 4] = *((uint32_t *) &data[q]);
+ tx_buffer->data[q / 4] = *((uint32_t *) &data[q]);
}
FDCAN_TXBAR(canport) |= 1 << mailbox;
@@ -640,52 +561,49 @@ int fdcan_receive(uint32_t canport, uint8_t fifo_id, bool release, uint32_t *id,
bool *ext, bool *rtr, uint8_t *fmi, uint8_t *length,
uint8_t *data, uint16_t *timestamp)
{
- const struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
-
- const struct fdcan_rx_fifo_element *fifo;
-
unsigned pending_frames, get_index, dlc, len;
fdcan_get_fill_rxfifo(canport, fifo_id, &get_index, &pending_frames);
- fifo = ram->rx_fifo[fifo_id];
-
if (pending_frames == 0) {
return FDCAN_E_NOTAVAIL;
}
- dlc = (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_DLC_SHIFT)
+ const struct fdcan_rx_fifo_element *fifo = fdcan_get_rxfifo_addr(canport,
+ fifo_id, get_index);
+
+ dlc = (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_DLC_SHIFT)
& FDCAN_FIFO_DLC_MASK;
len = fdcan_dlc_to_length(dlc);
*length = len;
- if ((fifo[get_index].identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) {
+ if ((fifo->identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) {
*ext = true;
- *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_EID_SHIFT)
+ *id = (fifo->identifier_flags >> FDCAN_FIFO_EID_SHIFT)
& FDCAN_FIFO_EID_MASK;
} else {
*ext = false;
- *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_SID_SHIFT)
+ *id = (fifo->identifier_flags >> FDCAN_FIFO_SID_SHIFT)
& FDCAN_FIFO_SID_MASK;
}
if (timestamp) {
- *timestamp = (uint16_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_RXTS_SHIFT)
+ *timestamp = (uint16_t) (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_RXTS_SHIFT)
& FDCAN_FIFO_RXTS_MASK;
}
if (fmi) {
- *fmi = (uint8_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_MM_SHIFT)
+ *fmi = (uint8_t) (fifo->filt_fmt_dlc_ts >> FDCAN_FIFO_MM_SHIFT)
& FDCAN_FIFO_MM_MASK;
}
if (rtr) {
- *rtr = ((fifo[get_index].identifier_flags & FDCAN_FIFO_RTR) == FDCAN_FIFO_RTR);
+ *rtr = ((fifo->identifier_flags & FDCAN_FIFO_RTR) == FDCAN_FIFO_RTR);
}
for (unsigned int q = 0; q < len; q += 4) {
- *((uint32_t *) &data[q]) = fifo[get_index].data[q / 4];
+ *((uint32_t *) &data[q]) = fifo->data[q / 4];
}
if (release) {
diff --git a/lib/stm32/g4/Makefile b/lib/stm32/g4/Makefile
index 63a13e81..70318c6f 100644
--- a/lib/stm32/g4/Makefile
+++ b/lib/stm32/g4/Makefile
@@ -40,7 +40,7 @@ OBJS += crs_common_all.o
OBJS += dac_common_all.o dac_common_v2.o
OBJS += dma_common_l1f013.o
OBJS += dmamux.o
-OBJS += fdcan.o
+OBJS += fdcan.o fdcan_common.o
OBJS += flash.o flash_common_all.o flash_common_f.o flash_common_idcache.o
OBJS += gpio_common_all.o gpio_common_f0234.o
OBJS += opamp_common_all.o opamp_common_v2.o
diff --git a/lib/stm32/g4/fdcan.c b/lib/stm32/g4/fdcan.c
new file mode 100644
index 00000000..b2193587
--- /dev/null
+++ b/lib/stm32/g4/fdcan.c
@@ -0,0 +1,187 @@
+/** @addtogroup fdcan_file FDCAN peripheral API
+ *
+ * @ingroup peripheral_apis
+ *
+ * @brief libopencm3 STM32 FDCAN
+ *
+ * @version 1.0.0
+ *
+ * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+ *
+ * Devices can have up to three FDCAN peripherals residing in one FDCAN block. The peripherals
+ * support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
+ * bitrate switching is supported. The peripheral has several filters for incoming messages that
+ * can be distributed between two FIFOs and three transmit mailboxes. For transmitted messages
+ * it is possible to opt for event notification once message is transmitted.
+ *
+ * LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+#include
+#include
+#include
+
+/* --- FD-CAN functions ----------------------------------------------------- */
+
+/** @ingroup fdcan_file */
+/**@{
+ * */
+
+/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO.
+ *
+ * Obtains value of FIFO entry length. For G4 it returns constant value as
+ * G4 has FIFO element length hardcoded.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused.
+ * @param [in] fifo_id ID of FIFO whole length is queried. Unused.
+ * @returns Length of FIFO entry length covering frame header and frame payload.
+ */
+unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id)
+{
+ /* Silences compiler. Variables are present for API compatibility
+ * with STM32H7
+ */
+ (void) (canport);
+ (void) (fifo_id);
+ return sizeof(struct fdcan_rx_fifo_element);
+}
+
+/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port.
+ *
+ * Obtains value of entry length in transmit queue/FIFO. For G4 it returns constant value
+ * as G4 has transmit buffer entries of fixed length.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused.
+ * @param [in] fifo_id ID of FIFO whole length is queried. Unused.
+ * @returns Length of FIFO entry length covering frame header and frame payload.
+ */
+unsigned fdcan_get_txbuf_element_size(uint32_t canport)
+{
+ /* Silences compiler. Variables are present for API compatibility
+ * with STM32H7
+ */
+ (void) (canport);
+ return sizeof(struct fdcan_tx_buffer_element);
+}
+
+/** Configure amount of filters and initialize filtering block.
+ *
+ * This function allows to configure global amount of filters present.
+ * FDCAN block will only ever check as many filters as this function configures.
+ * Function will also clear all filter blocks to zero values. This function
+ * can be only called after @ref fdcan_init has already been called and
+ * @ref fdcan_start has not been called yet as registers holding filter
+ * count are write-protected unless FDCAN block is in INIT mode. It is possible
+ * to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
+ * after FDCAN block has already been started.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] std_filt requested amount of standard ID filter rules (0-28)
+ * @param [in] ext_filt requested amount of extended ID filter rules (0-8)
+ */
+void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
+{
+ struct fdcan_standard_filter *lfssa = fdcan_get_flssa_addr(canport);
+ struct fdcan_extended_filter *lfesa = fdcan_get_flesa_addr(canport);
+
+ /* Only perform initialization of message RAM if there are
+ * any filters required
+ */
+ if (std_filt > 0) {
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT))
+ | (std_filt << FDCAN_RXGFC_LSS_SHIFT);
+
+
+ for (int q = 0; q < FDCAN_SFT_MAX_NR; ++q) {
+ lfssa[q].type_id1_conf_id2 = 0;
+ }
+ } else {
+ /* Reset filter count to zero */
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT));
+ }
+
+ if (ext_filt > 0) {
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT))
+ | (ext_filt << FDCAN_RXGFC_LSE_SHIFT);
+
+ for (int q = 0; q < FDCAN_EFT_MAX_NR; ++q) {
+ lfesa[q].conf_id1 = 0;
+ lfesa[q].type_id2 = 0;
+ }
+ } else {
+ /* Reset filter count to zero */
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT));
+ }
+}
+
+/** Enable FDCAN operation after FDCAN block has been set up.
+ *
+ * This function will disable FDCAN configuration effectively
+ * allowing FDCAN to sync up with the bus. After calling this function
+ * it is not possible to reconfigure amount of filter rules, yet
+ * it is possible to configure rules themselves. FDCAN block operation
+ * state can be checked using @ref fdcan_get_init_state.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
+ * confirming that it left INIT mode. If set to 0, function will return
+ * immediately.
+ * @returns Operation error status. See @ref fdcan_error.
+ * @note If this function returns with timeout, it usually means that
+ * FDCAN_clk is not set up properly.
+ */
+int fdcan_start(uint32_t canport, uint32_t timeout)
+{
+ /* Error here usually means, that FDCAN_clk is not set up
+ * correctly, or at all. This usually can't be seen above
+ * when INIT is set to 1, because default value for INIT is
+ * 1 as long as one has FDCAN_pclk configured properly.
+ **/
+ if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
+ return FDCAN_E_TIMEOUT;
+ }
+
+ return FDCAN_E_OK;
+}
+
+/** Configure FDCAN FIFO lock mode
+ *
+ * This function allows to choose between locked and overewrite mode of FIFOs. In locked mode,
+ * whenever FIFO is full and new frame arrives, which would normally been stored into given
+ * FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO
+ * is rewritten by frame just received.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] locked true activates locked mode, false activates overwrite mode
+ */
+void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked)
+{
+ if (locked) {
+ FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM);
+ } else {
+ FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM;
+ }
+}
+
+/** @} */
diff --git a/lib/stm32/h7/Makefile b/lib/stm32/h7/Makefile
index 5a133ec7..2c7cbb18 100644
--- a/lib/stm32/h7/Makefile
+++ b/lib/stm32/h7/Makefile
@@ -39,6 +39,7 @@ ARFLAGS = rcs
OBJS += dac_common_all.o dac_common_v2.o
OBJS += exti_common_all.o
+OBJS += fdcan.o fdcan_common.o
OBJS += flash_common_all.o flash_common_f.o flash_common_f24.o
OBJS += fmc_common_f47.o
OBJS += gpio_common_all.o gpio_common_f0234.o
diff --git a/lib/stm32/h7/fdcan.c b/lib/stm32/h7/fdcan.c
new file mode 100644
index 00000000..fd0cad0e
--- /dev/null
+++ b/lib/stm32/h7/fdcan.c
@@ -0,0 +1,473 @@
+/** @addtogroup fdcan_file FDCAN peripheral API
+ *
+ * @ingroup peripheral_apis
+ *
+ * @brief libopencm3 STM32 FDCAN
+ *
+ * @version 1.0.0
+ *
+ * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+ *
+ * Device is equipped with two FDCAN peripherals residing in one FDCAN block. The peripherals
+ * support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
+ * bitrate switching is supported. The peripheral has several filters for incoming messages that
+ * can be distributed between two FIFOs and transmit buffer all of configurable amount of
+ * entries. For transmitted messages it is possible to opt for event notification once message
+ * is transmitted.
+ *
+ * The FDCAN peripheral present in STM32 H7 is a superset of FDCAN peripheral found in other MCUs
+ * such as STM32 G4. It allows more fine-grade control over buffer and filter allocation and
+ * supports TTCAN on CAN1, etc. This driver provides source-level backwards compatible
+ * implementation of driver, which allows build of unmodified software originally written for
+ * STM32 G4 on STM32 H7.
+ *
+ * LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+#include
+#include
+#include
+
+#define FDCAN_LSS_COUNT(can_base) \
+ ((FDCAN_SIDFC(can_base) >> FDCAN_SIDFC_LSS_SHIFT) & FDCAN_SIDFC_LSS_MASK)
+
+#define FDCAN_LSE_COUNT(can_base) \
+ ((FDCAN_XIDFC(can_base) >> FDCAN_XIDFC_LSE_SHIFT) & FDCAN_XIDFC_LSE_MASK)
+
+/* --- FD-CAN functions ----------------------------------------------------- */
+
+/** @ingroup fdcan_file */
+/**@{
+ * */
+
+/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO.
+ *
+ * Obtains value of FIFO entry length. This value covers both fixed-size frame
+ * header block and payload buffer. User can configure payload buffer size individually
+ * for each FIFO and designated RX buffer.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of FIFO whole length is queried.
+ * @returns Length of FIFO entry length covering frame header and frame payload.
+ */
+unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id)
+{
+ unsigned element_size;
+ if (fifo_id == 0) {
+ element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F0DS_SHIFT;
+ } else {
+ element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F1DS_SHIFT;
+ }
+
+ /* Mask is unshifted and at this point, element_size is unshifted too */
+ return 8 + fdcan_dlc_to_length((element_size & FDCAN_RXESC_F0DS_MASK) | 0x7);
+}
+
+/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port.
+ *
+ * Obtains value of entry length in transmit queue/FIFO. This value covers both
+ * fixed-sized frame header block and payload buffer. User can configure payload buffer
+ * size.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of FIFO whole length is queried.
+ * @returns Length of FIFO entry length covering frame header and frame payload.
+ */
+unsigned fdcan_get_txbuf_element_size(uint32_t canport)
+{
+ unsigned element_size;
+ element_size = (FDCAN_TXESC(canport) >> FDCAN_TXESC_TBDS_SHIFT) & FDCAN_TXESC_TBDS_MASK;
+ return 8 + fdcan_dlc_to_length((element_size & FDCAN_TXESC_TBDS_MASK) | 0x7);
+}
+
+/** Initialize allocation of standard filter block in CAN message RAM.
+ *
+ * Allows specifying size of standard filtering block (in term of available filtering
+ * rules and filter base address within CAN message RAM. Note, that there are no limitations
+ * nor checking on address provided. It is possible to share whole filtering block or
+ * portion of it between multiple CAN interfaces.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] flssa standard filtering block start address offset in message RAM
+ * @param [in] lss amount of standard filters
+ */
+void fdcan_init_std_filter_ram(uint32_t canport, uint32_t flssa, uint8_t lss)
+{
+ FDCAN_SIDFC(canport) = flssa << FDCAN_SIDFC_FLSSA_SHIFT
+ | lss << FDCAN_SIDFC_LSS_SHIFT;
+}
+
+/** Initialize allocation of extended filter block in CAN message RAM.
+ *
+ * Allows specifying size of extended filtering block (in term of available filtering
+ * rules and filter base address within CAN message RAM. Note, that there are no limitations
+ * nor checking on address provided. It is possible to share whole filtering block or
+ * portion of it between multiple CAN interfaces.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] flesa extended filtering block start address offset in message RAM
+ * @param [in] lse amount of extended filters
+ */
+void fdcan_init_ext_filter_ram(uint32_t canport, uint32_t flesa, uint8_t lse)
+{
+ FDCAN_XIDFC(canport) = flesa << FDCAN_XIDFC_FLESA_SHIFT
+ | lse << FDCAN_XIDFC_LSE_SHIFT;
+}
+
+/** Initialize allocation of FIFO block in CAN message RAM.
+ *
+ * Allows specifying size of FIFO block (in term of available messages in FIFO
+ * and FIFO base address within CAN message RAM. Note, that there are no limitations
+ * nor checking on address provided.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of fifo being configured
+ * @param [in] fxsa FIFO block start address offset in message RAM
+ * @param [in] fxs amount of entries allocated in FIFO
+ */
+void fdcan_init_fifo_ram(uint32_t canport, unsigned fifo_id, uint32_t fxsa, uint8_t fxs)
+{
+ FDCAN_RXFIC(canport, fifo_id) = (FDCAN_RXFIC(canport, fifo_id)
+ & ~(
+ (FDCAN_RXFIC_FIS_MASK << FDCAN_RXFIC_FIS_SHIFT)
+ | (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT)
+ ))
+ | (fxs << FDCAN_RXFIC_FIS_SHIFT)
+ | (fxsa & (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT));
+}
+
+/** Initialize allocation of transmit event block in CAN message RAM.
+ *
+ * Allows specifying size of transmit event block (in term of allocated events and block
+ * base address within CAN message RAM. Note, that there are no limitations
+ * nor checking on address provided.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of fifo being configured
+ * @param [in] fxsa FIFO block start address offset in message RAM
+ * @param [in] fxs amount of entries allocated in FIFO
+ */
+void fdcan_init_tx_event_ram(uint32_t canport, uint32_t tesa, uint8_t tes)
+{
+ FDCAN_TXEFC(canport) = (FDCAN_TXEFC(canport)
+ & ~(
+ (FDCAN_TXEFC_EFS_MASK << FDCAN_TXEFC_EFS_SHIFT)
+ | (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT)
+ ))
+ | (tes << FDCAN_TXEFC_EFS_SHIFT)
+ | (tesa & (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT));
+}
+
+/** Initialize allocation of transmit queue block in CAN message RAM.
+ *
+ * Allows specifying size of transmit queue block (in term of allocated buffers and block
+ * base address within CAN message RAM. Note, that there are no limitations
+ * nor checking on address provided.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of fifo being configured
+ * @param [in] fxsa FIFO block start address offset in message RAM
+ * @param [in] fxs amount of entries allocated in FIFO
+ */
+void fdcan_init_tx_buffer_ram(uint32_t canport, uint32_t tbsa, uint8_t tbs)
+{
+ FDCAN_TXBC(canport) = (FDCAN_TXBC(canport)
+ & ~(
+ (FDCAN_TXBC_TFQS_MASK << FDCAN_TXBC_TFQS_SHIFT)
+ | (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT)
+ ))
+ | (tbs << FDCAN_TXBC_TFQS_SHIFT)
+ | (tbsa & (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT));
+}
+
+/** Initialize size of data fields in reception buffers.
+ *
+ * Configures maximum size of message payload, which can be stored in different
+ * reception buffers. Each buffer can have maximum payload size configured independently.
+ * Buffers can only be configured to sizes which are valid sizes of FDCAN payload. Sizes smaller
+ * than 8 are automatically padded to 8.
+ * @note If you change these values, then content of reception buffers is invalidated. You may
+ * also want to recalculate base addresses of objects in message RAM as their size might have
+ * changed.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] rxbuf maximum storable payload size of dedicated RX buffer
+ * @param [in] rxfifo0 maximum storable payload size of RX FIFO 0
+ * @param [in] rxfifo1 maximum storable payload size of RX FIFO 1
+ * @returns operation return status. See @ref fdcan_error.
+ */
+int fdcan_set_rx_element_size(uint32_t canport, uint8_t rxbuf, uint8_t rxfifo0, uint8_t rxfifo1)
+{
+ unsigned rxbufdlc = fdcan_length_to_dlc(rxbuf);
+ unsigned rxfifo0dlc = fdcan_length_to_dlc(rxfifo0);
+ unsigned rxfifo1dlc = fdcan_length_to_dlc(rxfifo1);
+
+ if (rxbufdlc == 0xFF || rxfifo0dlc == 0xFF || rxfifo1dlc == 0xFF) {
+ return FDCAN_E_INVALID;
+ }
+
+ /* RXESC fields use format of FDCAN DLC, albeit using only
+ * three LSBs. DLC < 8 is always allocated as 8 bytes and is always
+ * encoded as 0.
+ */
+ if (rxbufdlc <= 8) {
+ rxbufdlc = 0;
+ } else {
+ rxbufdlc &= ~(1 << 4);
+ }
+
+ if (rxfifo0dlc <= 8) {
+ rxfifo0dlc = 0;
+ } else {
+ rxfifo0dlc &= ~(1 << 4);
+ }
+
+ if (rxfifo1dlc <= 8) {
+ rxfifo1dlc = 0;
+ } else {
+ rxfifo1dlc &= ~(1 << 4);
+ }
+
+ FDCAN_RXESC(canport) = rxbufdlc << FDCAN_RXESC_RBDS_SHIFT
+ | rxfifo1dlc << FDCAN_RXESC_F1DS_SHIFT
+ | rxfifo0dlc << FDCAN_RXESC_F0DS_SHIFT;
+
+ return FDCAN_E_OK;
+}
+
+/** Initialize size of data fields in transmit buffers.
+ *
+ * Configures maximum size of message payload, which can be stored either in dedicated
+ * transmit buffer or into transmit queue/FIFO. One size is applied both to transmit buffer
+ * and transmit queue / FIFO. Buffers can only be configured to sizes which are valid sizes
+ * of FDCAN payload. Sizes smaller than 8 are automatically padded to 8 bytes.
+ * @note If you change these values, then content of transmission buffers is invalidated. You may
+ * also want to recalculate base addresses of objects in message RAM as their size might have
+ * changed.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] txbuf maximum storable payload size of TX buffer / FIFO / queue
+ * @returns operation return status. See @ref fdcan_error.
+ */
+int fdcan_set_tx_element_size(uint32_t canport, uint8_t txbuf)
+{
+ unsigned txbufdlc = fdcan_length_to_dlc(txbuf);
+
+ if (txbufdlc == 0xFF) {
+ return FDCAN_E_INVALID;
+ }
+
+ if (txbufdlc <= 8) {
+ txbufdlc = 0;
+ } else {
+ txbufdlc &= ~(1 << 4);
+ }
+
+ FDCAN_TXESC(canport) = txbufdlc << FDCAN_TXESC_TBDS_SHIFT;
+
+ return FDCAN_E_OK;
+}
+
+/** Configure amount of filters and initialize filtering block.
+ *
+ * This function allows to configure global amount of filters present.
+ * FDCAN block will only ever check as many filters as this function configures.
+ * Function will also clear all filter blocks to zero values. This function
+ * can be only called after @ref fdcan_init has already been called and
+ * @ref fdcan_start has not been called yet as registers holding filter
+ * count are write-protected unless FDCAN block is in INIT mode. It is possible
+ * to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
+ * after FDCAN block has already been started.
+ *
+ * This function is provided for source level compatibility with code written for
+ * STM32G4. As an alternative, you can use @ref fdcan_init_std_filter_ram() and
+ * @ref fdcan_init_ext_filter_ram() to have more control over filtering configuration.
+ * Note that if you do so, your code won't be compatible with STM32G4 controllers.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] std_filt requested amount of standard ID filter rules (0-28)
+ * @param [in] ext_filt requested amount of extended ID filter rules (0-8)
+ */
+void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
+{
+ struct fdcan_standard_filter *lfssa;
+ struct fdcan_extended_filter *lfesa;
+
+ int can_id = FDCAN_BLOCK_ID(canport);
+
+ int lfsofs = 0;
+ int lfeofs = 0;
+ int lfssize = std_filt * sizeof(struct fdcan_standard_filter);
+ int lfesize = ext_filt * sizeof(struct fdcan_extended_filter);
+
+ if (can_id == 0) {
+ lfsofs = 0;
+ lfeofs = lfssize;
+ } else {
+ lfsofs = CAN_MSG_BASE + CAN_MSG_SIZE - lfssize;
+ lfeofs = lfsofs - lfesize;
+ }
+
+ fdcan_init_std_filter_ram(canport, lfsofs, 28);
+ fdcan_init_ext_filter_ram(canport, lfeofs, 8);
+
+ lfssa = fdcan_get_flssa_addr(canport);
+ lfesa = fdcan_get_flesa_addr(canport);
+
+ /* Only perform initialization of message RAM if there are
+ * any filters required
+ */
+ if (std_filt > 0) {
+ for (int q = 0; q < 28; ++q) {
+ lfssa[q].type_id1_conf_id2 = 0;
+ }
+ }
+
+ if (ext_filt > 0) {
+ for (int q = 0; q < 8; ++q) {
+ lfesa[q].conf_id1 = 0;
+ lfesa[q].type_id2 = 0;
+ }
+ }
+}
+
+/** Enable FDCAN operation after FDCAN block has been set up.
+ *
+ * This function will disable FDCAN configuration effectively
+ * allowing FDCAN to sync up with the bus. After calling this function
+ * it is not possible to reconfigure amount of filter rules, yet
+ * it is possible to configure rules themselves. FDCAN block operation
+ * state can be checked using @ref fdcan_get_init_state.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
+ * confirming that it left INIT mode. If set to 0, function will return
+ * immediately.
+ * @returns Operation error status. See @ref fdcan_error.
+ * @note If this function returns with timeout, it usually means that
+ * FDCAN_clk is not set up properly.
+ */
+int fdcan_start(uint32_t canport, uint32_t timeout)
+{
+ /* This block detects obviously invalid configuration of
+ * RX FIFOs and TX buffers. Such situation may happen
+ * if code originally targeted for STM32G4 is compiled for
+ * STM32H7. This code is not aware of H7 abilities and
+ * leaves this stuff unconfigured. We pick up here and
+ * assume, that the code wants the FDCAN to behave as if
+ * it was STM32G4. Therefore we will configure two three entry
+ * long RX FIFOs, three entry long TX event buffer and three
+ * entry long TX FIFO/queue here.
+ */
+ if (FDCAN_RXFIFO_OFFSET(canport, 0) == 0
+ && FDCAN_RXFIFO_OFFSET(canport, 1) == 0
+ && FDCAN_TXBUF_OFFSET(canport) == 0
+ && FDCAN_TXEVT_OFFSET(canport) == 0
+ && FDCAN_RXESC(canport) == 0
+ && FDCAN_TXESC(canport) == 0
+ ) {
+ /* These sizes are fixed based on what G4 contains. */
+ int fifo0_size = 3 * sizeof(struct fdcan_rx_fifo_element);
+ int fifo1_size = fifo0_size;
+ int txevt_size = 3 * sizeof(struct fdcan_tx_event_element);
+ int txbuf_size = 3 * sizeof(struct fdcan_tx_buffer_element);
+
+ fdcan_set_rx_element_size(canport, 0, 64, 64);
+ fdcan_set_tx_element_size(canport, 64);
+
+ /* At this point we simply assume that FLSSA and FLESA were
+ * set up by calling fdcan_init_filter(). It they weren't,
+ * there just won't be allocated any space for them.
+ * That's not a problem as after fdcan_start returns, it is
+ * not possible to configure filter amount anymore.
+ *
+ * Default approach is to configure CAN1 to occupy bottom
+ * of message RAM and CAN1 to occupy top of message RAM keeping
+ * large unused space in between. This ensures that even if someone
+ * starts playing with CAN1, "implicit" configuration of CAN2 done
+ * here won't break things magically.
+ */
+ if (FDCAN_BLOCK_ID(canport) == 0) {
+ /* CAN1 will use bottom-up layout with
+ * FLSSA < FLESA < F0SA < F1SA < TXESA < TXBSA
+ * Rx buffer is not used.
+ */
+ fdcan_init_fifo_ram(canport, 0,
+ FDCAN_LFESA_OFFSET(canport)
+ + FDCAN_LSE_COUNT(canport) * sizeof(struct fdcan_extended_filter),
+ 3);
+ fdcan_init_fifo_ram(canport, 1,
+ FDCAN_RXFIFO_OFFSET(canport, 0) + fifo0_size, 3);
+ fdcan_init_tx_event_ram(canport,
+ FDCAN_RXFIFO_OFFSET(canport, 1) + fifo1_size, 3);
+ fdcan_init_tx_buffer_ram(canport,
+ FDCAN_TXEVT_OFFSET(canport) + txevt_size, 3);
+ } else {
+ /* LFESA might be uninitialized. In such case
+ * we forge it's address at the end of MSG RAM
+ */
+ int lfesa_offs = FDCAN_LFESA_OFFSET(canport);
+ if (lfesa_offs == 0) {
+ lfesa_offs = CAN_MSG_SIZE;
+ }
+ /* CAN2 will use top-down layout with
+ * TXBSA < TXESA < F1SA < F0SA < FLESA < FLSSA
+ * Rx buffer is not used.
+ * This arrangement should ensure that even if
+ * CAN1 was configured manually, CAN2 default
+ * configuration should not break CAN1.
+ */
+ fdcan_init_fifo_ram(canport, 0, lfesa_offs - fifo0_size, 3);
+ fdcan_init_fifo_ram(canport, 1,
+ FDCAN_RXFIFO_OFFSET(canport, 0) - fifo1_size, 3);
+ fdcan_init_tx_event_ram(canport,
+ FDCAN_RXFIFO_OFFSET(canport, 1) - txevt_size, 3);
+ fdcan_init_tx_buffer_ram(canport,
+ FDCAN_TXEVT_OFFSET(canport) - txbuf_size, 3);
+ }
+
+ }
+ /* Error here usually means, that FDCAN_clk is not set up
+ * correctly, or at all. This usually can't be seen above
+ * when INIT is set to 1, because default value for INIT is
+ * 1 as long as one has FDCAN_pclk configured properly.
+ **/
+ if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
+ return FDCAN_E_TIMEOUT;
+ }
+
+ return FDCAN_E_OK;
+}
+
+/** Configure FDCAN FIFO lock mode
+ *
+ * This function allows to choose between locked and overewrite mode of FIFOs. In locked mode,
+ * whenever FIFO is full and new frame arrives, which would normally been stored into given
+ * FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO
+ * is rewritten by frame just received.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] locked true activates locked mode, false activates overwrite mode
+ */
+void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked)
+{
+ if (locked) {
+ FDCAN_RXF0C(canport) &= ~(FDCAN_RXF0C_F0OM);
+ FDCAN_RXF1C(canport) &= ~(FDCAN_RXF1C_F1OM);
+ } else {
+ FDCAN_RXF0C(canport) |= FDCAN_RXF0C_F0OM;
+ FDCAN_RXF1C(canport) |= FDCAN_RXF1C_F1OM;
+ }
+}
+