# USB CDC串口转 CAN 本协议用于 USB CDC串口协议收发经典 CAN 报文,为变长协议,支持标准帧和扩展帧,单帧数据区最大 8 字节。CAN FD 请使用 [USB CDC串口转 FDCAN](usb_fdcan)。 如果在 Linux 上使用 SocketCAN,请参考 [SocketCAN / gs_usb](socketcan)。SocketCAN 不使用本页的 `ST / ET` 帧头。 USB CDC串口转 CAN 报文均以 `0x0D 0x0A` 结尾。 经典 CAN 波特率使用 `AT+SETFDCAN=,` 中的 `arb_baud`。`data_baud` 只用于 CAN FD 数据段,不影响经典 CAN。 ## 协议格式 | 类型 | 帧格式 | | --- | --- | | 经典 CAN 扩展帧 | `0x45 0x54 + Channel(1字节) + ExtID(4字节) + 数据长度编码(1字节) + Data(0~8字节) + 0x0D 0x0A` | | 经典 CAN 标准帧 | `0x53 0x54 + Channel(1字节) + 0x00 0x00(保留字段2字节) + StdID(2字节) + 数据长度编码(1字节) + Data(0~8字节) + 0x0D 0x0A` | ## 字段说明 | 字段 | 说明 | | --- | --- | | `0x45 0x54` | ASCII`ET`的十六进制表示,经典 CAN 扩展帧 报文头 | | `0x53 0x54` | ASCII`ST`的十六进制表示,经典 CAN 标准帧 报文头 | | `Channel` | `0x01~0x04` 指定 CAN 接口;`0x00` 表示发送到当前设备侧所有 CAN 接口 | | `ExtID` | 29 bit 扩展帧 CAN ID,放在 4 字节字段低 29 bit | | `StdID` | 11 bit 标准帧 CAN ID,放在 2 字节字段低 11 bit | | `保留字段` | 标准帧保留字段,固定 `0x00 0x00` | | `数据长度编码` | 数据长度编码,取值 `0x00~0x08` | | `Data` | CAN 数据,长度由 `Len` 决定 | | `0x0D 0x0A` | 报文尾 | 经典 CAN 的“数据长度编码”,字段范围 `0x00~0x08`。 | 编码字段值 | 真实数据长度 | | --- | ---: | | `0x00` | 0 | | `0x01` | 1 | | `0x02` | 2 | | `0x03` | 3 | | `0x04` | 4 | | `0x05` | 5 | | `0x06` | 6 | | `0x07` | 7 | | `0x08` | 8 | ## 扩展帧示例 | 帧头 | Channel | CANID | Len | Data | 帧尾 | | --- | --- | --- | --- | --- | --- | | `0x45 0x54` | `0x01` | `0x00 0x00 0xFD 0x01` | `0x08` | `0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08` | `0x0D 0x0A` | ## 标准帧示例 | 帧头 | Channel | Reserved | CANID | Len | Data | 帧尾 | | --- | --- | --- | --- | --- | --- | --- | | `0x53 0x54` | `0x02` | `0x00 0x00` | `0x01 0x42` | `0x08` | `0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04` | `0x0D 0x0A` | ## Python 示例 完整代码样例请参考 usb2can_cybergear_sine_demo_v6.py ```python TAIL = bytes([0x0D, 0x0A]) def build_usb_can_ext(channel: int, can_id: int, data: bytes) -> bytes: if not 0 <= can_id <= 0x1FFFFFFF: raise ValueError("extended CAN ID must be 0..0x1FFFFFFF") data = data[:8] return ( bytes([0x45, 0x54, channel & 0xFF]) + can_id.to_bytes(4, "big") + bytes([len(data)]) + data + TAIL ) def build_usb_can_std(channel: int, can_id: int, data: bytes) -> bytes: if not 0 <= can_id <= 0x7FF: raise ValueError("standard CAN ID must be 0..0x7FF") data = data[:8] return ( bytes([0x53, 0x54, channel & 0xFF, 0x00, 0x00]) + can_id.to_bytes(2, "big") + bytes([len(data)]) + data + TAIL ) ```