With the basics out of the way, it’s time to look at the actual hardware peripheral, as used in STM32 microcontrollers. It turns out the CAN hardware is almost identical across all STM32 families. Notable exceptions are that on the F103, the CAN memory buffers are shared with USB (which can’t be used at the same time), and that on the more recent µCs, new “CAN FD” (Flexible Datarate) capabilities have been added for higher speed.

STM32 overview

Here is a diagram from the F103 reference manual:

(I’ve omitted most of the second CAN controller details, available on some chip variants)

Two things to keep in mind is that CAN packets are very small (i.e. 11 or 29 address bits and up to 8 bytes of payload), and that all CAN bus traffic reaches every device.

For this reason, the hardware has a group of “mailboxes” (packet buffers, essentially): there are six to collect received packets and three to hold outgoing frames (to be sent when the bus rules, loading, and priorities allow it).

On the receiving end, multiple filters can be defined to select which incoming packets should be picked up. This can be by address range or by matching certain bits in the address - all non-matching packets are simply ignored. This way, transfers on the CAN bus which do not concern this device can take place without burdening it.

All received data is routed to one of two FIFOs, allowing “urgent” data to sidestep the normal processing flow, if you need this capability.

The CAN bus controller can be used in polled mode or in interrupt mode. There is no DMA mode. Then again, if you look at it another way: all transfers are always done via DMA, since they end up in a dedicated CAN memory area. It makes no difference in throughput: all the accepted packets are saved until the application code copies and processes them.

Controller logic

The CAN bus controller hardware is part of several STM32 families, including the F103 and the F407. In the low-end F103 range, it shares memory with the USB hardware, which means that USB and CAN cannot be enabled at the same time. This is somewhat unfortunate, since one of the things I’d like to do is create a bridge between the two.

Luckily, the F407 series µCs do support both. In fact, these chips tend to have multiple CAN bus controllers. With a single controller, at most 14 filters can be supported, with two this number increases to 28. This allows a very fine-grained setup, although it really only matters if you’re interested in ignoring some of the traffic on the CAN bus. In the simple case, one filter which accepts everything is fine. The application code can then decide what it wants to do with each received packet.

The CAN bus hardware is very easy to use, because it takes care of everything. No periodic attention is needed to keep it going, no matter how busy the bus is. Evidently, to process incoming packets, you do need to pick them up, and to send out packets, you do have to copy then into one of the empty mailboxes. But that’s about it.

All bit synchronisation, start bit detection, bit timing, bit stuffing, address arbitration, CRC checking, retrransmission, and acknwledgements are managed in hardware. In essence, this makes the CAN bus an all-or-nothing proposition: either everything works, or nothing works at all (if the wiring is wrong, for example).


Each packet on the CAN bus has an 11- or 29-bit address (they can be mixed as needed). This is not a destination address, it’s more like a tag, identifying the message contents. This is a key property of message flow on the CAN bus, and bears repeating: you’re not sending a message to a certain node, you’re only placing a message on the bus which some other node(s) on the bus will recognise and process.

Addressing is very organic - much like a blood stream: all sorts of stuff moves around, and in certain places, some recipient(s) will pick it up and act on it. Each packet can act as a signal to trigger something, or contain an actual payload with information, or both.

The node which picks up the message will check its CRC and reject it if it fails, leading to a retransmission. If the CRC is correct, the packet will be acknowledged (there’s a bit in the protocol for that), letting the sender know that at least one node on the bus accepted the packet. Given the logic AND nature of the bus, a single acknowledgement is enough.

The acknowledgement is essential: it tells the sender that all is well and that the sender’s corresponding mailbox may be released for other uses.

If there is no receiver at all, there will never be an acknowledgement. This will lead to a burst of retransmissions on the bus (fully prioritised, as always). The hardware keeps track of the retransmit count, and eventually cancels the transmission in the sender. There’s a configuration setting to let the sender retry again, after a long idle time on the bus - this is very convenient as all the usual cases are handled very gracefully: a brief bus disconnect, powering up nodes in random order, resets, and other temporary bus failures.