![]() |
RT-Thread RTOS
An open source embedded real-time operating system
|
RT-Thread exposes CAN controllers as **RT_Device_Class_CAN** devices. The framework in **components/drivers/can/dev_can.c** provides interrupt-driven RX/TX FIFOs, optional hardware filters, CAN-FD message layout, blocking and non-blocking send paths, and periodic bus status polling. Board and SoC drivers implement **struct rt_can_ops** and register with **rt_hw_can_register()**.
**can_dm.c** is not the bus core — it only supplies ISO 11898 DLC/length helpers (can_dlc2len, can_len2dlc). See CAN FD DLC helpers (can_dm).
Header: components/drivers/include/drivers/dev_can.h. Core: components/drivers/can/dev_can.c.
| Component | File | Role |
|---|---|---|
| Framework | dev_can.c | Device ops, FIFOs, ISR demux, rt_hw_can_register |
| FD DLC helpers | can_dm.c, can_dm.h | DLC ↔ byte length (FD payloads) |
| DM SoC example | bsp/rockchip/dm/can/canfd-rockchip.c | OFW platform driver + rt_hw_can_register |
| Legacy BSP | e.g. bsp/stm32/.../drv_can.c, drv_fdcan.c | HAL-based rt_can_ops |
With **RT_USING_DM**, SoC-specific CAN drivers may live under **SOC_DM_CAN_DIR** (see can/Kconfig).
| Option | Role |
|---|---|
**RT_USING_CAN** | Build components/drivers/can/ |
**RT_CAN_USING_HDR** | Per-filter RX lists and filter callbacks (rt_can_hdr) |
**RT_CAN_USING_CANFD** | 64-byte payload, fd_frame / brs, bit-timing fields in can_configure |
**RT_CANMSG_BOX_SZ** | Software RX buffer depth (messages) |
**RT_CANSND_BOX_NUM** | Concurrent blocking TX slots (match HW mailboxes) |
**RT_CANSND_MSG_TIMEOUT** | Blocking send completion timeout (ticks) |
**RT_CAN_NB_TX_FIFO_SIZE** | Non-blocking TX ring buffer (bytes); size ≥ N × sizeof(struct rt_can_msg) |
**RT_CAN_MALLOC_NB_TX_BUFFER** | Allocate NB ring at open instead of static pool in rt_can_device |
Optional (not in core Kconfig): **RT_CAN_USING_BUS_HOOK** — periodic bus_hook from status timer.
| Open flag | Effect |
|---|---|
**RT_DEVICE_FLAG_INT_RX** | Allocates software RX FIFO; driver enables RX IRQ via control(SET_INT, INT_RX) |
**RT_DEVICE_FLAG_INT_TX** | Allocates TX mailbox pool + semaphore; enables TX IRQ |
**RT_DEVICE_FLAG_RDWR** | Set at register time (rt_hw_can_register) |
**read** only works with **INT_RX** and ref_count > 0. **write** requires the device to be opened; size must be a multiple of **sizeof(struct rt_can_msg)**.
Register **rt_device_set_rx_indicate()** to wake a thread when frames arrive (see example in dev_can.h Doxygen block).
Batch read: pass an array of rt_can_msg and divide returned bytes by sizeof(struct rt_can_msg).
| Mode | How | Behavior |
|---|---|---|
| Blocking | Default (nonblocking == 0), thread context, INT_TX open | Waits for HW mailbox + RT_CAN_EVENT_TX_DONE / FAIL |
| Non-blocking | msg.nonblocking = 1, or call from ISR | Uses sendmsg_nonblocking; overflow → nb_tx_rb ring |
| Private mailbox | RT_CAN_CMD_SET_PRIV + msg.priv = mailbox index | _can_int_tx_priv — fixed TX slot per message |
**RT_CANSND_MSG_TIMEOUT** applies to blocking waits. Non-blocking path needs **ops->sendmsg_nonblocking** implemented in the low-level driver.
| Command | Value | Argument | Role |
|---|---|---|---|
**RT_DEVICE_CTRL_CONFIG** | — | struct can_configure * | Re-configure (baud, mode, box sizes) |
**RT_CAN_CMD_SET_BAUD** | 0x14 | baud (driver-defined) | Set arbitration bitrate |
**RT_CAN_CMD_SET_BAUD_FD** | 0x1B | baud | CAN-FD data phase bitrate |
**RT_CAN_CMD_SET_BITTIMING** | 0x1C | struct rt_can_bit_timing_config * | Custom prescaler/segments |
**RT_CAN_CMD_SET_MODE** | 0x15 | mode | RT_CAN_MODE_NORMAL / LISTEN / LOOPBACK / LOOPBACKANLISTEN |
**RT_CAN_CMD_SET_PRIV** | 0x16 | RT_CAN_MODE_PRIV / NOPRIV | Private TX mailbox mode |
**RT_CAN_CMD_SET_FILTER** | 0x13 | struct rt_can_filter_config * | HW filters (needs **RT_CAN_USING_HDR**) |
**RT_CAN_CMD_SET_CANFD** | 0x1A | enable flag | Enable FD in driver |
**RT_CAN_CMD_START** | 0x1D | non-zero / zero | Start or stop controller |
**RT_CAN_CMD_GET_STATUS** | 0x17 | struct rt_can_status * | Error counters, pkg stats |
**RT_CAN_CMD_SET_STATUS_IND** | 0x18 | rt_can_status_ind_type_t | User callback on timer tick |
**RT_CAN_CMD_SET_BUS_HOOK** | 0x19 | rt_can_bus_hook | Periodic hook (if enabled) |
**RT_DEVICE_CTRL_SET_INT / CLR_INT** | — | RT_DEVICE_FLAG_INT_RX/TX, RT_DEVICE_CAN_INT_ERR | IRQ mask (used internally on open/close) |
Typical bring-up sequence:
rt_device_open(..., INT_TX | INT_RX)RT_CAN_CMD_SET_BAUD / SET_BITTIMING / SET_CANFD as neededRT_CAN_CMD_SET_FILTER (optional)RT_CAN_CMD_SET_MODERT_CAN_CMD_START with non-zero argumentOn **close**, the framework calls **RT_CAN_CMD_START** with **RT_FALSE** to stop the controller.
CAN1MBaud, CAN800kBaud, CAN500kBaud, CAN250kBaud, CAN125kBaud, CAN100kBaud, CAN50kBaud, CAN20kBaud, CAN10kBaud — used in **can_configure.baud_rate**.
| Field | Meaning |
|---|---|
**id** (29 bits) | CAN ID |
**ide** | RT_CAN_STDID (0) or RT_CAN_EXTID (1) |
**rtr** | RT_CAN_DTR (data) or RT_CAN_RTR (remote) |
**len** | DLC or payload length (driver may expect bytes or DLC; FD drivers often use can_len2dlc) |
**priv** | TX mailbox index in private mode |
**hdr_index** | RX: matched filter bank; set -1 before read for any frame |
**nonblocking** | 1 → non-blocking send path |
**fd_frame / brs** | CAN-FD (RT_CAN_USING_CANFD) |
**data[]** | 8 bytes (classic) or 64 bytes (FD) |
| Filter field | Role |
|---|---|
**mode** | RT_CAN_MODE_MASK (0) or RT_CAN_MODE_LIST (1) |
**mask** | Mask mode: 1 = bit must match |
**hdr_bank** | -1 = driver assigns; ≥0 = fixed bank |
**rxfifo** | CAN_RX_FIFO0 or CAN_RX_FIFO1 |
**ind / args** | Optional per-filter RX callback |
Framework mirrors filters into **can->hdr[]** and routes RX frames to per-bank lists when **hdr_index** matches.
Helper macros: **RT_CAN_FILTER_ITEM_INIT**, **RT_CAN_FILTER_EXT_INIT**, **RT_CAN_STD_RMT_FILTER_INIT**, etc. (dev_can.h).
Enable **RT_CAN_USING_CANFD** in Kconfig and in driver:
can_configure.enable_canfd**, **baud_rate_fd**use_bit_timing** + **can_timing** / **canfd_timing** (struct rt_can_bit_timing)RT_CAN_CMD_SET_CANFD**, **RT_CAN_CMD_SET_BAUD_FD**, **RT_CAN_CMD_SET_BITTIMING**Set **rt_can_msg.fd_frame** and **brs** on TX; on RX use **can_dlc2len()** if HW returns DLC nibble (CAN FD DLC helpers (can_dm)).
**struct rt_can_status** — REC/TEC, **errcode** (RT_CAN_BUS_*), packet/drop counters, error type histogram.
Periodic timer (**can_configure.ticks**, default from driver) calls **RT_CAN_CMD_GET_STATUS** and optional **status_indicate** callback.
**enum RT_CAN_STATUS_MODE**: NORMAL, ERRWARNING, ERRPASSIVE, BUSOFF (derived from **errcode** in FinSH **canstat**).
FinSH (when **RT_USING_FINSH**): **canstat can1** — exported from dev_can.c.
| Op | Required | Role |
|---|---|---|
**configure** | Yes | Apply struct can_configure (baud, mode, FD) |
**control** | Yes | IOCTL commands above, SET_INT/CLR_INT |
**sendmsg** | For blocking TX | boxno = mailbox index |
**recvmsg** | For IRQ RX | fifo = HW RX FIFO index; fill rt_can_msg |
**sendmsg_nonblocking** | For NB TX / ISR | Return **RT_EOK** or **-RT_EBUSY** |
Device name is typically **can0**, **can1**, … Auto-naming: **rt_dm_dev_set_name_auto(&can->parent, "can")** in DM drivers (Rockchip).
BSP ISR calls **rt_hw_can_isr(can, event)**:
event & 0xff | Meaning |
|---|---|
**RT_CAN_EVENT_RX_IND** (0x01) | Frame received; **event >> 8** = RX FIFO index |
**RT_CAN_EVENT_RXOF_IND** (0x06) | RX overflow (still tries one recv) |
**RT_CAN_EVENT_TX_DONE** (0x02) | Mailbox **event >> 8** completed OK |
**RT_CAN_EVENT_TX_FAIL** (0x03) | Mailbox transmission failed |
After **TX_DONE**, framework drains **nb_tx_rb** via **sendmsg_nonblocking**.
**recvmsg** must return byte count or -1 if no frame. Populate **hdr_index** when hardware reports filter match.
RT_PLATFORM_DRIVER_EXPORT** with **rt_ofw_node_id** (compatible)probe**: map reg/IRQ, **rt_clk_get**, **rt_reset_control_get**, deassert resetcan_configure** (bit timing from DT or constants)rt_hw_interrupt_install**, **rt_hw_can_register**remove**: mask IRQ, **rt_device_unregister**Reference: bsp/rockchip/dm/can/canfd-rockchip.c — uses **can_dm.h** for FD DLC encoding.
| BSP / path | Notes |
|---|---|
STM32 drv_can.c / drv_fdcan.c | Classic + FDCAN |
NXP i.MX / MCX drv_can.c | FlexCAN |
| WCH, Renesas, Phytium, Synwit, Nuvoton | Various rt_hw_can_register |
Rockchip canfd-rockchip.c | DM + OFW |
rt_hw_can_register**recvmsg** + **rt_hw_can_isr**; no blocking in ISRRT_CAN_CMD_START**sndboxnumber** to match hardware TX buffersRT_CAN_NB_TX_FIFO_SIZE** — sizeof(struct rt_can_msg) is much larger with 64-byte payloadINT_RX/INT_TX — read/write return **-RT_ENOSYS**sendmsg_nonblocking — write with nonblocking=1 failsdropedrcvpkg); increase **msgboxsz** or read fasterRT_CAN_CMD_GET_STATUS**; recovery is driver/BSP specificcan_len2dlc/can_dlc2len** consistently (CAN FD DLC helpers (can_dm))open — ref_count > 1 skips FIFO teardown on close; design single-owner stackscan_dm) — FD DLC helperscomponents/drivers/include/drivers/dev_can.h — full API and application examplecomponents/drivers/can/dev_can.ccomponents/drivers/can/readme-zh.txt — legacy Chinese notes for driver porting