USB 转 CAN
如下协议定义只适用于固件 V4.1 版本及以后
发送 AT+VER 串口指令,可以查询固件版本,如未返回版本号或者返回 V33,请下载最新固件
支持 SANPO 电机调试工具 快速生成调试代码,支持全系列 CAN/RS485 协议电机
协议说明
适用场景 |
USB 发送协议(USB->CAN) |
USB 接收协议(CAN->USB) |
|---|---|---|
CAN 扩展帧电机 |
固定帧头 2 个字节(0x45 0x54)+ Channel 1 个字节 + 扩展帧 CANID 4 个字节 + 数据长度(1 字节)+ 数据(最大 8 字节)+ 固定帧尾 2 个字节(0x0D 0x0A) |
固定帧头 2 个字节(0x45 0x54)+ Channel 1 个字节 + 扩展帧 CANID 4 个字节 + 数据长度(1 字节)+ 数据(最大 8 字节)+ 固定帧尾 2 个字节(0x0D 0x0A) |
CAN 标准帧电机 |
固定帧头 2 个字节(0x53 0x54)+ Channel 1 个字节 + 预留 2 个字节(0x00 0x00)+ CANID 标准帧 2 个字节 + 数据长度(1 字节)+ 数据(最大 8 字节)+ 固定帧尾 2 个字节(0x0D 0x0A) |
固定帧头 2 个字节(0x53 0x54)+ Channel 1 个字节 + 预留 2 个字节(0x00 0x00)+ CANID 标准帧 2 个字节 + 数据长度(1 字节)+ 数据(最大 8 字节)+ 固定帧尾 2 个字节(0x0D 0x0A) |
使用 SANPO 电机调试工具 快速生成测试代码
CAN 扩展帧报文示例
发送报文
固定帧头(2 字节) |
Channel(1 字节) |
CANID(4 字节) |
数据长度(1 字节) |
数据(最大 8 字节) |
固定帧尾(2 字节) |
|---|---|---|---|---|---|
0x45 0x54 |
0x00 |
0x01 0x7F 0xFF 0x07 |
0x08 |
0x7F 0x2A 0x7F 0xFF 0x05 0x1E 0x19 0x99 |
0x0D 0x0A |
接入 USB 后,会显示 2 个串口,两个串口分别对应 STM32(1) 和 STM32(2),请查看开发板背面的说明,每个串口控制 2 路 CAN 接口
Channel 为 0x01 或者 0x02 或者 0x03 或者 0x04,指定向哪个 CAN 接口发送报文,如果为 0x00,将向当前串口上的所有 CAN 接口发送报文
扩展帧 CANID 为 29bit,占据 CANID 4 个字节的低 29bit
接收报文
固定帧头(2 字节) |
Channel(1 字节) |
CANID(4 字节) |
数据长度(1 字节) |
数据(最大 8 字节) |
固定帧尾(2 字节) |
|---|---|---|---|---|---|
0x45 0x54 |
0x00 |
0x02 0x00 0x09 0xFD |
0x08 |
0x7F 0xF9 0x7F 0xDF 0x7F 0xFF 0x01 0x11 |
0x0D 0x0A |
代码片段(CAN 扩展帧,Python)
(完整样例请参考 usb2can_cybergear_sine_demo_v4.py
HEADER_NORMAL_EXT = bytes([0x45, 0x54])
TAIL = bytes([0x0D, 0x0A])
# 发送(帧头2字节 + channel1字节 + CANID4字节 + DLC1字节 + data + 帧尾2字节)
def build_extended_frame(channel: int, arbitration_id: int, data: list[int]) -> bytes:
can_id_bytes = arbitration_id.to_bytes(4, byteorder="big", signed=False)
payload = bytearray(HEADER_NORMAL_EXT)
payload.append(channel & 0xFF)
payload.extend(can_id_bytes)
data_bytes = bytearray(data[:8])
payload.append(len(data_bytes))
payload.extend(data_bytes)
payload.extend(TAIL)
return bytes(payload)
payload = build_extended_frame(channel=0x00, arbitration_id=0x0000FD01, data=[0x01] + [0x00] * 7)
ser.write(payload)
ser.flush()
# 接收(帧头2字节 + channel1字节 + CANID4字节 + DLC1字节 + data + 帧尾2字节)
rx = ser.read(ser.in_waiting or 1)
if rx[:2] == HEADER_NORMAL_EXT and rx[-2:] == TAIL:
channel = rx[2]
can_id = int.from_bytes(rx[3:7], byteorder="big", signed=False)
dlc = rx[7]
data = rx[8 : 8 + dlc]
print(channel, f"0x{can_id:08X}", dlc, list(data))
CAN 标准帧报文示例
发送报文
固定帧头(2 字节) |
Channel(1 字节) |
预留 2 个字节 |
CANID(2 字节) |
数据长度(1 字节) |
数据(最大 8 字节) |
固定帧尾(2 字节) |
|---|---|---|---|---|---|---|
0x53 0x54 |
0x00 |
0x00 0x00 |
0x01 0x42 |
0x08 |
0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04 |
0x0D 0x0A |
接入 USB 后,会显示 2 个串口,两个串口分别对应 STM32(1) 和 STM32(2),请查看开发板背面的说明,每个串口控制 2 路 CAN 接口
Channel 为 0x01 或者 0x02 或者 0x03 或者 0x04,指定向哪个 CAN 接口发送报文,如果为 0x00,将向当前串口上的所有 CAN 接口发送报文
标准帧 CANID 为 11bit,占据 CANID 2 个字节的低 11bit
接收报文
固定帧头(2 字节) |
Channel(1 字节) |
预留 2 个字节 |
CANID(2 字节) |
数据长度(1 字节) |
数据(最大 8 字节) |
固定帧尾(2 字节) |
|---|---|---|---|---|---|---|
0x53 0x54 |
0x00 |
0x00 0x00 |
0x01 0x42 |
0x08 |
0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04 |
0x0D 0x0A |
代码片段(CAN 标准帧,Python)
(完整代码请参考 usb2can_demo.py
HEADER_NORMAL_STD = bytes([0x53, 0x54])
TAIL = bytes([0x0D, 0x0A])
# 发送(帧头2字节 + channel1字节 + 预留2字节 + CANID2字节 + DLC1字节 + data + 帧尾2字节)
def build_standard_frame(channel: int, can_id_11bit: int, data: bytes) -> bytes:
if not 0 <= can_id_11bit <= 0x7FF:
raise ValueError("standard CAN ID must be 0..0x7FF (11 bits)")
can_id_bytes = can_id_11bit.to_bytes(2, byteorder="big", signed=False)
dlc = len(data)
return (
HEADER_NORMAL_STD
+ bytes([channel & 0xFF])
+ bytes([0x00, 0x00]) # reserved
+ can_id_bytes
+ bytes([dlc])
+ data
+ TAIL
)
payload = build_standard_frame(channel=0x00, can_id_11bit=0x142, data=bytes([0x00] * 8))
ser.flush()
ser.write(payload)
ser.flush()
# 接收(帧头2字节 + channel1字节 + 预留2字节 + CANID2字节 + DLC1字节 + data + 帧尾2字节)
rx = ser.read(ser.in_waiting or 1)
if rx[:2] == HEADER_NORMAL_STD and rx[-2:] == TAIL:
channel = rx[2]
can_id = int.from_bytes(rx[5:7], byteorder="big", signed=False)
dlc = rx[7]
data = rx[8 : 8 + dlc]
print(channel, f"0x{can_id:04X}", dlc, list(data))
电机调试工具
SANPO 电机调试工具 支持通用 CAN/RS485 电机,包括小米、宇树、灵足、达秒、脉塔、翎控等
小米 CyberGear 电机官方调试软件:下载地址
注意:保存路径中不能有中文,否则软件无法启动
注意:小米官方调试软件协议特殊,连接小米官方调试软件以后,开发板会切换为小米模式,使用完成后,请发送 AT+ET 串口指令切换为正常模式
开发样例
USB 转 CAN 扩展帧样例程序(Python 版本):下载地址
例如:向小米 CyberGear CAN 电机(电机 ID 为 12)下发正弦运控指令,电机接入开发板串口 Windows COM8(Linux /dev/ttyACM0),CAN-4 接口
Windows:
python3 usb2can_cybergear_sine_demo_v4.py --port COM8 --motors 12 --channel 4
Ubuntu(Jetson):
sudo python3 usb2can_cybergear_sine_demo_v4.py --port /dev/ttyACM0 --motors 12 --channel 4