Embedded firmware

Communication Protocols on STM32 (Part 4): CAN and FDCAN – frames, arbitration and bit timing

CAN and CAN FD on STM32 (FDCAN), from the frame format, ID-based arbitration, bit timing calculation, frame time and bus load to HAL configuration, receive filters and bus-off recovery. Part 4 of the Communication Protocols on STM32 series.

14 min read
Communication Protocols on STM32 (Part 4): CAN and FDCAN – frames, arbitration and bit timing

Note: the diagrams in this article were drawn by Capitlab for illustration. Table values are calculated from formulas; the "Measured" column will be filled in once measurements are made on hardware. The code was written from the HAL documentation and datasheets and has not yet been run on hardware, so verify it before using it in a product.

CAN (Controller Area Network) was created for cars and is now found in industrial machines, robots and medical equipment. Differential signalling makes it robust against noise, the hardware detects errors and retransmits on its own, and many peer nodes share a single pair of wires.

STM32 has two kinds of CAN peripheral. Newer families (G0, G4, H5, H7, U5...) have FDCAN, which handles both classic CAN and CAN FD. Older ones such as the F1 and F4 have bxCAN, with a different set of HAL functions (HAL_CAN_*). This part is written for FDCAN.

A few things worth knowing up front. CAN has no node addresses: the ID describes the content of the message and sets its priority. The MCU only has logic-level TX/RX pins, so you need a transceiver plus two 120 Ω resistors at the ends of the bus. A node on its own can never complete a transmission because nobody sends the ACK, so when testing a single board use loopback mode.

1. How it works

1.1. Physical layer

CANH and CANL are twisted together, and the information is the voltage difference between them. The bus has two states: dominant (logic 0) and recessive (logic 1). When several nodes transmit at once, dominant always beats recessive. The lossless arbitration in section 1.3 relies on this.

CAN network: CANH/CANL pair, two 120 ohm resistors at the ends and three nodes, each with an MCU and transceiver

Figure 1. A typical CAN bus. Each node has an MCU and a transceiver (TJA1051, MCP2562FD...). The two 120 Ω resistors sit at the two ends of the bus. Labels in the figure are in Vietnamese.

With power off, the resistance between CANH and CANL should be about 60 Ω (two 120 Ω resistors in parallel). 120 Ω means one resistor is missing, a very high value means both are missing, and a very low value means a short or extra terminators. When the bus misbehaves, take this measurement first; it only needs a multimeter.

1.2. The classic CAN frame

Standard CAN frame: SOF, 11-bit ID, RTR, IDE, r0, DLC, 64 data bits, 15-bit CRC, ACK, EOF

Figure 2. A standard CAN frame with an 11-bit ID and 8 data bytes: 108 bits (before stuffing), plus 3 bits of interframe space.

The ID is 11 bits in a standard frame and 29 bits in an extended frame. The 4-bit DLC gives the number of data bytes, from 0 to 8. A 15-bit CRC protects the frame, and the receiving nodes pull the ACK bit low to confirm reception.

After 5 identical bits in a row, the hardware inserts one bit of the opposite level so that the nodes stay synchronised (bit stuffing). The real length of a frame on the bus therefore depends on its content. Classic CAN runs at up to 1 Mbit/s.

1.3. Arbitration

When the bus is idle, any node may start transmitting. If two nodes start together, each one transmits and reads the bus back at the same time. A node that sends a recessive bit but reads dominant knows a higher-priority frame is on the bus, so it stops transmitting and switches to receiving. The winner's frame continues untouched.

Nodes A, B, C transmitting IDs 0x123, 0x120, 0x1A0 at once: C loses at ID7, A loses at ID1, B wins

Figure 3. Three nodes transmitting together: C loses at bit ID7 (sends 1 but the bus is 0), A loses at bit ID1, and B with the lowest ID (0x120) wins.

This dictates how IDs are assigned: important messages (safety, real-time control) get low IDs, less urgent ones get high IDs. Never let two nodes send the same ID with different content.

1.4. Error detection and node states

CAN checks for errors at several levels: bit errors (comparing the sent bit with the read-back bit), stuff errors, CRC errors, form errors and ACK errors. Each node keeps two error counters, TEC for transmit and REC for receive, and is in one of three states:

State Condition Behaviour
Error Active TEC and REC both at most 127 Normal; signals errors with a dominant error flag
Error Passive TEC or REC above 127 Signals errors with a recessive flag, must wait longer after transmitting
Bus-Off TEC above 255 Disconnected from the bus, neither sends nor receives

