STM32 CAN Interface

STM32 CAN Interface


11 minute read

Table of Contents

Considering the growing interest in the STM32 CAN Peripheral in embedded applications, this article aims to explain the main features of the protocol and provide comprehensive information on the topic. First, we cover the theory and understand the main concepts underlying the interface. Then, we will learn how to configure and utilize the STM32 CAN Interface in STM32 MCUs. Specifically, we will work on the STM32 CAN Flexible Data (FD CAN) Peripheral on the STM32G491 MCU. Even if you use different hardware, all the principles and general rules remain unchanged. We also focus on more advanced topics, such as error handling, filtering, and debugging in the STM32 CAN Interface.

Yet another Interface?

In embedded applications, we have various interfaces, including I2C, USB, SPI, and others. If so, why do we need another communication protocol that we call CAN? To make our lives difficult? To answer this question, let's look at the illustration below. It depicts the classical view of the interfaces. There is a master who can communicate with slaves, but the obvious problem here is that slaves cannot communicate with each other. In a modern car, various components need to exchange information, including the throttle sensor, ABS brakes, and dashboard. If we used something like SPI or I²C, all the data would have to pass through a single central computer, which creates a bottleneck.

STM32 CAN Theory

With CAN, on the other hand, every part can send its data directly on the bus, and any other part can read it. For example, the ABS can send wheel speed information, the dashboard can display it, and the airbag system can also utilize it — all without requiring the main engine computer's prior approval. The primary principle of the CAN interface is to design a bus that allows all components to exchange data with one another.

There is another crucial advantage of the CAN over other traditional interfaces that I personally experienced. In one project, I connected an IMU sensor to a microcontroller using a cable. On random occasions, the communication kept failing, and I spent tons of time finding a 'bug' in my code. It took me an eternity to realize that the problem is not my code but the I2C interface: it works 'shity' with cables. CAN introduces positive and negative pairs so it is robust to external disturbances. So, you can use long cables without being afraid of data loss.

To sum up, we can list the following advantages of the CAN Interface:

  • Multi-master communication → any node can initiate transmission, no central master required.

  • Efficient arbitration → lowest identifier wins without data loss, ensuring priority for critical messages.

  • Robust error handling → automatic error detection, retransmission, and fault confinement.

  • Reliable in noisy environments → Differential signaling (CAN_H, CAN_L) makes it resistant to EMI, making it ideal for cars and industry.

  • Scalability → easy to add or remove nodes without redesigning the whole system.

  • Broadcast nature → multiple nodes can receive one message at the same time.

  • Deterministic behavior → high-priority messages are guaranteed to get through quickly.

  • Standardized → widely used, with strong ecosystem and hardware support (especially in STM32).

  • Cost-effective wiring → only two wires needed for many nodes, reducing complexity compared to point-to-point links.

  • Backward compatibility → Classical CAN and CAN FD controllers can share the same bus (FD nodes can fall back to Classical).

If you don't understand some of the technical words in the list, don't worry. We will slowly dive into the intricacies of the CAN, and eventually, you will master it.

CAN Frame Format

So, how does CAN allow multiple nodes to use the same bus without data collision? Arbitration, an ingenious concept in CAN, plays a crucial role in multi-node communication. To understand it, let's literally start from bits. In CAN, we call bit 0 dominant while bit 1 is recessive. If a node sets the data bus to 0 while the other node sets it to 1, the final level of the data line would be 0! Even if 100500 nodes set the data line to 1 and a single node to 0, the outcome will be 0. It means that zero always wins over one. For the same reason, we call bit 0 dominant and bit 1 recessive.

During the arbitration, nodes convey the messages' ID, and they constantly monitor the data line. If a node transmits one and but the data line remains zero, it means that the node is out of the game and can stop transmitting. In other words, one has the data with higher priority. Therefore, the lower the message ID, the higher the priority it has. Example:

* Node A wants to send message ID = 0x100 (binary: 0001 0000 0000).

