1. CANopen协议概述:从CAN总线到CANopen
各位同学,咱们今天正式开课。做嵌入式实时控制,CANopen 是绕不开的一关。我最早接触它是在一个伺服驱动器项目上,当时被对象字典搞得一头雾水——后来才发现,这东西说白了就是个“通信协议+设备描述”的标准化方案。
先别急着看代码,咱们把地基打牢。
1.1 从CAN总线到CANopen
CAN 总线(Controller Area Network)是底层硬件,它只负责把数据帧发出去、收回来。但问题来了——你发了一帧数据,里面 8 个字节到底代表什么?是速度值还是温度值?谁该响应?谁该忽略?
CANopen 就是在 CAN 总线之上,定义了一套“大家怎么说话”的规则。它把 CAN 帧的 11 位 ID 重新组织,分成功能码和节点 ID,还规定了数据内容的含义。
核心区别一句话:CAN 是物理层+数据链路层,CANopen 是应用层协议。没有 CANopen,你只能看到一堆 0 和 1;有了 CANopen,你才知道“0x41 0x00”是“电机目标转速 1000 rpm”。
我刚开始做项目时,直接用 CAN 裸帧通信,结果每次换设备都要重新对协议,累得够呛。后来切到 CANopen,才发现标准化有多香。
1.2 对象字典(OD)的概念
对象字典是 CANopen 的灵魂。你可以把它想象成一个“设备参数大表格”。每个参数都有一个 16 位索引(Index)和 8 位子索引(Subindex)。
举个例子:
| 索引 | 子索引 | 参数名称 | 数据类型 | 说明 |
|---|---|---|---|---|
| 0x6007 | 0x00 | Abort Connection Option | UNSIGNED8 | 节点掉线后的行为 |
| 0x6040 | 0x00 | Controlword | UNSIGNED16 | 控制字,启停电机 |
| 0x6041 | 0x00 | Statusword | UNSIGNED16 | 状态字,读取状态 |
对象字典里不光存数据,还存了数据的属性——比如读写权限、数据类型、默认值。我见过不少新手直接往 0x6040 写 0x0006,结果电机没反应。为什么?因为控制字要先写 0x06 进入“准备就绪”状态,再写 0x0F 才能启动。这些状态机逻辑,都写在对象字典对应的设备配置文件里。
避坑指南:我曾经在调试时发现对象字典里某个参数怎么都写不进去,查了半天才发现是子索引写错了。记住:子索引 0x00 通常代表“整个对象”或“默认值”,具体要看 DS-301 或设备子协议的定义。
1.3 PDO与SDO的区别
这是 CANopen 里最核心的两个通信机制。我直接说人话:
- SDO(Service Data Object):像快递,点对点,确认送达。你发一个请求,对方回一个响应。适合配置参数、读写对象字典。
- PDO(Process Data Object):像广播,一对多,不确认。适合实时传输控制数据,比如速度、位置、电流。
为什么要有两种?你想想看:
- SDO 有握手、有确认,速度慢但可靠。适合上电初始化时配置参数。
- PDO 没有握手,一帧最多 8 字节数据,速度快。适合 1ms 甚至更快的控制周期。
关键区别表:
| 特性 | SDO | PDO |
|---|---|---|
| 通信方式 | 点对点,请求-响应 | 生产者-消费者,无确认 |
| 数据长度 | 最多 4 字节(分段可更多) | 最多 8 字节 |
| 用途 | 配置、诊断、读写 OD | 实时控制、状态反馈 |
| 实时性 | 低(ms 级) | 高(μs 级) |
| CAN ID 分配 | 固定(0x580+节点ID 等) | 可映射,可配置 |
我记得有个项目,客户要求 500μs 内完成 4 个轴的位置同步。用 SDO 肯定不行,一帧来回就 200μs 了。最后全部切到 PDO,配合同步帧,才把抖动压下去。
1.4 CANopenNode软件架构简介
CANopenNode 是一个开源的 CANopen 协议栈实现,用 C 语言写的。我选它做课程平台,原因有三:
- 代码干净:分层清晰,移植方便。我在 STM32、AT32、GD32 上都跑过。
- 文档齐全:虽然英文为主,但例程很全。
- 社区活跃:遇到问题,GitHub Issues 里基本能找到答案。
它的架构大致分三层:
- CAN 驱动层:负责收发 CAN 帧,跟硬件打交道。
- 协议栈核心层:处理 NMT、SDO、PDO、心跳、同步等协议逻辑。
- 应用层:你的业务代码,比如电机控制算法、传感器采集。
下面这张图,是我自己画的一个简化架构,你看一眼就明白了:
从图上你能看到,应用层在最上面,你写业务逻辑;核心层在中间,处理所有 CANopen 协议细节;驱动层在最下面,跟硬件打交道。我一般移植时,只改驱动层和应用层,核心层基本不动。
注意:CANopenNode 默认使用定时器中断来驱动协议栈的定时事件(比如心跳、同步)。如果你用的 MCU 定时器资源紧张,记得提前规划好。我曾经在 GD32F103 上因为定时器冲突,导致心跳丢帧,排查了两天才发现是定时器优先级没配好。
好了,第一章就到这里。对象字典、PDO、SDO 这三个概念,你最好能用自己的话讲清楚。后面几章,咱们会一步步搭建一个真实的 CANopen 从站,从 OD 配置到 PDO 映射,再到实时控制,全走一遍。