USB CDC串口转 FDCAN

本协议用于 USB CDC串口协议收发 CAN FD 报文,为变长协议,支持标准帧和扩展帧,单帧数据区最大 64 字节。CAN FD 支持 BRS。仲裁段波特率由 AT+SETFDCAN=<arb_baud>,<data_baud>arb_baud 配置,数据段波特率由 data_baud 配置,当前支持 2Mbps5Mbps

出厂默认 CAN/FDCAN 参数为仲裁段 1Mbps、数据段 5Mbps、BRS 开启;如果现场 CAN FD 总线使用该参数,可以直接按本页 SF / EF 帧格式发送。需要修改波特率时,再通过 USB CDC串口工具发送 AT+SETFDCAN=<arb_baud>,<data_baud>,并把发送结束符设置为 CRLF。配置会写入固件持久化存储,下次上电继续使用,无需重复配置。

如果在 Linux 上使用 SocketCAN,请参考 SocketCAN / gs_usb。SocketCAN 不使用本页的 SF / EF 帧头。

USB CDC串口转 FDCAN 协议报文以 0x0D 0x0A 结尾。

协议格式

类型

帧格式

CAN FD 扩展帧

0x45 0x46 + Channel(1字节) + ExtID(4字节) + 数据长度编码(1字节) + Data(0~64字节) + 0x0D 0x0A

CAN FD 标准帧

0x53 0x46 + Channel(1字节) + 0x00 0x00(保留字段2字节) + StdID(2字节) + 数据长度编码(1字节) + Data(0~64字节) + 0x0D 0x0A

字段说明

字段

说明

0x45 0x46

ASCIIEF的十六进制表示,CAN FD 扩展帧 报文头

0x53 0x46

ASCIISF的十六进制表示,CAN FD 标准帧 报文头

Channel

0x01~0x04 指定 CAN/FDCAN 接口;0x00 表示发送到当前设备侧所有 CAN/FDCAN 接口

ExtID

29 bit 扩展帧 CAN ID,放在 4 字节字段低 29 bit

StdID

11 bit 标准帧 CAN ID,放在 2 字节字段低 11 bit

保留字段

标准帧保留字段,固定 0x00 0x00

数据长度编码

CAN FD 数据长度编码,取值 0x00~0x0F

Data

CAN FD 数据,真实长度由“数据长度编码”决定

0x0D 0x0A

报文尾

CAN FD 的“数据长度编码”,字段范围 0x00~0x0F

编码字段值

真实数据长度

0x00

0

0x01

1

0x02

2

0x03

3

0x04

4

0x05

5

0x06

6

0x07

7

0x08

8

0x09

12

0x0A

16

0x0B

20

0x0C

24

0x0D

32

0x0E

48

0x0F

64

扩展帧示例

帧头

Channel

CANID

数据长度编码

Data

帧尾

0x45 0x46

0x01

0x01 0x7F 0xFF 0x07

0x0A

16 字节有效数据

0x0D 0x0A

标准帧示例

帧头

Channel

Reserved

CANID

数据长度编码

Data

帧尾

0x53 0x46

0x02

0x00 0x00

0x01 0x42

0x09

12 字节有效数据

0x0D 0x0A

Python 示例

TAIL = bytes([0x0D, 0x0A])
LEN_TABLE = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]

def length_code_to_size(code: int) -> int:
    if not 0 <= code < len(LEN_TABLE):
        raise ValueError("invalid CAN FD length code")
    return LEN_TABLE[code]

def build_usb_fdcan_ext(channel: int, can_id: int, length_code: int, data: bytes) -> bytes:
    if not 0 <= can_id <= 0x1FFFFFFF:
        raise ValueError("extended CAN ID must be 0..0x1FFFFFFF")
    size = length_code_to_size(length_code)
    if len(data) != size:
        raise ValueError("data length does not match CAN FD length code")
    return bytes([0x45, 0x46, channel & 0xFF]) + can_id.to_bytes(4, "big") + bytes([length_code]) + data + TAIL

def build_usb_fdcan_std(channel: int, can_id: int, length_code: int, data: bytes) -> bytes:
    if not 0 <= can_id <= 0x7FF:
        raise ValueError("standard CAN ID must be 0..0x7FF")
    size = length_code_to_size(length_code)
    if len(data) != size:
        raise ValueError("data length does not match CAN FD length code")
    return bytes([0x53, 0x46, channel & 0xFF, 0x00, 0x00]) + can_id.to_bytes(2, "big") + bytes([length_code]) + data + TAIL