A node with a hardware fault (wrong bit rate, broken wire) drives its TEC up quickly and drops into bus-off on its own, so it cannot take the whole bus down. To come back, it has to observe 128 sequences of 11 consecutive recessive bits on the bus.

1.5. CAN FD

CAN FD (Flexible Data-rate) carries up to 64 data bytes and can switch to a higher rate during the data phase (Bit Rate Switch, BRS). Arbitration still runs at the nominal rate, so CAN FD has two sets of bit timing parameters: Nominal and Data. The CRC is longer (17 or 21 bits), and the payload length can only be 0 to 8, 12, 16, 20, 24, 32, 48 or 64 bytes.

2. Bit timing

Each CAN bit is split into segments measured in time quanta (tq). The time quantum is the smallest time unit, obtained by dividing the peripheral clock by a prescaler:

Bit time = 1 tq (Sync) + TimeSeg1 + TimeSeg2

Bitrate = fdcan_ker_clk / (Prescaler × (1 + TimeSeg1 + TimeSeg2))

Sample point = (1 + TimeSeg1) / (1 + TimeSeg1 + TimeSeg2).

One CAN bit of 16 time quanta: Sync 1 tq, TimeSeg1 13 tq, TimeSeg2 2 tq, sample point at 87.5%

Figure 4. Example with an 80 MHz clock and prescaler 5: tq = 62.5 ns, 16 tq per bit gives 1 Mbit/s with the sample point at 87.5%.

Calculated for an 80 MHz peripheral clock, 16 tq per bit, TimeSeg1 = 13, TimeSeg2 = 2:

Bit rate Prescaler tq Sample point
125 kbit/s 40 500 ns 87.5%
250 kbit/s 20 250 ns 87.5%
500 kbit/s 10 125 ns 87.5%
1 Mbit/s 5 62.5 ns 87.5%

The remaining parameter is SJW (Synchronization Jump Width), the maximum correction applied at each resynchronisation. It is usually 1 to 4 tq and must not exceed TimeSeg2. Every node on the bus must use the same bit rate, and the sample point should sit somewhere between 80 and 87.5%.

For an accurate bit rate, take the clock from a crystal (HSE), not an internal oscillator. Many Nucleo boards have no HSE crystal and take their clock from the ST-LINK instead, so check the actual configuration. Choose an FDCAN clock that is a multiple of the bit rate so the number of tq per bit is an integer.

For CAN FD the transceiver must handle the data-phase rate (the MCP2562FD goes up to 8 Mbit/s). The cable must be twisted pair, terminated with 120 Ω at both ends, with stubs kept as short as possible.

ST's documentation has a reference example on the STM32H753 with a 120 MHz FDCAN clock and prescaler 1. The 1 Mbit/s nominal phase uses 120 tq per bit, NominalTimeSeg1 = 101, NominalTimeSeg2 = 18, giving a sample point of about 85%. The 8 Mbit/s data phase uses 15 tq per bit, DataTimeSeg1 = 8, DataTimeSeg2 = 6, giving about 60%.

3. Frame time and bus load

A standard frame has 47 + 8n bits, where n is the number of data bytes, including the 3 interframe bits but not stuff bits. In the worst case stuffing adds up to ⌊(34 + 8n − 1) / 4⌋ bits. Calculated values:

Data bytes Bits (no stuffing) Bits (worst case) Time at 500 kbit/s Time at 1 Mbit/s Max frames/s at 500 kbit/s
0 47 55 94 to 110 µs 47 to 55 µs 9,090 to 10,638
1 55 65 110 to 130 µs 55 to 65 µs 7,692 to 9,090
2 63 75 126 to 150 µs 63 to 75 µs 6,666 to 7,936
4 79 95 158 to 190 µs 79 to 95 µs 5,263 to 6,329
8 111 135 222 to 270 µs 111 to 135 µs 3,703 to 4,504

Use the table to estimate bus load. For example, ten 8-byte frames every 10 ms, i.e. 1000 frames/s, at 500 kbit/s take 1000 × 111 / 500,000 = 22% to 27% of the bandwidth. In automotive work the bus load is usually kept below roughly 30 to 50% to leave room for low-priority frames and error recovery.

4. Configuration in STM32CubeIDE

