主站对象字典 (Master OD)
符合 ETG.1510 标准的主站对象字典,提供主站身份信息和诊断数据。
主站对象字典按照 ETG.1510 规范实现,使用标准 CoE 索引约定。配合邮箱网关使用,可实现远程诊断工具对主站的标准访问。
master/diagnostics_ext.h 只把两个快路径接了出来:0x1018 子索引 1..4(GetMasterIdentity)与 0xF120 子索引 0(GetMasterDiagData)。其余索引在 C SDK 上没有可用实现,读取直接返回 -4;native 的 dx_sdo_read / dx_sdo_write 入口无条件拒绝 slave_index == 0,所以经它转发到主站 OD 的路由恒失败。
因此下表描述的是主站对象字典本身(配合邮箱网关,外部 CoE 诊断工具可按标准访问),其中 0x8nnn / 0x9nnn / 0xAnnn 无法用 C SDK 读到。把这套 ETG.1510 对象实装成 SDK API 的是 C# 与 Python 两腿,需要程序内读取从站配置 / 信息 / 诊断对象时请用它们。
另外 0x1009(Hardware Version)的取值以 native 主站对象字典为准,是常量 "1.0"(native mailbox_gateway.c;Java 的 MasterObjectDictionary 同样返回 "1.0")。只有 C# 与 Python 例外,返回进程位宽 "x64" / "x86"(MasterDiagnostics_ETG1510.cs / master_od.py),属少数派实现。
对象索引总览
| 索引 | 名称 | 类别 | 子索引数 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x1000 | Device Type | 标准对象 | 0 | 只读 | 0x00001389(EtherCAT Master) |
| 0x1008 | Device Name | 标准对象 | 0 | 只读 | "Darra EtherCAT Master" |
| 0x1009 | Hardware Version | 标准对象 | 0 | 只读 | "1.0" |
| 0x100A | Software Version | 标准对象 | 0 | 只读 | SDK 版本号 |
| 0x1018 | Identity Object | 标准对象 | 4 | 只读 | 主站身份信息 |
| 0x8nnn | Configuration Data | 从站配置 | 40 | 只读 | 第 n 个从站的配置数据(地址、名称、厂商、邮箱等) |
| 0x9nnn | Information Data | 从站信息 | 32 | 只读 | 第 n 个从站的检测信息(实际地址、厂商 ID、DL Status) |
| 0xAnnn | Diagnosis Data | 从站诊断 | 19 | 部分可写 | 第 n 个从站的诊断数据(AL Status、错误计数、链路等) |
| 0xF002 | Detect Modules | 检测命令 | 3 | 只读 | 从站扫描状态 |
| 0xF120 | Master Diag Data | 主站诊断 | 16 | 只读 | 主站诊断数据(丢帧计数、帧频、状态) |
Identity Object (0x1018)
| 子索引 | 名称 | 类别 | 类型 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x01 | Vendor ID | 标准对象 | uint32 | 只读 | 厂商 ID |
| 0x02 | Product Code | 标准对象 | uint32 | 只读 | 产品代码 |
| 0x03 | Revision Number | 标准对象 | uint32 | 只读 | 修订号 |
| 0x04 | Serial Number | 标准对象 | uint32 | 只读 | 序列号 |
Configuration Data (0x8nnn) — 从站配置
从站 n 的配置数据索引为 0x8000 | n(n = 1..slave_count,从站号 1-based 直接落在低 12 位)。例如从站 1 = 0x8001,从站 2 = 0x8002。
| 子索引 | 名称 | 类别 | 类型 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x00 | Number of Entries | 从站配置 | byte | 只读 | 固定 40 |
| 0x01 | Fixed Station Address | 从站配置 | uint16 | 只读 | 配置地址(ConfigAddr) |
| 0x02 | Type | 从站配置 | string | 只读 | 设备类型字符串 |
| 0x03 | Name | 从站配置 | string | 只读 | 设备名称 |
| 0x04 | Device Type | 从站配置 | uint32 | 只读 | 设备类型编码 |
| 0x05 | Vendor ID | 从站配置 | uint32 | 只读 | 厂商 ID |
| 0x06 | Product Code | 从站配置 | uint32 | 只读 | 产品代码 |
| 0x07 | Revision Number | 从站配置 | uint32 | 只读 | 修订号 |
| 0x08 | Serial Number | 从站配置 | uint32 | 只读 | 序列号 |
| 0x21 | Mailbox Out Size | 从站配置 | uint16 | 只读 | SM0 邮箱输出大小 |
| 0x22 | Mailbox In Size | 从站配置 | uint16 | 只读 | SM1 邮箱输入大小 |
| 0x23 | Link Status | 从站配置 | byte | 只读 | 端口链路状态 |
| 0x24 | Link Preset | 从站配置 | byte | 只读 | 端口链路预设 |
| 0x25 | Flags | 从站配置 | byte | 只读 | 从站标志(Bit3: 热连接组成员) |
| 0x26 | Port Physics | 从站配置 | uint16 | 只读 | 端口物理类型 |
| 0x27 | Mailbox Protocols | 从站配置 | uint16 | 只读 | 支持的邮箱协议(CoE=0x04, FoE=0x08 等) |
| 0x28 | Diag History Supported | 从站配置 | byte | 只读 | 是否支持 0x10F3 诊断历史对象 |
Information Data (0x9nnn) — 从站检测信息
每个从站对应一个 0x9nnn 对象,包含运行时检测到的实际信息。
从站 n 的检测信息索引为 0x9000 | n(n = 1..slave_count,从站号 1-based 直接落在低 12 位)。例如从站 1 = 0x9001,从站 2 = 0x9002。
| 子索引 | 名称 | 类别 | 类型 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x00 | Number of Entries | 从站信息 | byte | 只读 | 固定 32 |
| 0x01 | Station Address | 从站信息 | uint16 | 只读 | 实际配置地址 |
| 0x05 | Vendor ID | 从站信息 | uint32 | 只读 | 检测到的厂商 ID |
| 0x06 | Product Code | 从站信息 | uint32 | 只读 | 检测到的产品代码 |
| 0x07 | Revision Number | 从站信息 | uint32 | 只读 | 检测到的修订号 |
| 0x08 | Serial Number | 从站信息 | uint32 | 只读 | 检测到的序列号 |
| 0x20 | DL Status | 从站信息 | uint16 | 只读 | DL Status 寄存器(ESC 0x0110-0x0111) |
Diagnosis Data (0xAnnn) — 从站诊断
每个从站对应一个 0xAnnn 对象,包含实时诊断数据。每次读取时自动更新。
从站 n 的诊断数据索引为 0xA000 | n(n = 1..slave_count,从站号 1-based 直接落在低 12 位)。例如从站 1 = 0xA001,从站 2 = 0xA002。
| 子索引 | 名称 | 类别 | 类型 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x00 | Number of Entries | 从站诊断 | byte | 只读 | 固定 19 |
| 0x01 | AL Status | 从站诊断 | uint16 | 只读 | 当前 AL 状态 |
| 0x02 | AL Control | 从站诊断 | uint16 | 可写 | 写入时同步到从站 |
| 0x03 | Last AL Status Code | 从站诊断 | uint16 | 只读 | 最近一次 AL 错误码(ESC 0x0134) |
| 0x04 | Link Conn. Status | 从站诊断 | byte | 只读 | 端口链路连接状态 |
| 0x05 | Link Control | 从站诊断 | byte | 只读 | 链路控制(DL Control 寄存器 0x0101) |
| 0x06 | Fixed Addr Conn. Port 0 | 从站诊断 | uint16 | 只读 | Port 0 连接的从站配置地址 |
| 0x07 | Fixed Addr Conn. Port 1 | 从站诊断 | uint16 | 只读 | Port 1 连接的从站配置地址 |
| 0x08 | Fixed Addr Conn. Port 2 | 从站诊断 | uint16 | 只读 | Port 2 连接的从站配置地址 |
| 0x09 | Fixed Addr Conn. Port 3 | 从站诊断 | uint16 | 只读 | Port 3 连接的从站配置地址 |
| 0x0A | Frame Error Port 0 | 从站诊断 | uint32 | 只读 | Port 0 帧错误计数 |
| 0x0B | Frame Error Port 1 | 从站诊断 | uint32 | 只读 | Port 1 帧错误计数 |
| 0x0C | Frame Error Port 2 | 从站诊断 | uint32 | 只读 | Port 2 帧错误计数 |
| 0x0D | Frame Error Port 3 | 从站诊断 | uint32 | 只读 | Port 3 帧错误计数 |
| 0x0E | Cyclic WC Error Counter | 从站诊断 | uint32 | 只读 | 周期性工作计数器错误 |
| 0x0F | Slave Not Present Counter | 从站诊断 | uint32 | 只读 | 从站不在线计数 |
| 0x10 | Abnormal State Change Counter | 从站诊断 | uint32 | 只读 | 异常状态变化计数 |
| 0x11 | Disable Auto Link Control | 从站诊断 | byte | 只读 | 自动链路控制禁用标志 |
| 0x12 | Last CoE/SoE Protocol Error | 从站诊断 | uint32 | 只读 | 最近一次 CoE/SoE 协议错误码 |
| 0x13 | New Diag Messages | 从站诊断 | byte | 只读 | 是否有新诊断消息(通过 0x10F3 检测) |
主站身份
GetMasterIdentity()
BOOL GetMasterIdentity(uint16_t master_index, ec_master_identity_t* identity);
获取主站身份信息。
参数:
master_index(uint16_t) — 主站索引identity(ec_master_identity_t*) — 输出身份信息
返回值:
BOOL— 成功返回TRUE
ec_master_identity_t
typedef struct {
uint32_t VendorId; /* 厂商 ID */
uint32_t ProductCode; /* 产品代码 */
uint32_t RevisionNo; /* 修订号 */
uint32_t SerialNo; /* 序列号 */
char DeviceName[64]; /* 设备名称 */
char HwVersion[32]; /* 硬件版本 */
char SwVersion[32]; /* 软件版本 */
} ec_master_identity_t;
主站诊断数据 (0xF120)
C SDK 只在子索引 0 上接了快路径:diagnostics_etg1510_read() 读 0xF120:00 时调用 GetMasterDiagData(),一次性返回整个 ec_master_diag_data_t(20 字节 = 4 个 uint32 + uint16 + 2 字节对齐填充),不按子索引拆分;其余子索引没有可用实现,读取返回 -4。结构字段与 ETG.1510 子索引的对应关系:
| 偏移 | 字段 | 类别 | 类型 | 读写 | ETG.1510 子索引 | 说明 |
|---|---|---|---|---|---|---|
| 0 | CyclicLostFrames | 主站诊断 | uint32 | 只读 | 01 | 周期性丢帧计数(内核 5 秒滑动窗口值,不是百分比) |
| 4 | AcyclicLostFrames | 主站诊断 | uint32 | 只读 | 02 | 非周期性丢帧计数(内核按超时帧统计,同为 5 秒滑动窗口值) |
| 8 | CyclicFramesPerSec | 主站诊断 | uint32 | 只读 | 03 | 每秒周期帧数 |
| 12 | AcyclicFramesPerSec | 主站诊断 | uint32 | 只读 | 04 | 每秒非周期帧数(内核不填该字段,读数恒 0) |
| 16 | MasterState | 主站诊断 | uint16 | 只读 | 16 | 主站当前 EtherCAT 状态 |
GetMasterDiagData()
BOOL GetMasterDiagData(uint16_t master_index, ec_master_diag_data_t* diag);
获取主站诊断数据(ETG.1510 0xF120)。
参数:
master_index(uint16_t) — 主站索引diag(ec_master_diag_data_t*) — 输出诊断数据
返回值:
BOOL— 成功返回TRUE
ec_master_diag_data_t
typedef struct {
uint32_t CyclicLostFrames; /* 周期帧丢失计数 (内核 5 秒滑动窗口值) */
uint32_t AcyclicLostFrames; /* 非周期帧丢失计数 (内核按超时帧统计, 同为 5 秒滑动窗口值) */
uint32_t CyclicFramesPerSec; /* 每秒周期帧数 */
uint32_t AcyclicFramesPerSec; /* 每秒非周期帧数 (内核不填该字段, 读数恒 0) */
uint16_t MasterState; /* 主站当前状态 */
} ec_master_diag_data_t;
0xF200 — 诊断接口控制
| 子索引 | 名称 | 类别 | 类型 | 读写 | 说明 |
|---|---|---|---|---|---|
| 0x00 | Number of Entries | 诊断控制 | byte | 只读 | 固定 16 |
| 0x10 | Reset Diag Info | 诊断控制 | byte | 可写 | 写入 1 复位所有从站和主站的诊断计数器(上升沿触发) |
ETG.1510 索引常量
master/diagnostics_ext.h 中定义的对象索引宏:
| 宏 | 值 | 对象 |
|---|---|---|
| ETG1510_DEVICE_TYPE | 0x1000 | Device Type |
| ETG1510_DEVICE_NAME | 0x1008 | Manufacturer Device Name |
| ETG1510_HW_VERSION | 0x1009 | Hardware Version |
| ETG1510_SW_VERSION | 0x100A | Software Version |
| ETG1510_IDENTITY | 0x1018 | Identity Object |
| ETG1510_CONFIG_DATA_BASE | 0x8000 | 从站配置数据基地址 |
| ETG1510_INFO_DATA_BASE | 0x9000 | 从站信息数据基地址 |
| ETG1510_DIAG_DATA_BASE | 0xA000 | 从站诊断数据基地址 |
| ETG1510_DETECT_MODULES | 0xF002 | Detect Modules Command |
| ETG1510_MASTER_DIAG_DATA | 0xF120 | Master Diag Data |
| ETG1510_DIAG_INTERFACE_CTRL | 0xF200 | Diag Interface Control |
通用 OD 访问
下列函数 (声明于 master/diagnostics_ext.h) 只转发两个快路径: 0x1018 子索引 1..4 路由到 GetMasterIdentity, 0xF120 子索引 0 路由到 GetMasterDiagData。 其余索引在 C SDK 上没有可用实现: native 的 dx_sdo_read / dx_sdo_write 入口无条件拒绝 slave_index == 0, 经它路由到主站 OD 恒失败, 读写分别返回 -4 与 -2/-4, 不会与"从站没响应 / 索引不存在"混在一起。 这两个 SDK 层函数直接以主站索引调用,不需要任何上下文句柄。
diagnostics_etg1510_read()
int diagnostics_etg1510_read(uint16_t master_index, uint16_t index, uint8_t subindex,
uint8_t* outBuf, int bufSize);
通过 ETG.1510 OD 读取主站对象 (SDO Upload 包装).
参数:
master_index(uint16_t) — 主站索引index(uint16_t) — OD 索引 (0x1000-0xFFFF)subindex(uint8_t) — OD 子索引outBuf(uint8_t*) — 输出缓冲区bufSize(int) — 缓冲区大小
返回值:
int— 实际写入字节数;-1=参数错误;-3=缓冲区不足;-4=该索引无可用实现(除 0x1018:1..4 与 0xF120:0 外的全部索引)
diagnostics_etg1510_write()
int diagnostics_etg1510_write(uint16_t master_index, uint16_t index, uint8_t subindex,
const uint8_t* data, int len);
通过 ETG.1510 OD 写入主站对象 (SDO Download 包装). 目标本来是 0xF200 Diag Interface Control, 0xF002 Detect Modules 等可写主站对象; 但 native 拒绝 slave_index == 0, 当前 C SDK 上留不出可用的写通道(见下返回值)。
返回值:
int—0成功(当前没有索引能走到,见下);-1参数错误;-2SDO 失败 (native 拒绝 / Abort / 超时);-4薄壳不可用 (入口未就绪,未下发)。- 当前没有任何索引能走到成功分支: native 的
dx_sdo_write入口无条件拒绝slave_index == 0。
示例:
/* 写 0xF200:0x10 = 1 复位主站诊断计数 */
uint8_t one = 1;
diagnostics_etg1510_write(mi, 0xF200, 0x10, &one, 1);
/* 读 0x100A 软件版本字符串 */
uint8_t buf[32] = {0};
int n = diagnostics_etg1510_read(mi, 0x100A, 0x00, buf, sizeof(buf));
if (n > 0) printf("SW Version: %s\n", buf);
AL 状态码分类
diagnostics_classify_al_error()
DarraAlErrorClass diagnostics_classify_al_error(uint16_t alStatusCode);
按 ETG.1020 把 AL Status Code 归到大类, 方便 UI 分组展示告警。
返回值:
DarraAlErrorClass— 错误大类枚举
相关结构:
typedef enum {
AL_ERR_NONE = 0,
AL_ERR_GENERAL = 1, /* 0x0001-0x000F 通用错误 */
AL_ERR_STARTUP = 2, /* 0x0010-0x001F 启动 */
AL_ERR_MAILBOX = 3, /* 0x0020-0x002F 邮箱 */
AL_ERR_WATCHDOG = 4, /* 0x0030-0x003F 看门狗 */
AL_ERR_SYNC = 5, /* 0x0040-0x004F 同步 */
AL_ERR_DC = 6, /* 0x0050-0x005F DC */
AL_ERR_MBX_EOE = 7, /* 0x0060-0x006F EoE 邮箱 */
AL_ERR_UNKNOWN = 99
} DarraAlErrorClass;
完整示例
#include "ethercat.h"
#include <stdio.h>
int main(void)
{
uint16_t master = Initialize();
uint16_t slave_count = SetNetwork(master, "\\Device\\NPF_{...}", "");
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);
/* ===== 读取主站身份 (0x1018) ===== */
ec_master_identity_t id;
if (GetMasterIdentity(master, &id)) {
printf("=== 主站信息 (0x1018) ===\n");
printf("设备名称: %s\n", id.DeviceName);
printf("软件版本: %s\n", id.SwVersion);
printf("硬件版本: %s\n", id.HwVersion);
printf("VendorId: 0x%08X\n", id.VendorId);
printf("ProductCode: 0x%08X\n", id.ProductCode);
printf("RevisionNo: 0x%08X\n", id.RevisionNo);
printf("SerialNo: 0x%08X\n", id.SerialNo);
}
/* ===== 读取主站诊断数据 (0xF120) ===== */
/* 字段对应 ETG.1510 子索引 01/02/03/04/16; 丢帧计数是内核 5 秒滑动窗口值, 不是百分比 */
ec_master_diag_data_t diag;
if (GetMasterDiagData(master, &diag)) {
printf("\n=== 主站诊断 (0xF120) ===\n");
printf("周期帧频率: %u Hz\n", diag.CyclicFramesPerSec);
printf("非周期帧频率: %u Hz\n", diag.AcyclicFramesPerSec);
printf("周期帧丢失计数: %u\n", diag.CyclicLostFrames);
printf("主站状态: 0x%04X\n", diag.MasterState);
}
/* ===== 遍历所有从站诊断数据 (0xAnnn) ===== */
/* slave_count 来自 SetNetwork 返回值 */
printf("\n=== 从站诊断 (0xAnnn) ===\n");
for (uint16_t i = 1; i <= slave_count; i++) {
char name[128];
GetSlaveName(master, i, name, sizeof(name));
/* 读取 AL Status 和错误码 */
uint16_t al_status = GetSlaveState(master, i);
uint16_t al_code = GetSlaveALStatusCode(master, i);
/* 读取链路质量 */
int16_t link_quality = GetSlaveLinkQuality(master, i);
printf("从站 %d [%s]: AL=0x%04X, Code=0x%04X, 链路=%d%%\n",
i, name, al_status, al_code, link_quality);
}
/* ===== PDO 丢帧统计(按组) ===== */
printf("\n=== PDO 丢帧统计 ===\n");
for (uint8_t g = 0; g < 8; g++) {
uint32_t total = 0, consecutive = 0, max_consecutive = 0;
GetPDOFrameLossStats(master, g, &total, &consecutive, &max_consecutive);
if (total > 0 || GetGroupSlaveCount(master, g) > 0) {
printf("组 %d: 累计=%u, 连续=%u, 最大连续=%u\n",
g, total, consecutive, max_consecutive);
}
}
Stop(master);
Dispose(master);
return 0;
}