7.5.18 EtherCAT 用户手册
1. 概述
本模块基于开源 SOEM(Simple Open EtherCAT Master)库,针对搭载 FreeRTOS 的嵌入式 MCU 平台(S100)进行了移植与适配。主要特性如下:
本模块基于开源 SOEM(Simple Open EtherCAT Master)库,针对搭载 FreeRTOS 的嵌入式 MCU 平台(S600)进行了移植与适配。主要特性如下:
| 特性 | 说明 |
|---|---|
| EtherCAT 主站 | 实现完整的 EtherCAT MainDevice(主站)功能 |
| 实时调度 | 通过 FreeRTOS 任务实现周期性过程数据收发 |
| 硬件适配 | 通过 McalCdd/Ethernet(Eth.h)驱动底层以太网收发 |
| 邮箱协议 | 支持 CoE、FoE、EoE、SoE、AoE 邮箱协议 |
| 分布式时钟 | 支持 DC 同步,提供 PI 控制器进行时钟对齐 |
| 从站诊断 | 支持从站状态检测、自动恢复与重配 |
| Shell 接口 | 提供交互式命令行测试入口 |
2. 软件架构
| 层级 | 模块 / 文件 | 说明 | 对外接口 |
|---|---|---|---|
| 用户应用层 | ec_sample/src/ec_sample.c | EtherCAT 完整运行示例,包含实时任务、检测任务与启动流程 | EtherCAT_Sample_Start() / Shell Ec_Sample_Test |
| 用户应用层 | slaveinfo/src/slaveinfo.c | 从站信息扫描、SDO 读写工具 | EtherCAT_SlaveInfo_Start() / Shell Ec_SlaveInfo、Ec_SlaveOP |
| SOEM 核心层 | core/src/ec_main.c | 主站初始化、状态机、邮箱收发、EEPROM 读写 | ecx_init / ecx_readstate / ecx_writestate 等 |
| SOEM 核心层 | core/src/ec_config.c | 从站自动枚举与 PDO 映射配置 | ecx_config_init / ecx_config_map_group |
| SOEM 核心层 | core/src/ec_coe.c | CoE(CANopen over EtherCAT)SDO 读写、对象字典查询 | ecx_SDOread / ecx_SDOwrite / ecx_readODlist |
| SOEM 核心层 | core/src/ec_dc.c | 分布式时钟(DC)配置与同步 | ecx_configdc |
| SOEM 核心层 | core/src/ec_foe.c | FoE(File over EtherCAT)文件传输 | ecx_FOEread / ecx_FOEwrite |
| SOEM 核心层 | core/src/ec_eoe.c | EoE(Ethernet over EtherCAT)以太网隧道 | ecx_EOEsend / ecx_EOErecv |
| SOEM 核心层 | core/src/ec_soe.c | SoE(Servo over EtherCAT)伺服参数访问 | ecx_SoEread / ecx_SoEwrite |
| SOEM 核心层 | core/src/ec_base.c | 底层 EtherCAT 帧构造与收发 | ecx_send_processdata / ecx_receive_processdata |
| SOEM 核心层 | core/src/ec_print.c | 错误码与状态码转可读字符串 | ec_ALstatuscode2string / ecx_elist2string |
| OSAL 抽象层 | osal/src/osal.c | OS 时间、任务、互斥锁的 FreeRTOS 适配实现 | osal_usleep / osal_mutex_lock 等 |
| OSHW/NIC 层 | oshw/src/nicdrv.c | 网络收发适配,将 SOEM 帧操作映射到 McalCdd/Ethernet API | ecx_setupnic / ecx_outframe / ecx_inframe |
| OSHW/NIC 层 | oshw/src/oshw.c | 字节序转换工具(大小端互转) | oshw_htons / oshw_ntohs |
| 硬件层 | 以太网控制器 Controller 0 | 物理网口,收发 EtherCAT 帧(ETH 类型 0x88A4) | McalCdd Eth_Transmit / Eth_Receive_ethercat |
3. 目录结构
McalCdd/EtherCAT/
├── SConscript # 顶层构建脚本
├── README.md # 原始说明
├── core/ # SOEM 核心源码
│ ├── SConscript
│ ├── src/
│ │ ├── ec_main.c # 主功能:初始化、状态机、邮箱
│ │ ├── ec_config.c # 从站自动配置(PDO 映射)
│ │ ├── ec_coe.c # CoE(CANopen over EtherCAT)SDO 操作
│ │ ├── ec_dc.c # 分布式时钟(DC)配置
│ │ ├── ec_eoe.c # EoE(Ethernet over EtherCAT)
│ │ ├── ec_foe.c # FoE(File over EtherCAT)
│ │ ├── ec_soe.c # SoE(Servo over EtherCAT)
│ │ ├── ec_base.c # 底层帧操作
│ │ └── ec_print.c # 调试打印工具
│ └── inc/
│ ├── nicdrv.h # NIC 驱动接口
│ ├── osal.h # OSAL 接口
│ ├── osal_defs.h # OSAL 类型定义
│ ├── oshw.h # OS/HW 工具接口
│ └── soem/
│ ├── soem.h # 总头文件(一键包含)
│ ├── ec_main.h # 数据结构与主 API 声明
│ ├── ec_type.h # 基本类型、枚举、宏
│ ├── ec_options.h # 编译期配置参数
│ ├── ec_coe.h # CoE API 声明
│ ├── ec_config.h # 配置 API 声明
│ ├── ec_dc.h # DC API 声明
│ ├── ec_eoe.h # EoE API 声明
│ ├── ec_foe.h # FoE API 声明
│ ├── ec_soe.h # SoE API 声明
│ └── ec_print.h # 打印工具声明
├── osal/ # FreeRTOS OSAL 实现
│ ├── SConscript
│ └── src/osal.c
└── oshw/ # 硬件驱动适配
├── SConscript
└── src/
├── nicdrv.c # McalCdd/Ethernet NIC 驱动
└── oshw.c # 字节序工具
samples/EtherCAT/
├── SConscript
├── ec_sample/
│ ├── SConscript
│ └── src/ec_sample.c # 基本运行示例
└── slaveinfo/
├── SConscript
└── src/slaveinfo.c # 从站信息扫描示例
4. 关键数据结构
4.1 ecx_contextt — 主站上下文
所有 SOEM API 均以此结构体的指针作为第一个参数。应用层需声明一个该类型的静态变量:
static ecx_contextt ctx;
主要成员:
| 成员 | 类型 | 说明 |
|---|---|---|
port | ecx_portt | 网络端口(含帧缓冲、互斥锁) |
slavelist[] | ec_slavet[EC_MAXSLAVE] | 从站列表,下标 0 为广播虚拟从站 |
slavecount | int | 实际发现的从站数量 |
grouplist[] | ec_groupt[EC_MAXGROUP] | 从站分组信息 |
ecaterror | boolean | 是否存在 EtherCAT 错误 |
DCtime | int64 | 从站 DC 时间(ns) |
ENI | ec_enit * | EtherCAT 网络信息(可选) |
FOEhook | 函数指针 | FoE 数据回调钩子 |
EOEhook | 函数指针 | EoE 数据回调钩子 |
manualstatechange | int | 0=自动状态转换,1=手动控制 |
overlappedMode | boolean | 重叠模式(适用于 TI ESC) |
packedMode | boolean | 紧凑 PDO 映射模式 |
4.2 ec_slavet — 从站信息
| 成员 | 说明 |
|---|---|
state | 当前从站状态(ec_state 枚举值) |
ALstatuscode | AL 状态码,错误时提供具体原因 |
configadr | 配置地址(站地址) |
eep_man / eep_id / eep_rev | EEPROM 中的厂商/产品/版本信息 |
Obytes / Ibytes | 输出/输入字节数 |
outputs / inputs | 指向 IOmap 中对应区域的指针 |
hasdc | 是否支持分布式时钟 |
CoEdetails | CoE 功能标志 位 |
mbx_proto | 支持的邮箱协议位掩码 |
islost | 从站是否已丢失连接 |
name | 从站可读名称字符串 |
4.3 ec_groupt — 从站分组
| 成员 | 说明 |
|---|---|
Obytes / Ibytes | 整组输出/输入字节数 |
outputsWKC / inputsWKC | 期望的工作计数器值 |
docheckstate | 需要执行状态检查标志 |
hasdc | 组内是否有 DC 从站 |
5. 从站状态机
EtherCAT 从站遵循以下状态机(ESM):
INIT (0x01)
│
▼ I→P
PRE_OP (0x02)
│
▼ P→S
SAFE_OP (0x04)
│
▼ S→O
OPERATIONAL (0x08)
各状态对应枚举值:
| 枚举值 | 数值 | 说明 |
|---|---|---|
EC_STATE_NONE | 0x00 | 无效状态 |
EC_STATE_INIT | 0x01 | 初始化 |
EC_STATE_PRE_OP | 0x02 | 预运行 |
EC_STATE_BOOT | 0x03 | 引导状态(固件更新) |
EC_STATE_SAFE_OP | 0x04 | 安全运行(只读 PDO) |
EC_STATE_OPERATIONAL | 0x08 | 运行(全双工 PDO) |
EC_STATE_ERROR | 0x10 | 错误/ACK |
写状态示例:
/* 请求所有从站进入 OPERATIONAL */
ctx.slavelist[0].state = EC_STATE_OPERATIONAL;
ecx_writestate(&ctx, 0);
/* 等待确认(超时 2s) */
ecx_statecheck(&ctx, 0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE);
6. API 参考
6.1 初始化与关闭
ecx_init
int ecx_init(ecx_contextt *context, const char *ifname);
初始化 EtherCAT 主站,打开底层网络接口。
| 参数 | 说明 |
|---|---|
context | 主站上下文指针 |
ifname | 网络接口名称(嵌入式平台填 NULL,默认使用控制器 0) |
返回值:成功返回非零值,失败返回 0。
int rv = ecx_init(&ctx, NULL);
if (rv == 0) {
/* 初始化失败 */
}
ecx_close
void ecx_close(ecx_contextt *context);
关闭主站,释放底层网络资源。
6.2 从站配置
ecx_config_init
int ecx_config_init(ecx_contextt *context);
扫描总线上的所有从站,将其配置至 PRE_OP 状态,填充 ctx.slavelist[] 和 ctx.slavecount。
返回值:发现的从站数量。
ecx_config_map_group
int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group);
为指定分组的所有从站配置 PDO 映射,将输入/输出区域映射到 pIOmap 缓冲区。
| 参数 | 说明 |
|---|---|
context | 主站上下文 |
pIOmap | IO 映射缓冲区(建议 4096 字节) |
group | 分组编号(0 表示默认分组) |
返回值:映射成功的 IO 字节总数。
static uint8 IOmap[4096];
int map_result = ecx_config_map_group(&ctx, IOmap, 0);
配置完成后可获取期望工作计数器:
ec_groupt *group = &ctx.grouplist[0];
int expectedWKC = (group->outputsWKC * 2) + group->inputsWKC;
ecx_reconfig_slave
int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout);
重新配置指定从站(用于故障恢复)。成功返回值 >= EC_STATE_PRE_OP。
ecx_recover_slave
int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout);
恢复已丢失连接的从站。
6.3 状态管理
ecx_readstate
int ecx_readstate(ecx_contextt *context);
读取所有从站当前状态,更新 ctx.slavelist[i].state。
返回值:最低优先级从站的状态值。
ecx_writestate
int ecx_writestate(ecx_contextt *context, uint16 slave);
向指定从站写入 ctx.slavelist[slave].state 中设置的目标状态。slave=0 时广播到所有从站。
ecx_statecheck
uint16 ecx_statecheck(ecx_contextt *context, uint16 slave, uint16 reqstate, int timeout);
轮询等待从站达到目标状态,直到超时。
| 参数 | 说明 |
|---|---|
slave | 从站编号,0=所有 |
reqstate | 期望的目标状态 |
timeout | 超时时间(微秒),建议使用 EC_TIMEOUTSTATE(2s) |
返回值:当前达到的状态值。
6.4 过程数据(PDO)
PDO 通信是 EtherCAT 实时数据交换的核心,须在专用实时任务中周期性调用。
ecx_send_processdata
int ecx_send_processdata(ecx_contextt *context);
发送所有分组的过程数据帧(将 outputs 区域内容发送到从站)。
ecx_receive_processdata
int ecx_receive_processdata(ecx_contextt *context, int timeout);
接收过程数据响应帧(将从站输入更新至 inputs 区域)。