Setting Value Note
Clock Divider Per clock tree FDCAN clock from PLL or HSE, see Clock Configuration
Frame Format Classic Mode, FD no BRS or FD with BRS
Mode Normal (or Internal Loopback for testing)
Auto Retransmission Enable Retransmit after lost arbitration or errors
Nominal Prescaler / Time Seg1 / Time Seg2 / SJW From the table in section 2
Data Prescaler / Time Seg1 / Time Seg2 Only for FD with BRS
Std Filters Nbr Number of 11-bit ID filters needed
Tx FIFO Queue Elements e.g. 8
NVIC FDCAN1 interrupt 0 Enable for interrupt-driven reception

5. Source code

5.1. Initialisation and receive filter

CubeMX generates MX_FDCAN1_Init. What you add is the filter, the interrupts and the start call:

FDCAN_FilterTypeDef f = {0};
f.IdType       = FDCAN_STANDARD_ID;
f.FilterIndex  = 0;
f.FilterType   = FDCAN_FILTER_MASK;
f.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
f.FilterID1    = 0x120;
f.FilterID2    = 0x7F0;               // mask: accept IDs 0x120 to 0x12F
HAL_FDCAN_ConfigFilter(&hfdcan1, &f);

// Frames matching no filter: reject
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT,
                             FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE);

HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
HAL_FDCAN_Start(&hfdcan1);

In FDCAN_FILTER_MASK mode, every bit set to 1 in the mask must match the corresponding bit of FilterID1 in the received ID. A mask of 0 (FilterID1 = 0, FilterID2 = 0) accepts every ID. A mask of 0x7FF accepts exactly one ID.

5.2. Sending a frame

FDCAN_TxHeaderTypeDef tx = {0};
uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8};

tx.Identifier          = 0x123;
tx.IdType              = FDCAN_STANDARD_ID;
tx.TxFrameType         = FDCAN_DATA_FRAME;
tx.DataLength          = FDCAN_DLC_BYTES_8;
tx.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
tx.BitRateSwitch       = FDCAN_BRS_OFF;
tx.FDFormat            = FDCAN_CLASSIC_CAN;
tx.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
tx.MessageMarker       = 0;

if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &tx, data) != HAL_OK) {
    // FIFO full or peripheral not started
}

This call only places the frame in the queue; arbitration and transmission are handled by the peripheral. To know when the frame has gone out, enable FDCAN_IT_TX_COMPLETE.

5.3. Receiving in the callback

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *h, uint32_t it)
{
    if (it & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) {
        FDCAN_RxHeaderTypeDef rx;
        uint8_t d[64];
        while (HAL_FDCAN_GetRxFifoFillLevel(h, FDCAN_RX_FIFO0) > 0) {
            if (HAL_FDCAN_GetRxMessage(h, FDCAN_RX_FIFO0, &rx, d) != HAL_OK) break;
            // rx.Identifier is the ID, rx.DataLength is the DLC code (not a byte count)
            // DLC 0..8 means 0..8 bytes, DLC 9..15 means 12, 16, 20, 24, 32, 48, 64 bytes
        }
    }
}

DataLength in HAL is the code of an FDCAN_DLC_BYTES_n constant, not a byte count. Compare against those constants, or use a conversion table that matches your HAL version.

5.4. High-speed CAN FD

To use CAN FD with BRS, set Frame Format to "FD mode with BRS", fill in Data Prescaler and Data Time Seg, and change the header when sending:

tx.FDFormat      = FDCAN_FD_CAN;
tx.BitRateSwitch = FDCAN_BRS_ON;
tx.DataLength    = FDCAN_DLC_BYTES_64;

The element sizes in FDCAN message RAM (RxBufferSize, TxElmtSize) must also be large enough for 64 bytes; otherwise long frames are truncated or rejected.

5.5. Error monitoring and bus-off recovery

void can_check_bus(void)
{
    FDCAN_ProtocolStatusTypeDef ps;
    FDCAN_ErrorCountersTypeDef  ec;
    HAL_FDCAN_GetProtocolStatus(&hfdcan1, &ps);
    HAL_FDCAN_GetErrorCounters(&hfdcan1, &ec);

    if (ps.BusOff) {
        // The peripheral has stopped. Call Start to begin recovery
        // (waits for 128 sequences of 11 recessive bits on the bus).
        HAL_FDCAN_Start(&hfdcan1);
    }
    // ec.TxErrorCnt, ec.RxErrorCnt: watch these for early warning as errors rise
    // ps.ErrorPassive, ps.Warning: error state flags
}

