1. 为什么两者天然统一:CoE 的出身
先把结论说透:对象字典本来就是 CANopen 的,EtherCAT 的 CoE 是"把 CANopen 搬到 EtherCAT 上"。
- CiA 301(应用层/对象字典规范)定义 OD 结构:index+subindex 寻址、数据类型(uint8/16/32、int8/16/32、visible string、octet string)、通信对象区 0x1000~0x1FFF;
- CiA 402(驱动规范)定义 0x6000 系列运动对象:6040h 控制字、6041h 状态字、6060h 模式、607Ah 目标位置……CANopen 板块第 10 篇讲的就是它;
- EtherCAT CoE(ETG.1000.6)直接复用这套字典:对象编号、subindex、数据类型、甚至 PDO 映射对象 0x1600/0x1A00 都与 CANopen 完全一致。
这意味着:同一个 6040h=0x000F 命令、同一个 607Ah 目标位置、同一张 0x1600 映射表,在两种总线上语义完全相同。标准层面没有"统一"问题,问题只在软件实现——怎么让一套固件、一份应用代码同时服务两条总线。
2. 统一要吸收的差异:一张对照表
语义相同,但访问通道、传输机制、同步与监控不同。适配层要吸收的差异全在这里:
| 维度 | CANopen | EtherCAT(CoE) | 统一策略 |
|---|---|---|---|
| 对象字典定义 | CiA 301 + CiA 402 | CoE 复用同一字典 | 同一份 OD 表 |
| 配置通道 | SDO(COB-ID 0x580/0x600 + 节点号) | CoE SDO over 邮箱(0x1000 系列) | 适配层收口成 od_get/set |
| 过程数据 | PDO(COB-ID 0x180+节点 / 8 字节 CAN 帧) | PDO over SM2/SM3 + FMMU + LRW | 同一张映射表(编号一致) |
| 同步机制 | SYNC + 同步 PDO(1ms 级) | DC Sync0(ns 级抖动) | 适配层各自实现,应用无感 |
| 网络状态机 | NMT(Op/PreOp/Stopped) | AL 状态机(Init→PreOp→SafeOp→Op) | 各自维护,只暴露"网通"信号 |
| 节点监控 | Heartbeat / LifeGuard | 看门狗 WD(帧超时 + PDI WD) | 各自实现 |
| 参数保存 | 0x1010 存 / 0x1011 恢复 | 同左(主站还可管 ESI) | 统一 0x1010/0x1011 实现 |
| 字节序 | little-endian | little-endian | 天然一致,无需处理 |
| 帧长限制 | PDO ≤ 8 字节/帧 | 映射总长无硬限 | OD 按 bit 宽定义,截断由适配层处理 |
注意最后一行:统一的是字典和访问语义,不是实时机制。DC 是 EtherCAT 独有,多轴龙门/机器人强同步只能走 EtherCAT;CANopen 的优势在成本(无 ESC+PHY),单轴/IO 类 1ms 周期足够。产品线布局通常是:单轴/IO 用 CANopen,多轴联动用 EtherCAT,固件共用七成以上。
3. 统一架构总览:OD 核心 + 双协议适配层
核心思路一句话:应用层和协议栈之间隔一层"协议无关的对象字典核心"。应用只认识 index.subindex;CANopen 栈和 EtherCAT 栈都把自己收到的请求翻译成对这个核心的读写。
这套分层的直接收益:
- 应用一次编写、双协议复用——运动控制逻辑只读 OD,切换总线不动应用;
- 固件维护一份 OD 文档——新增对象只改核心表,两种栈自动可见;
- 诊断/一致性检查一套——0x1010/0x1011 参数保存、写保护、上限校验只写一次。
工程印证:汇川 SV660N(CANopen)与 SV660E(EtherCAT)同平台固件换协议板卡;从站侧一颗 MCU 同时挂 CAN 收发器和 SPI 接 LAN9252,配置字选协议启动哪边栈,OD 完全一份。
4. 对象字典核心设计:一项一表项
两种协议栈各自都带一张字典表(CANopenNode 的 OD、SSC 生成的 CoE OD),结构大同小异。统一的核心是把它们抽成公共表项,并补齐 CANopen 栈一般没有的三个字段:
三个字段的价值:
- pdo_map 映射位——决定"哪些对象能进 0x1600/0x1A00"。没有它,映射逻辑得硬编码或按协议分开维护;有了它,映射表统一生成;
- on_write 变更回调——CANopen 和 CoE 的 SDO 都能写 6060h,改模式的动作(停轴、清目标)必须两边都触发——把动作挂在 OD 项上,天然双栈复用;
- check 一致性检查——上限/单位校验只写一处,SDO 和本地配置(厂商工具)走同一条校验链。
5. 统一访问接口:od_get / od_set / od_subscribe
两个栈的 SDO 处理都收口到三个薄接口:
要点:接口里不许出现"哪个协议"的痕迹。调用方(应用)和实现方(栈)都只认 od_key_t;协议特有的东西(COB-ID、邮箱头)全部关在适配层内部。这样双栈并存时,新增第三种协议(如 Modbus RTU 做配置通道)只是再加一个 handler。
6. 映射表统一:同一张 0x1600 / 0x1A00
前文说过:CoE 的 PDO 映射对象编号和 CANopen 完全相同,映射项格式也一致——三元组(index.subindex.bitlen)。所以映射表可以是一份,差异只在"怎么把映射的数据搬到总线":
工程含义:
- 映射配置逻辑复用——0x1600/0x1A00 的 SDO 改写(先清项数→逐项写→收尾,第 13 篇第 5 节)两种栈代码几乎一样;
- 映射缓冲区双份的时机——应用写"影子区",在 SYNC(CANopen)或 Sync0(EtherCAT)时刻原子切到"发送区",见第 8 节;
- 8 字节边界由适配层吸收——CANopen 侧组帧截断到 8 字节(超过的拆多帧或报错),EtherCAT 侧无此限制。
7. 状态机三层分离:网络 / CiA402 / 电机控制
双协议产品最常见的架构病是把网络状态机当驱动状态机用。正确姿势是三层各管各的:
| 状态机 | 由谁维护 | 决定什么 | 典型状态 |
|---|---|---|---|
| 网络层 | 协议栈(NMT / AL 状态机) | 过程数据通没通 | CANopen: Op/PreOp/Stopped;EtherCAT: Init→PreOp→SafeOp→Op |
| 驱动层 | 应用(CiA402) | 电机能不能动 | disabled→Ready→Switched on→Op enabled |
| 电机控制层 | FOC/伺服内部 | 闭环怎么跑 | 脱机/开环/闭环/故障 |
三个常见错误:
- NMT Op 当"已使能"——CANopen 上节点进 Op 只表示 PDO 通了,电机没使能;EtherCAT 进 OP 同理,还得走 6040h 命令(第 13 篇使能时序);
- 网络断线直接清 CiA402 状态——应该置"网通=0"让应用按策略处理(保持使能 or 快停),而不是粗暴重置状态机;
- EtherCAT AL 状态机被当 NMT 用——AL 的 SafeOp/Op 是"数据分级",不是"节点开关",映射到应用层就是两个信号:
net_ready(配置完成)和data_valid(过程数据有效)。
统一做法:网络层就绪 → 置"网通"标志;应用层只看这个标志 + CiA402 状态机,不感知具体协议。
8. 数据一致性:锁与双缓冲
最容易被忽略的一节。进 OP 后同时存在两类访问:SDO(邮箱)低频写(主站改 6060h 模式、读故障)和过程数据高频读写(每周期 6040h/607Ah 下发、6041h/6064h 回报)。同一变量两条通道并发访问,不保护就撕裂。
- 映射缓冲区双份:应用写影子区,SYNC/Sync0 时刻原子切到发送区;接收侧同样,应用从"已就位区"读,避免周期中途读到半个值;
- SDO 写走锁:od_set 内部对目标对象加锁(关中断或自旋),写完解锁——CANopen 和 CoE 的 SDO handler 都走同一条 od_set,锁只有一处;
- 周期对象与配置对象分离:6040h/607Ah 这类"每周期变的"标记为周期对象(SDO 仍可写但应用按周期值优先);6060h 这类"低频变"的标记为配置对象(SDO 写后必须确认生效)。
主站一边周期下发目标位置(PDO),一边邮箱改 6060h 从 CSP 切 CSV。若 OD 无锁:模式切换回调读到的是半新半旧的目标值 → 从站可能用 CSV 突然吃掉一个位置值。双缓冲 + 锁 + "切换后清目标"(第 13 篇第 3 节)三道一起才能兜住。
9. 字节序、位宽与 8 字节边界
三个小问题,一次说清:
- 字节序:CANopen 与 EtherCAT 都是 little-endian(低字节在前),多字节对象(607Ah int32)两边读法一样——天然一致,无需转换。这是统一的最大红利之一;
- 位宽:OD 项按 bit 宽定义(16/32/8),CANopen 侧组帧时不满 8 字节补 0(如 RxPDO 6 字节有效 + 2 字节填充),EtherCAT 侧按映射总长原样走——截断/填充逻辑收在适配层;
- 对齐:EtherCAT 从站 FMMU/SM 一般按 4 字节对齐分组,映射项若出现奇数位宽(bitlen=12 等)适配层要处理——CANopen 没这问题,但为统一,映射项建议按 2/4 字节对齐设计。
10. 交互演示:同一 OD,双协议帧级访问
同一个对象字典,两种总线怎么访问——选协议、选对象、写值,看帧一级的差异:SDO 走配置通道(CAN 8 字节帧 / CoE 邮箱),PDO 走过程数据通道(CAN PDO 帧 / LRW 帧 SM2 区),最终落到同一份数据。
- ① 协议切换:CANopen 的 SDO/PDO 走 CAN 帧(COB-ID + 8 字节);EtherCAT 的 CoE SDO 走邮箱(0x1000 头)、过程数据走 LRW 帧 SM2 区;
- ② 同一 OD 值:SDO 写 6040h=0x000F,两种协议的帧内容不同、落到字典的同一个表项;
- ③ 对比 PDO:CANopen PDO 帧固定 8 字节(有效 6 字节补 0),EtherCAT 只带映射总长 6 字节——帧效率差异一眼可见。
11. 可编译 C 代码 + 避坑清单
下面是 OD 核心 + 双栈适配骨架(可直接编译测试),覆盖:表项定义、od_get/od_set 实现、CANopen SDO handler 与 CoE SDO handler 收口、映射缓冲区双份示意。
避坑清单:
| 坑 | 现象 | 对策 |
|---|---|---|
| OD 各自维护两份 | 加一个对象改两处,漏改则双协议行为不一致 | 抽公共 OD 核心,双栈只调 od_get/set(第 4~5 节) |
| NMT/AL 状态当 CiA402 用 | "进 Op 就以为使能了",电机不动还查半天 | 三层状态机分离,网络层只置"网通"标志(第 7 节) |
| SDO 与 PDO 并发写同一变量 | 值撕裂、模式切换吃进半新目标 | 双缓冲 + od_set 锁 + 切换后清目标(第 8 节) |
| 忽略字节序 | 多字节对象在两种总线读出不同 | 两边都是 little-endian,天然一致;别加多余 swap(第 9 节) |
| 映射按协议分开写 | 0x1600 逻辑两份,改映射漏一处 | 同一张映射表(编号/三元组格式一致),适配层只差搬运方式(第 6 节) |
| CAN 8 字节截断处理在各处散落 | 映射超过 8 字节后各协议行为不一 | 截断/填充收口在适配层,OD 层无感(第 6/9 节) |
一句话收尾:对象字典是同源的(CiA 301/402 + CoE),标准层面不存在"统一"问题;软件层面把"OD 核心"抽出来当中间层,应用一次编写、双协议复用,差异全部收在适配层——这正是汇川等厂商"一套固件换协议板卡"的架构。第 13 篇的使能时序、本专题的 PDO/SM·FMMU/SOEM 篇,都是这套架构下具体协议的落地。