CAN · Go · Cross-platform

如何设计一个跨平台 Go CAN / CAN FD 通信库

机器人上层业务真正需要的不是某一个驱动,而是一组稳定、可测试、可替换的通信语义。gocan 的目标,是让同一套业务代码可以运行在 Linux SocketCAN、Windows PCANBasic,以及基于 CANable 2.0 的 SLCAN-FD 设备上。

问题不是“能发帧”,而是“如何保持一致”

不同平台的 CAN API 在通道命名、帧结构、超时、错误码、时间戳与并发模型上都有差异。直接在业务层调用驱动,短期看最快,长期却会让设备协议、操作系统和硬件接口紧密耦合。

驱动层负责解释平台,上层只负责理解 CAN 帧和业务协议。

因此,核心抽象只保留两件事:总线和帧。平台相关配置通过后端配置对象进入,而不是泄漏到所有调用位置。

从最小 Bus / Frame API 开始

type Frame struct {
    ID       uint32
    Data     []byte
    Extended bool
    FD       bool
    BRS      bool
}

type Bus interface {
    Open(ctx context.Context) error
    Send(ctx context.Context, frame Frame) error
    Receive(ctx context.Context) (Frame, error)
    Close() error
}

这个接口刻意不暴露 PCAN handle、Linux socket 文件描述符或串口对象。业务协议只操作 Frame,从而可以用同一套逻辑实现设备发现、命令发送和响应匹配。

帧模型必须明确表达 CAN FD

仅靠 len(Data) > 8 推断 CAN FD 不够可靠。FD 帧还可能包含 BRS,扩展帧 ID 也必须独立表达。显式字段能避免后端做猜测,也便于日志和测试准确还原通信过程。

后端驱动如何隔离

后端主要环境关键差异
SocketCANLinux网络接口模型,支持经典 CAN 与 CAN FD
PCANBasicWindowsDLL 与通道常量,初始化参数由驱动定义
SLCAN-FDWindows / Linux串口协议,需要处理文本命令、波特率与帧编码

每个后端都实现相同接口,并在内部完成帧转换。构造函数可以根据 URI 或配置类型选择后端,例如 socketcan://can0pcan://PCAN_USBBUS1slcanfd:///dev/ttyACM0

并发读取与请求响应匹配

机器人设备经常同时存在周期状态帧、命令响应和错误上报。若每个请求都直接调用底层 Receive,不同 goroutine 会竞争同一数据流,导致响应被错误消费。

更稳定的结构是只保留一个读取循环:

  1. 后端读取循环持续收帧;
  2. 帧进入统一分发器;
  3. 订阅者按 ID、命令字或自定义谓词匹配;
  4. 未匹配帧进入状态流或日志流。
sub := bus.Subscribe(func(f Frame) bool {
    return f.ID == responseID && len(f.Data) > 0 && f.Data[0] == command
})

defer sub.Close()

if err := bus.Send(ctx, request); err != nil {
    return Frame{}, err
}

select {
case frame := <-sub.C():
    return frame, nil
case <-ctx.Done():
    return Frame{}, fmt.Errorf("wait response: %w", ctx.Err())
}

错误必须能指导下一步动作

send failed 对现场调试几乎没有帮助。错误信息应携带后端、通道、操作、帧 ID 和原始错误,并区分超时、总线关闭、设备断开、参数不支持等类别。

实践判断:可以被程序恢复的错误要有稳定类型;只给人看的上下文放在错误链中。这样上层既能自动重连,也能输出完整诊断。

测试策略

硬件库不能只依赖真实设备测试。建议分三层:

  • 纯单元测试:帧编码、DLC 映射、错误转换、过滤器。
  • 虚拟总线测试:Linux 使用 vcan 验证收发、并发与超时。
  • 硬件矩阵测试:PCAN、CANable、实际机器人设备验证驱动差异。

最终得到的不是“封装”,而是可迁移能力

统一通信层让机器人协议实现、设备管理和控制台日志不再关心具体驱动。新增后端时,只需要实现总线接口并通过一致性测试;新增设备型号时,则可以复用稳定的收发、超时与诊断机制。

继续阅读

了解这套通信层如何进入多型号机器人控制台。

Maestro 统一控制台 →