Call this periodically, every 100 ms for instance, or from HAL_FDCAN_ErrorStatusCallback. Count how many times the node has gone bus-off and store the number; it is very useful when diagnosing problems in the field.

Without a second node, set Mode = FDCAN_MODE_INTERNAL_LOOPBACK (or EXTERNAL_LOOPBACK) to test the send, receive and filter code on a single board.

6. Common faults

Symptom Usual cause Fix
Transmission never completes, error counters rise Only one node on the bus (no ACK) or bit rate mismatch between nodes Add a second node, check the bit rate, or use Loopback
Repeated bus-off Wrong bit rate, missing 120 Ω terminator, CANH/CANL swapped, transceiver unpowered Measure the bus resistance (60 Ω), check wiring and transceiver supply
Fine at 500 kbit/s, fails at 1 Mbit/s or FD Inaccurate clock, poor sample point, long cable, long stubs Use HSE, adjust Seg1/Seg2, shorten cable and stubs
Nothing received Filter rejects the frames, interrupt or HAL_FDCAN_Start missing Check the filter, global filter and NVIC
Frames lost under high load Receive FIFO full because processing is slow Larger FIFO, keep the callback short, use an RTOS queue
Wrong data length with CAN FD DLC code confused with byte count, element size too small Use the DLC table, configure 64-byte elements
HAL FDCAN on an F4 chip F1 and F4 have bxCAN, not FDCAN Use HAL_CAN_* for bxCAN

7. Designing a CAN network

Run the bus as one line with a terminator at each end and short stubs to the nodes. Avoid star wiring. Use twisted-pair cable with a characteristic impedance of about 120 Ω, and connect the node grounds through a third wire or the enclosure if needed.

The higher the bit rate, the shorter the bus must be. Commonly quoted rule-of-thumb figures are about 40 m at 1 Mbit/s, 100 m at 500 kbit/s and 500 m at 125 kbit/s, but always verify them with your actual cable and environment.

In industrial environments, TVS diodes and a common-mode choke help against interference. For CAN FD on long buses or at high rates, choose a transceiver with SIC (signal improvement capability).

8. Measurement plan

The "Measured" column stays empty until the code runs on hardware.

Test Method Calculated Measured
Bus resistance with power off Multimeter between CANH and CANL About 60 Ω (to be filled)
Bit rate 500 kbit/s Oscilloscope, measure bit width 2 µs per bit (to be filled)
Actual sample point Oscilloscope or logic analyzer with CAN decoding 87.5% as configured (to be filled)
Duration of an 8-byte frame at 500 kbit/s Logic analyzer, SOF to end of EOF 222 to 270 µs (to be filled)
Maximum sustainable bus load Send continuously, count frames at the other node 3,703 frames/s (worst case) (to be filled)
CAN FD: highest stable data-phase rate Step through 2, 4, 5, 8 Mbit/s, record errors Depends on transceiver and cable (to be filled)
Priority arbitration Two nodes send different IDs at once, see which wins Lower ID wins (to be filled)
Bus-off recovery time Inject a fault on purpose, time the recovery Depends on configuration and bus load (to be filled)

9. FAQ

Do CAN nodes need a shared clock? No. Each node has its own clock and resynchronises on the falling edge at the start of a frame and on stuff bits. That is why the clock must be accurate (crystal based), so that accumulated drift stays within SJW.

What is the difference between CAN 2.0A and 2.0B? 2.0A uses 11-bit IDs. 2.0B supports both 11-bit and 29-bit IDs. Both kinds can run on the same bus.

Can CAN FD coexist with classic CAN? A classic-only node will flag an error when it sees an FD frame. Use one type across the whole bus, or run the FD nodes in classic mode.

Can I skip the transceiver? No. The MCU only outputs logic levels. The transceiver produces the correct dominant and recessive voltages on the differential pair, tolerates noise and protects the MCU pins.

Summary

When the bus fails, measure the resistance between CANH and CANL before touching the software. Calculate the bit timing so the sample point lands around 80 to 87.5%, with a crystal clock. Watch the error counters and handle bus-off in the firmware.

Part 5 moves on to LIN: a cheap single-wire bus built on UART, widely used for auxiliary devices in cars, with its Break field, PID and checksum.

To decode CAN payloads quickly, try the Capitlab Hex Converter.

References