CoE - CANopen 协议
CoE 是 EtherCAT 中最常用最广泛的应用层协议,将 CANopen 的对象字典和通信机制移植到 EtherCAT 上,掌握 CoE 是 EtherCAT 开发的基础。
协议简介
官方说明:
- 应用范围:I/O 组件、驱动、编码器、比例阀、液压控制器等
- 兼容机制:对象字典、PDO、SDO、网络管理
- 固件复用:绝大部分 CANopen 固件可重复使用
- 扩展性:可选扩展对象以利用 EtherCAT 带宽
适用设备
- 数字/模拟 I/O 模块
- 伺服驱动器(CiA 402)
- 步进电机控制器
- 编码器
- 传感器
- 变频器
- 比例阀
- 液压控制器
核心概念
对象字典 (Object Dictionary)
对象字典是 CoE 的核心,使用 16 位索引 + 8 位子索引寻址:
- 0x0000 — 保留
- 0x0001 - 0x0FFF — 数据类型定义(UNSIGNED8, INTEGER16 等)
- 0x1000 - 0x1FFF — 通信参数(设备类型、同步管理器配置)
- 0x2000 - 0x5FFF — 厂商特定参数(设备专有功能)
- 0x6000 - 0x9FFF — 标准化设备协议(CiA 401, CiA 402 等)
- 0xA000 - 0xFFFF — 保留
常用通信对象
| 索引 | 名称 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 0x1000 | Device Type | UNSIGNED32 | RO | 设备类型标识 |
| 0x1001 | Error Register | UNSIGNED8 | RO | 错误寄存器 |
| 0x1008 | Manufacturer Device Name | STRING | RO | 设备名称 |
| 0x1009 | Hardware Version | STRING | RO | 硬件版本 |
| 0x100A | Software Version | STRING | RO | 软件版本 |
| 0x1018 | Identity Object | RECORD | RO | 身份信息(VendorID、ProductCode 等) |
| 0x1C00 | Sync Manager Type | ARRAY | RO | SM 类型配置 |
| 0x1C10-1C13 | SM PDO Assign | ARRAY | RW | SM PDO 分配 |
| 0x1C32-1C33 | SM Synchronization | RECORD | RW | SM 同步配置 |
SDO 通信服务
SDO (Service Data Object) 用于非周期性参数访问,通过 Mailbox 通信实现。
SDO 特点
- 非实时通信:通过 Mailbox 传输,不占用 PDO 周期
- 完全访问:可读取/写入对象字典任意对象
- 分段传输:支持大数据块传输
- 确认机制:有响应确认
代码示例
// 读取设备名称
byte[] name = slave.CoE.SDORead(0x1008, 0);
string deviceName = Encoding.ASCII.GetString(name);
Console.WriteLine($"设备名称: {deviceName}");
// 读取 Identity Object
byte[] identity = slave.CoE.SDORead(0x1018, 0, completeAccess: true);
// 写入参数
slave.CoE.SDOWrite(0x6060, 0, new byte[] { 8 }); // 设置操作模式为 CSP
// 完全访问模式读取
byte[] pdoMapping = slave.CoE.SDORead(0x1600, 0, completeAccess: true);
SDO 访问类型
- Normal Access — 访问单个子索引,用于读写单个参数
- Complete Access — 一次访问整个对象,用于读写数组、记录类型
PDO 映射
PDO (Process Data Object) 用于周期性实时数据交换,直接映射到逻辑地址空间。
PDO 类型
- RxPDO(Receive PDO):主站 → 从站(输出数据)
- TxPDO(Transmit PDO):从站 → 主站(输入数据)
PDO 映射对象
- 0x1600 - 0x17FF — RxPDO Mapping — 定义输出数据映射
- 0x1A00 - 0x1BFF — TxPDO Mapping — 定义输入数据映射
PDO 映射结构
每个 PDO 映射对象包含多个子索引:
0x1600 - RxPDO Mapping 1
├── 0x00: 映射对象数量 (SubIndex 0)
├── 0x01: 第1个映射对象 (Index:SubIndex:BitLength)
├── 0x02: 第2个映射对象
└── ...
映射对象格式(32位):
- Bit 31-16: 对象索引 (Index)
- Bit 15-8: 子索引 (SubIndex)
- Bit 7-0: 位长度 (Bit Length)
示例:伺服驱动器 PDO 映射
以下示例基于真实伺服驱动器 (Ezi-SERVO2 EtherCAT) 的 PDO 配置:
RxPDO (0x1600) — 主站到伺服(4个映射对象,共 96 bits = 12 bytes)
| 子索引 | 名称 | 对象地址 | 位长度 |
|---|---|---|---|
| 0x1600:01 | 控制字 | 0x6040:00 | 16 bits |
| 0x1600:02 | 目标位置 | 0x607A:00 | 32 bits |
| 0x1600:03 | 物理输出 | 0x60FE:01 | 32 bits |
| 0x1600:04 | 探针功能 | 0x60B8:00 | 16 bits |
TxPDO (0x1A00) — 伺服到主站(7个映射对象,共 176 bits = 22 bytes)
| 子索引 | 名称 | 对象地址 | 位长度 |
|---|---|---|---|
| 0x1A00:01 | 状态字 | 0x6041:00 | 16 bits |
| 0x1A00:02 | 实际位置 | 0x6064:00 | 32 bits |
| 0x1A00:03 | 数字输入 | 0x60FD:00 | 32 bits |
| 0x1A00:04 | 错误代码 | 0x603F:00 | 16 bits |
| 0x1A00:05 | 探针状态 | 0x60B9:00 | 16 bits |
| 0x1A00:06 | 探针位置1 | 0x60BA:00 | 32 bits |
| 0x1A00:07 | 探针位置2 | 0x60BC:00 | 32 bits |
PDO Assignment 配置:
| 对象索引 | 说明 | 配置内容 |
|---|---|---|
| 0x1C12 (RxPDO Assignment) | SM2 输出映射 | 1个PDO: 0x1600 |
| 0x1C13 (TxPDO Assignment) | SM3 输入映射 | 1个PDO: 0x1A00 |
代码示例
// 读取 TxPDO 映射
byte[] txPdoMap = slave.CoE.SDORead(0x1A00, 0, completeAccess: true);
// 访问 PDO 数据(通过 SDK)
byte[] outputs = slave.PDO.GetOutputs();
slave.PDO.SetOutputs(new byte[] { 0x0F, 0x00, ... });
同步管理器 (Sync Manager)
SM 负责将 PDO 映射到物理内存区域:
| SM | 类型 | 方向 | 默认地址 | 用途 |
|---|---|---|---|---|
| SM0 | Mailbox | 主站到从站 | 0x1000 | Mailbox Out (SDO, FoE 等) |
| SM1 | Mailbox | 从站到主站 | 0x1080 | Mailbox In (SDO 响应等) |
| SM2 | Process Data | 主站到从站 | 0x1100 | RxPDO (输出) |
| SM3 | Process Data | 从站到主站 | 0x1180 | TxPDO (输入) |
网络管理 (NMT)
CoE 支持 CANopen 网络管理功能:
NMT 状态
- Pre-Operational — 配置状态,可访问 SDO
- Operational — 运行状态,PDO 通信活跃
- Stopped — 停止状态
对象字典访问方法
通过 SDK
// SDO 方式
byte[] data = slave.CoE.SDORead(index, subindex);
slave.CoE.SDOWrite(index, subindex, data);
// PDO 方式(实时周期数据)
byte[] inputs = slave.PDO.GetInputs();
slave.PDO.SetOutputs(outputs);
访问规则
- RO (Read Only) — 只读,如设备类型
- WO (Write Only) — 只写,如某些命令
- RW (Read/Write) — 可读写,如配置参数
- RWW — 可读写,写前需 Pre-OP 状态
- RXPDO — 可通过 RxPDO 映射
- TXPDO — 可通过 TxPDO 映射
完全访问模式
Complete Access 用于一次性读写整个对象(数组或记录类型):
// 读取完整 PDO 映射
byte[] mapping = slave.CoE.SDORead(0x1600, 0, completeAccess: true);
// 写入完整 PDO 映射
slave.CoE.SDOWrite(0x1600, 0, mappingData, completeAccess: true);
优势:
- 原子操作,保证数据一致性
- 减少通信次数
- 适用于数组/记录类型
应用示例
读取设备信息
// 读取设备类型
byte[] typeData = slave.CoE.SDORead(0x1000, 0);
uint deviceType = BitConverter.ToUInt32(typeData, 0);
// 读取设备名称
byte[] nameData = slave.CoE.SDORead(0x1008, 0);
string deviceName = Encoding.ASCII.GetString(nameData);
// 读取版本信息
byte[] hwVersion = slave.CoE.SDORead(0x1009, 0);
byte[] swVersion = slave.CoE.SDORead(0x100A, 0);
Console.WriteLine($"设备: {deviceName}");
Console.WriteLine($"类型: 0x{deviceType:X8}");
Console.WriteLine($"硬件版本: {Encoding.ASCII.GetString(hwVersion)}");
Console.WriteLine($"软件版本: {Encoding.ASCII.GetString(swVersion)}");
配置 PDO 映射
// 1. 切换到 Pre-OP 状态
master.RequestState(SlaveState.PreOP);
// 2. 清空现有映射
slave.CoE.SDOWrite(0x1C12, 0, new byte[] { 0 }); // SM2 PDO Assign
slave.CoE.SDOWrite(0x1C13, 0, new byte[] { 0 }); // SM3 PDO Assign
// 3. 配置新映射
// RxPDO: 0x6040 (控制字, 16bit)
slave.CoE.SDOWrite(0x1600, 0, new byte[] { 0 }); // 清空
slave.CoE.SDOWrite(0x1600, 1, BitConverter.GetBytes(0x60400010)); // 0x6040:00, 16bits
slave.CoE.SDOWrite(0x1600, 0, new byte[] { 1 }); // 设置数量
// 4. 分配 SM
slave.CoE.SDOWrite(0x1C12, 1, BitConverter.GetBytes((ushort)0x1600));
slave.CoE.SDOWrite(0x1C12, 0, new byte[] { 1 });
// 5. 切换到 OP 状态
master.RequestState(SlaveState.OP);
与其他协议对比
| 特性 | CoE | SoE | EoE |
|---|---|---|---|
| 来源 | CANopen | SERCOS | Ethernet |
| 实时性 | 高 | 极高 | 中 |
| 复杂度 | 中 | 高 | 低 |
| 应用 | 通用 | 高端运动控制 | 网络设备 |
| 设备支持 | 最广泛 | 高端设备 | 网络设备 |