SANPO SPI 转 RS485
SANPO SPI 转 RS485 适合 Jetson、树莓派或其他主控板周期控制 RS485 电机和设备。主控板把设备原始指令放入固定长度的 RT 报文,并在当前或后续 SPI 传输中读取反馈。
出厂默认 RS485 参数为 4 Mbps、1 个停止位、无校验、8 个数据位。但是某些 Linux 串口枚举会改变当前运行中的参数,因此,建议每次启动 SPI 控制程序、发送业务数据前,通过管理串口 SANPO Studio Management Port 显式重设 RS485 参数;SPI 数据通道本身不接收文本 AT+ 命令。
发送 AT+ 命令时使用文本模式,并把发送结束符设置为 CRLF。不要把字符 \r\n 当作普通文本发送。
AT+SETRS485=<波特率>,<停止位>,<校验>,<数据位>
例如,设置为宇树电机常用的 4 Mbps、8-N-1:
AT+SETRS485=4000000,1,0,8
示例程序:发送数据前设置 RS485 波特率 和 SPI 模式
安装依赖:
python3 -m pip install pyserial
下面的示例代码会自动找到并通过两个管理口设置 RS485-1~RS485-4 的波特率。示例程序设置为宇树电机常用的 4 Mbps、8-N-1,并把两颗 STM32 MCU 都切换为适合 17 字节指令的 SHORT 模式。
import re
import time
import serial
from serial.tools import list_ports
RS485_COMMAND = "AT+SETRS485=4000000,1,0,8"
SPI_MODE = "SHORT"
def is_management_port(port):
if (port.vid, port.pid) != (0x1209, 0x2323):
return False
info = " ".join(str(getattr(port, name, "") or "")
for name in ("hwid", "interface", "location")).upper()
return ("MI_01" in info or "MANAGEMENT PORT" in info or
re.search(r":(?:X|\d+)\.1(?:\D|$)", info) is not None)
def request(stream, command):
stream.reset_input_buffer()
stream.write(command.encode("ascii") + b"\r\n")
stream.flush()
deadline, reply = time.monotonic() + 1.0, bytearray()
while time.monotonic() < deadline:
reply.extend(stream.read(stream.in_waiting or 1))
if b"OK\r\n" in reply or b"ERR," in reply:
break
text = reply.decode("ascii", errors="replace").strip()
if not text:
raise RuntimeError(f"{stream.port}: {command}: no reply")
print(f"{stream.port}: {command} -> {text}")
return text
def has_line(reply, expected):
return expected in {line.strip() for line in reply.splitlines()}
ports = sorted(p.device for p in list_ports.comports()
if is_management_port(p))
if len(ports) != 2:
raise SystemExit(f"Expected 2 SANPO management ports, found {len(ports)}: {ports}")
for device in ports:
with serial.Serial(device, 1_000_000, timeout=0.05, write_timeout=1) as stream:
time.sleep(0.05)
if not has_line(request(stream, RS485_COMMAND), "OK"):
raise RuntimeError(f"{device}: failed to configure RS485 bitrate")
if not has_line(request(stream, "AT+SPIMODE?"), f"SPIMODE:{SPI_MODE}"):
if not has_line(request(stream, f"AT+SPIMODE={SPI_MODE}"), "OK"):
raise RuntimeError(f"{device}: failed to configure SPI mode")
print("RS485-1..RS485-4 and SPI SHORT configured successfully.")
需要其他串口参数时,修改 RS485_COMMAND 中的四个数字;RS485 指令超过
18 字节时,把 SPI_MODE 改为 LONG,并让 SPI 程序改用 73 字节传输。代码中打开管理口使用的 1_000_000 只是 STM32 CDC 串口参数,不会设置 CAN 或 RS485 的物理波特率;物理总线参数只由明确的 AT+SETRS485 命令修改。
参数 |
支持值 |
|---|---|
波特率 |
|
停止位 |
|
校验 |
|
数据位 |
|
SPI 参数和长度
项目 |
SHORT |
LONG |
|---|---|---|
总传输长度 |
23 字节 |
73 字节 |
报文区 |
22 字节 |
72 字节 |
RS485 数据区 |
18 字节 |
68 字节 |
最后一个字节 |
CRC-8 |
CRC-8 |
两种模式都使用 SPI Mode 0、MSB First。CRC-8 多项式为 0x07,初始值 0x00,无结果异或。
选择 RS485 接口
SPI 片选 |
Channel |
发送目标 |
|---|---|---|
CS0(第一颗 STM32) |
|
RS485-1 |
CS0(第一颗 STM32) |
|
RS485-2 |
CS1(第二颗 STM32) |
|
RS485-3 |
CS1(第二颗 STM32) |
|
RS485-4 |
CS0或者CS1(任意片选) |
|
当前 MCU 的两路 RS485 |
发送时 1/3 都选择当前 STM32 的第一路,2/4 都选择第二路。回包只使用当前 STM32 Channel 1/2。主控中的 CS0、CS1 通常分别连接开发板的两个外部片选。注意:主控和开发板需要共地,接入两个GND可保证数据传输稳定性。
SHORT 报文
发送和接收使用相同布局:
字节位置 |
内容 |
|---|---|
0~1 |
固定 |
2 |
Channel |
3 |
RS485 数据长度 |
4~21 |
18 字节 RS485 数据区,未使用部分填 |
22 |
前 22 字节的 CRC-8 |
将 17 字节宇树控制帧发送到 Channel 2 时,CRC 前的报文区为:
52 54 02 11 FE EE 11 00 00 00 00 00 00 00 00 00 00 00 00 18 7C 00
最后增加对这 22 字节计算得到的 CRC-8。
LONG 报文
字节位置 |
内容 |
|---|---|
0~1 |
固定 |
2 |
Channel |
3 |
RS485 数据长度 |
4~71 |
68 字节 RS485 数据区,未使用部分填 |
72 |
前 72 字节的 CRC-8 |
CRC 算法与 SANPO SPI 转 CAN相同。
读取 RS485 反馈
开发板收到 RS485 数据后,会使用同样的 RT 布局放入 SPI 发送队列:
字节 2 表示反馈来自当前 MCU 的第一路或第二路 RS485。
字节 3 表示本次反馈的数据长度。
数据从字节 4 开始。
未使用的数据区填
00。
SPI 是全双工接口。发送控制帧时读到的可能是旧数据或全 00;如果还没有目标反馈,应继续发送同长度的全零空帧读取。
一次 RS485 接收事件不保证正好对应一条完整的电机协议帧。主控应使用 Channel 区分总线,再按照电机协议的帧头、长度和校验重组数据。
宇树 GO-M8010 示例
完整示例:SPI 转 RS485 宇树 GM8010 使用说明
sudo python3 spi2rs485_unitree_GM8010_demo_v8.py \
--spibus 0 \
--cs 0 \
--motors 1
参数名称以示例程序当前帮助信息为准。首次运行时应降低运动幅度并预留立即断电的安全措施。
常见问题
现象 |
处理方法 |
|---|---|
收到全 |
当前没有排队反馈;稍后继续发送同长度空帧 |
回包 Channel 为 1/2 |
回包使用当前 MCU 的本地编号,属于正常行为 |
17 字节指令在 SHORT 可用,较长指令失败 |
SHORT 最多 18 字节;切换 LONG |
CRC 错误 |
检查 Mode 0、MSB First、固定长度、SPI 时钟和片选时序 |
设备没有回复 |
检查片选、Channel、RS485 参数、设备 ID、指令校验、A/B 接线和供电 |
反馈被拆成多段 |
按设备协议在主控端进行重组,不要把一次 SPI 读取视为固定的一条电机回包 |