1. Topic

CAN/FDCAN/TWAI: bit timing, sample point, arbitration, termination, transceivers, filters, error counters and bus-off recovery. Main idea: CAN is not UART; it is a multi-master bus with arbitration, errors, ID priorities and strict physical requirements.

2. Why this matters in a project

CAN is useful for controller-to-controller communication, industrial modules, HIL fault scenarios, ESP32/STM32/Jetson connections, diagnostics and short phase_event, fault_event and heartbeat messages.

3. Theory

A CAN node:

text
MCU CAN/FDCAN/TWAI controller
  TX/RX logic-level
  -> CAN transceiver
  -> CAN_H / CAN_L differential bus

Do not connect CAN_TX/RX directly to CAN_H/CAN_L. ID encodes arbitration priority: a lower ID has higher priority.

text
0x080..0x0FF - fault/safety
0x100..0x1FF - phase/sync
0x200..0x2FF - commands
0x300..0x4FF - telemetry
0x600..0x6FF - diagnostics
0x700..0x70F - heartbeat

Bit timing requires a CAN kernel clock, prescaler, time quanta, TimeSeg1/2, SJW and sample point. Loopback does not test the transceiver, cable, termination, ground or sample point under real conditions. Termination: two 120 Ohm resistors at the ends. With power off, expect about 60 Ohm between CAN_H and CAN_L. Bus-off recovery:

text
bus-off detected
  -> stop non-critical TX
  -> save diagnostics
  -> backoff
  -> restart CAN controller
  -> repeated bus-off -> degraded mode

Filters prevent the application from processing all bus traffic. Architecture:

text
can_driver -> can_service -> can_protocol -> application events

4. Common mistakes

  1. Connecting CAN_TX/RX directly to CAN_H/L.
  2. Testing only loopback and assuming the bus is ready.
  3. Using an inaccurate clock on a real CAN network.
  4. Adding termination to every module.
  5. Failing to design IDs as priorities.
  6. Omitting filters.
  7. Rebooting the MCU on bus-off instead of controlled recovery.

5. Practical task

Create CAN_FDCAN_POLICY.md:

markdown
# CAN / FDCAN policy
Role:
CAN is used as industrial event and diagnostics bus.
Physical layer:
- external CAN transceiver is mandatory;
- 120 Ohm termination only at both ends;
- expected bus resistance off-power: ~60 Ohm;
- TVS near connector;
- ground/reference or isolation is defined;
- stub length is minimized.

Diagnostics:

c
typedef enum {
    CAN_BUS_STATE_STOPPED = 0,
    CAN_BUS_STATE_ACTIVE,
    CAN_BUS_STATE_ERROR_PASSIVE,
    CAN_BUS_STATE_BUS_OFF,
    CAN_BUS_STATE_RECOVERING,
    CAN_BUS_STATE_DEGRADED,
} can_bus_state_t;
typedef struct {
    can_bus_state_t state;
    uint32_t tx_ok, tx_fail, rx_ok, rx_dropped;
    uint32_t rx_fifo_overrun, tx_queue_full;
    uint32_t bus_error_count, error_passive_count;
    uint32_t bus_off_count, recovery_count;
    int64_t last_rx_us, last_tx_us, last_bus_off_us;
} can_diag_t;

6. Further reading and experiments

STM32 HAL FDCAN, ST AN5348, ST AN5878, Vector CAN physical-layer notes and ESP-IDF TWAI.

What does internal CAN loopback prove?

Exercise

An unpowered simple CAN bus has two 120 Ohm terminations and no other resistance paths between CAN_H/CAN_L. What should an ohmmeter read? What if a third 120 Ohm termination is added?

Self-check criteria: Show the parallel connection, 60/40 Ohm values and the limitation of the static check.

Show the supplied answer

Two parallel 120 Ohm resistors give 60 Ohm. Three give 40 Ohm. This is an ideal-resistor model; real circuitry may add measurement paths. Measure only with power removed. The static check does not establish a correct sample point or bus dynamics.