* Node B wants to send message ID = 0x080 (binary: 0000 1000 0000).

* Both start transmitting simultaneously. Bit by bit, the bus resolves to whichever is dominant.

* At the 4th bit, Node A sends a 1 (recessive) but receives a 0 (dominant) from Node B.

* Node A realizes it lost arbitration and stops. Node B’s frame goes through without error.

In this manner, all the nodes in the bus have the right to transmit data. The arbitration process defines the priority of messages and avoids data collisions. In addition, a node can broadcast data to other nodes.

Let's dive into the data format in CAN. The figure below demonstrates the frame format of the classical CAN Network. First, any frame begins with a Start of Frame (SOF) bit. Its value is always dominant (zero). The 11-bit ID follows it, marking the arbitration process. Using the scheme explained, the node with the lowest ID wins the arbitration. There is an additional bit in the arbitration field that we call Remote Transmission Request (RTR). It tells whether the frame contains actual data or is simply a request for data. If a node requests data while the other is transmitting the same data, the node that is transmitting will win the arbitration:

  1. RTR = 0 (dominant) → Data Frame

The frame carries a data payload (up to 8 bytes in Classical CAN).

  1. RTR = 1 (recessive) → Remote Frame

The frame does not contain data. Instead, it requests that another node with the same identifier send back the corresponding Data Frame.

After the RTR bit, we have the IDE (Identifier Extension) bit. We'll discuss that later, but for now, please keep in mind that its value is always dominant in the classical CAN.

Next comes the reserved bit (r0), which is always dominant in Classical CAN.

Following that, we reach the Data Length Code (DLC), a 4-bit field that specifies how many data bytes are in the frame (from 0 up to 8 in Classical CAN).

Then comes the Data Field, which contains the actual payload — sensor readings, control values, or status information. The DLC determines its length.

After the data, the CRC (Cyclic Redundancy Check) field is added. This ensures that receivers can detect transmission errors.

Next is the ACK(Acknowledgment) field. When a node successfully receives the frame and confirms no errors, it overwrites a recessive bit with a dominant one to acknowledge the sender.

Finally, the frame ends with the End of Frame (EOF), which consists of seven recessive bits, marking the precise boundary of the message.

In total, we have:

  • SOF: Start of frame (0, 1 bit)
  • Identifier: 11-bit identifier for arbitration
  • RTR: Remote Transmission Request, 0 - data frame, 1 - data request
  • IDE: Identifier Extension, 0 - Standard, 1 - Extended
  • R0: reserved
  • DLC: Data Length Code, 3 bits
  • DATA: Actual Data, 0-8 bytes
  • CRC : Cyclic Redundancy Check
  • ACK : Acknowledgment
  • EOF: End Of frame, 7 bits
  • IFS: Interframe Space, 3 bits

STM32 Classical CAN Format

Extended CAN


In classical CAN, the identifier has an 11-bit length, allowing up to 2048 identifiers within a bus. It seems like it wasn't enough, though it's hard to imagine more than 2048 message types on the same bus. Hence, a new version of CAN, Extended CAN, has been introduced. It is almost identical to the classical CAN but has some notable differences:
  • SRR or RRS: Substitute Remote Request. You remember the RTR bit that defines whether the frame contains data or just a request. In Extended CAN, there is no data request. Instead, the nodes broadcast the data, and other nodes listen. So this new bit substitutes the RTR bit, and it is always dominant (0)
  • IDE: Although this bit exists in the Classical CAN, it requires some attention. The extended CAN is compatible with the Classical one. So, both types can coexist on the same bus. The IDE bit determines whether the frame is using the Standard (11-bit) or Extended (29-bit) identifier format.
  • IDE = 0 (dominant) → Standard frame (Classical CAN, 11-bit ID).
  • IDE = 1 (recessive) → Extended frame (29-bit ID).

