How Do You Configure the STM32 FDCAN Peripheral for CAN and CAN FD?
Last updated 3 July 2026 · 7 min read
Direct Answer
The STM32 FDCAN peripheral (based on the Bosch M_CAN IP, found on G0, G4, H7, L5, and U5 series) configures classic CAN and CAN FD through two separate concerns: bit timing and message RAM layout. Bit timing requires two register sets — FDCAN_NBTP for the nominal (arbitration-phase) bit rate used by every frame, and FDCAN_DBTP for the data-phase bit rate used only by CAN FD frames with the Bit Rate Switch (BRS) flag set. Message RAM is a shared SRAM region (not per-instance) that must be manually partitioned in software between Rx FIFOs, Rx buffers, Tx buffers, and filter elements — on parts with multiple FDCAN instances (STM32G4 has up to three), all instances share one message RAM block, and an unconfigured or overlapping partition is a common source of corrupted or missing frames. This is a materially different configuration model from the older bxCAN peripheral (F0/F1/F3/F4/L0/L1/L4 series), which has no CAN FD support and a simpler, per-instance mailbox structure.
Detailed Explanation
The FDCAN peripheral on newer STM32 families (G0, G4, H7, L5, U5) implements the Bosch M_CAN IP block, which is a genuinely different peripheral from the bxCAN block on older STM32 families (F0/F1/F3/F4/L0/L1/L4) — not just an extended version of it. For the CAN and CAN FD protocol fundamentals this peripheral implements — frame formats, arbitration, error confinement, and what CAN FD actually changes at the bus level — see What Is CAN Bus?. This page covers the STM32-specific configuration: bit timing, message RAM, and the practical gotchas that come from message RAM being a shared resource across multiple FDCAN instances.
bxCAN vs FDCAN: What Actually Changed
bxCAN uses a small, fixed set of hardware mailboxes (typically 3 transmit mailboxes and 2 receive FIFOs with a few filter banks) configured directly through peripheral registers, with no CAN FD support at all. FDCAN replaces this with:
- A shared Message RAM region — a block of SRAM (separate from the MCU's main SRAM) that holds Rx FIFO elements, Rx buffer elements, Tx buffer elements, Tx event FIFO elements, and filter elements. On parts with multiple FDCAN instances, all instances share the same physical message RAM block and must be partitioned by software.
- Separate nominal and data bit timing — because CAN FD's data phase runs at a different (faster) bit rate than the arbitration phase, FDCAN has two independent bit timing register sets rather than one.
- CAN FD frame support — up to 64-byte payloads and the Bit Rate Switch (BRS) mechanism, neither of which bxCAN can do at all.
Bit Timing: FDCAN_NBTP and FDCAN_DBTP
Every CAN frame's arbitration phase (the ID and arbitration process) runs at the nominal bit rate, configured in FDCAN_NBTP: nominal prescaler (NBRP), the two nominal time segments (NTSEG1, NTSEG2), and the nominal synchronisation jump width (NSJW). This is directly analogous to bxCAN's single bit timing register, and the same segment-based calculation method applies — segment lengths are chosen so the total bit time matches the target bit rate at the FDCAN kernel clock frequency, with a sample point typically placed at 75–87.5% of the bit time.
For CAN FD frames with the BRS flag set, the data phase — which carries the actual payload at a higher bit rate — uses a second, independent register set: FDCAN_DBTP (data prescaler DBRP, data segments DTSEG1/DTSEG2, data synchronisation jump width DSJW). A classic CAN frame, or a CAN FD frame without BRS, never uses the data bit timing at all — only frames explicitly requesting the rate switch do.
The FDCAN kernel clock is independently selectable in the RCC configuration (from the system clock, PLL "Q" output, or HSE, depending on family) — get this clock source and frequency right before calculating bit timing values, since every segment calculation is relative to it. STM32CubeMX's FDCAN bit timing calculator (given a target nominal and data bit rate) computes correct prescaler and segment values directly from the configured kernel clock, and is the most reliable way to avoid a hand-calculation error.
Message RAM Partitioning
Message RAM is where FDCAN differs most operationally from bxCAN. It is a fixed-size block of dedicated SRAM (not the MCU's general-purpose SRAM), and the peripheral does not partition it automatically — firmware must configure a set of start-address and element-count registers (FDCAN_RXF0C, FDCAN_RXF1C, FDCAN_RXBC, FDCAN_TXBC, FDCAN_TXEFC, and the filter configuration registers) that define where each structure lives within the block and how many elements it holds.
On parts with a single FDCAN instance (STM32G0), this is a straightforward one-time layout. On parts with multiple instances (STM32G4 has up to three FDCAN peripherals, STM32H7 has two), all instances share the same physical message RAM block — each instance's partition must be configured with non-overlapping address ranges, or one instance's Rx FIFO can silently corrupt another instance's filter table or Tx buffer. STM32CubeMX's FDCAN configuration tool handles this partitioning automatically when multiple instances are enabled in the same project, but a hand-written or partially-generated configuration must get every instance's addresses right, not just the one instance the developer is actively testing.
Rx Filtering and FIFOs
Unlike bxCAN's simpler filter banks, FDCAN's filtering is more explicit about default behaviour: HAL_FDCAN_ConfigGlobalFilter() must be called even to accept all frames, because the peripheral's default is to reject non-matching frames rather than accept them. A common first-bring-up symptom — the peripheral initialises without error, transmits correctly, but never receives anything — is very often a missing or misconfigured global filter, not a wiring or bit-timing problem.
Received frames land in Rx FIFO0, Rx FIFO1, or a dedicated Rx buffer depending on which filter element matched — Rx FIFO0 is the typical default target for general-purpose reception, with FIFO1 or dedicated buffers used when firmware needs to separate specific message IDs into their own reception path for priority or latency reasons.
Design Considerations
- Move the peripheral out of Initialization mode before expecting bus activity. FDCAN starts in Initialization mode (
FDCAN_CCCR.INITset) after reset and afterHAL_FDCAN_Init(); the peripheral will not transmit or receive on the bus untilHAL_FDCAN_Start()clears this. A device that appears to configure correctly but never participates on the bus at all is frequently still sitting in Initialization mode. - Verify the CAN transceiver is CAN FD-rated if using CAN FD. The FDCAN peripheral will happily generate CAN FD frames at whatever data-phase rate is configured; a transceiver that can't actually drive that rate produces bus errors that look like a peripheral configuration problem but are a hardware selection issue.
- Use CubeMX's bit timing calculator rather than hand-calculating segments, especially for the data-phase timing — the higher data-phase bit rate leaves much less margin for a segment-calculation error than the nominal phase does.
- Plan message RAM partitioning across all enabled FDCAN instances up front on multi-instance parts (G4, H7) — treat the shared message RAM block as one resource to budget across every instance in the project, not something each instance's configuration can be decided independently.
For CAN and CAN FD hardware and firmware design, including STM32 FDCAN configuration and multi-node bus integration, Zeus Design's firmware team develops production embedded systems on STM32 and other MCU platforms.
Common Mistakes
- Never clearing Initialization mode after configuration —
HAL_FDCAN_Start()is a separate, required step afterHAL_FDCAN_Init(); forgetting it leaves the peripheral fully configured but electrically inactive on the bus. - Omitting the global filter configuration — the FDCAN peripheral rejects all frames by default; without an explicit
HAL_FDCAN_ConfigGlobalFilter()call (even one that simply accepts everything), reception silently never happens. - Overlapping message RAM partitions across multiple FDCAN instances — on G4 and H7 parts, an incorrect or hand-edited message RAM address configuration on one instance can corrupt another instance's Rx FIFO or filter table, producing intermittent or instance-dependent frame corruption that looks like a bus-level or wiring fault.
- Using a classic-CAN-rated transceiver on a CAN FD data-phase design — the peripheral configuration will appear correct, but the higher data-phase bit rate exceeds what the transceiver can reliably drive, producing bus errors at the data phase specifically while arbitration-phase (classic CAN-equivalent) traffic works fine.
- Assuming bxCAN driver code or examples port directly to FDCAN — the register set, HAL API, and message RAM model are different enough that bxCAN-based reference code needs a genuine rewrite against the FDCAN API, not a search-and-replace of function names.
Frequently Asked Questions
- Which STM32 families have FDCAN, and which have the older bxCAN?
- FDCAN (with full CAN FD support) is on the STM32G0, G4, H7, L5, and U5 series. bxCAN (classic CAN 2.0 only, no CAN FD) is on the older F0, F1, F3, F4, L0, L1, and L4 series. There is no in-place upgrade path — moving a design from an F4 to a G4, for example, means rewriting the CAN driver code against the FDCAN peripheral's different register set and HAL API, not just changing a clock setting.
- Do I need a different CAN transceiver for CAN FD?
- Yes, in practice. A CAN FD bus needs a transceiver rated for the data-phase bit rate the design uses (commonly 2–5 Mbit/s, up to 8 Mbit/s), such as the TCAN1042 or similar CAN FD-capable parts. A transceiver designed only for classic CAN's 1 Mbit/s maximum will not reliably drive the faster data-phase edges, and a design mixing a classic-CAN-only transceiver with an FDCAN-capable microcontroller peripheral will not actually get CAN FD's throughput benefit even though the peripheral itself supports it.
- Can an STM32 FDCAN node talk to a classic CAN 2.0 bus?
- Yes — the FDCAN peripheral can be configured for Classic CAN frame format only, in which case it behaves like a bxCAN node and interoperates normally with a classic CAN 2.0 bus. It can also run in a mixed mode where it primarily uses CAN FD frames but can still receive classic CAN frames from legacy nodes, though every node on a CAN FD bus segment must at minimum tolerate CAN FD framing (a pure classic-CAN-only controller will flag CAN FD frames as errors), so mixed-protocol buses need careful node-by-node verification.
References
Related Questions
What Is CAN Bus?
CAN bus is a differential, multi-master serial bus where nodes arbitrate by message ID priority. Learn how frames, error confinement, and CAN FD work.
How Do You Configure STM32 HAL DMA for UART, SPI, and ADC?
Configure STM32 HAL DMA for UART, SPI, and ADC — normal vs circular mode, interrupt callbacks, double buffering, and cache coherency on STM32H7/F7.
How Does the STM32 Clock Tree Work?
The STM32 clock tree routes HSE or HSI through a PLL to generate SYSCLK, then divides it across AHB and APB buses. Learn how it works and how to configure it.
How Do You Configure STM32 Peripherals with HAL and CubeMX?
STM32CubeMX generates HAL initialisation code for UART, SPI, and I2C from a GUI. This guide explains key settings and how generated code maps to the hardware.
Which STM32 Family Should You Use?
Compare STM32 families for new designs: G0, G4, F4, H7, L4, U5, WB, and WL — performance tiers, power profiles, peripheral sets, and which to choose.
What Is RS-485?
RS-485 is a differential multi-drop bus for up to 32 nodes over ~1200 m cable runs. Learn how half-duplex wiring, termination, and Modbus RTU work.
Related Forum Discussions
STM32L4 never wakes from Stop mode — button EXTI interrupt just doesn't fire
Working on a battery-powered sensor node, STM32L476RG (Nucleo board for now). Idea is: device sits in Stop 2 mode most of the time, wakes wh
STM32F401 UART printing garbage after switching to 84 MHz PLL — same 115200 baud in CubeMX and PuTTY
Got a WeAct Black Pill (STM32F401CCU6) project that's been running happily on the default HSI clock at 16 MHz. Using USART1 on PA9/PA10 thro
STM32H743 HAL_UART_Receive_DMA fires error callback immediately — TEIF1 set, RxCplt never fires
Upgrading a project from STM32F4 to STM32H743. UART DMA receive worked on the F4 without any issues — standard CubeMX setup, call HAL_UART_R
STM32 GPIO interrupt configured but ISR never fires — what am I missing?
Trying to use a button on PA0 to trigger an interrupt on an STM32F411 Nucleo board. Using HAL, generated the init code with CubeMX. The GPIO