Because of the way CAN arbitration works (dominant always wins over recessive), Standard frames have priority over Extended frames if both use the same 11-bit base identifier.

  • Identifier 2: extra 18 bits to allow more possible identifiers,
  • R1: Reserved (1 bit)

STM32 Extended CAN Format

Flexible Data-Rate (FD) CAN


Classical CAN worked great for a long time, but as machines became smarter, some problems showed up:
  • Small payload → only 8 bytes max per frame. Modern applications (ADAS, radar, lidar, infotainment, over-the-air updates) often need to send much more data.

  • Fixed bitrate → the arbitration and the data use the same speed, typically 500 kbps or 1 Mbps. This became too slow for high-bandwidth systems.

  • Higher software overhead → if you need to send a 64-byte block, you have to break it into at least 8 separate CAN messages, adding delay and CPU load.

Bosch introduced an upgrade to CAN that we call a Flexible Data-Rate (FD) CAN. It brings higher bandwidth and better efficiency:

  • Larger payload → up to 64 bytes per frame.
  • Bit rate switching (BRS) → arbitration still happens at a slower rate, but the data field can be sent at a much higher speed (e.g., 2–8 Mbps).

The figure below illustrates the frame format in the FD CAN. I skipped the details of the arbitration because it still can be an 11-bit (Classical) or a 29-bit (Extended) identifier. It contains additional fields:

  • FDF: Flexible Data Format, always recessive (1) in CAN FD Frame.
  • BRS: Bit Rate Switch, 0 (dominant) - same bitrate, 1(recessive) - faster data rate in data fields
  • ESI: Error state Indicator, 0 - error passive, 1 - error active

STM32 FD CAN Frame Format


In this series of tutorials, we will focus on this version of the STM32 CAN Peripheral: FD CAN.

STM32 CAN Filtering


Another extremely crucial element of the STM32 CAN protocol is frame filtering. On a typical bus, there may be dozens or even hundreds of nodes exchanging data at the same time. If every single frame had to be received and processed by each node, it would place a massive burden on the microcontroller. Even powerful processors would waste cycles handling irrelevant data.

In reality, most nodes only need a small subset of the total traffic. For instance, a controller responsible for the lighting system in a car does not need to concern itself with frames related to engine management or braking systems. To address this, STM32 CAN controllers implement acceptance filters.

These filters examine the identifier field of each incoming frame. If the identifier matches the criteria defined by the filter, the frame is accepted and passed on to the host processor. If not, the controller discards the frame silently. In this way, the processor only receives the data it truly needs, dramatically reducing workload and improving system efficiency.

In STM32 MCUs, three types of filtering techniques are implemented:

  • STM32 CAN Range Filters. You can define a range of IDs to be accepted. For example, accept frames whose IDs lie within 12-28
  • ID of the received frame has to be a specific value.
  • Classical Filter. The process is performed in two stages. First, the acceptance mask defines which bits of the incoming identifier are relevant and which can be ignored. Bits set to 0 in the mask are treated as “don’t care,” meaning their value does not affect the comparison.

In the second stage, the remaining “active” bits of the identifier are compared against the acceptance code. If they match, the frame is accepted by the STM32 CAN controller and passed on to the host processor. Otherwise, the frame is discarded.

This two-stage mechanism allows very flexible filtering. A filter can be configured to accept only one specific identifier, or an entire group of identifiers that share a common prefix. For example, by masking the last four bits of the identifier, a node could subscribe to 16 consecutive identifiers at once, while ignoring everything else on the bus.

Lastly, multiple filters can be combined to create a more complex filtering scheme. Each filter entry can cover a different range of identifiers or target specific message groups. By cascading several filters, the STM32 CAN Interface can precisely tailor which messages it accepts, even in a very crowded network. For example, one filter could capture engine-related frames, another could handle diagnostic messages, while a third might accept only safety-critical signals. This modular approach ensures that each node receives exactly the data it needs — no more and no less — while keeping processor workload as low as possible.


« Back to Blog