【原创】IgH EtherCAT主站详解(五)--Master、Slave及Domain模块介绍
一、Master 模块
3.1 — master.c / master.h — EtherCAT 主站核心
概览
Master 模块是什么?
Master 模块是 IgH EtherCAT 主站的核心中枢,负责管理整个 EtherCAT 通信栈的生命周期。每个 EtherCAT 主站实例对应一个 ec_master 结构体,它管理从站、域 (Domain)、数据报 (Datagram)、状态机 (FSM) 以及与网卡设备的交互。
核心职责
生命周期管理: ORPHANED → IDLE → OPERATION 三阶段切换
线程管理: IDLE 线程(扫描/配置)和 OPERATION 线程(实时通信)
Datagram 收发: 组帧发送、接收匹配、WKC 校验
从站管理: 总线扫描、配置下发、状态监控
DC 同步: 参考时钟同步、漂移补偿
Master 三阶段生命周期
技术详情
线程模型
| 线程 | 运行阶段 | 职责 | 优先级 |
|---|---|---|---|
EtherCAT-IDLE |
IDLE | 从站扫描、配置、FSM 执行、Datagram 收发 | 普通 |
EtherCAT-OP |
OPERATION | FSM 执行、外部 Datagram 注入、统计输出 | 实时 |
EtherCAT-EoE |
IDLE/OP | EoE 虚拟网络接口数据处理(可选) | 普通 |
关键数据结构
ec_master 结构体字段
| 字段 | 类型 | 说明 |
|---|---|---|
index |
unsigned int |
主站索引号 |
reserved |
unsigned int |
是否被应用程序占用 |
phase |
ec_master_phase_t |
当前阶段 (ORPHANED/IDLE/OPERATION) |
devices[2] |
ec_device_t |
主/备网卡设备 |
fsm |
ec_fsm_master_t |
Master FSM 状态机 |
fsm_datagram |
ec_datagram_t |
FSM 专用数据报 |
slaves |
ec_slave_t * |
从站数组 |
slave_count |
unsigned int |
从站总数 |
configs |
struct list_head |
从站配置列表 |
domains |
struct list_head |
域列表 |
sii_images |
struct list_head |
SII 镜像列表 |
datagram_queue |
struct list_head |
数据报发送队列 |
datagram_index |
uint8_t |
当前数据报索引(自动递增) |
ext_datagram_ring[32] |
ec_datagram_t |
外部数据报环形缓冲(大小 EC_EXT_RING_SIZE=32) |
dc_ref_clock |
ec_slave_t * |
DC 参考时钟从站 |
app_time |
u64 |
应用程序时间(最后一次 ecrt_master_sync 调用) |
thread |
struct task_struct * |
当前主线程 |
send_cb / receive_cb |
void (*)(void *) |
当前收发回调函数 |
master_sem |
ec_lock_t |
主站信号量(保护配置操作) |
io_sem |
ec_lock_t |
IO 信号量(IDLE 阶段使用) |
scan_busy / config_busy |
unsigned int |
扫描/配置进行中标志 |
active |
unsigned int |
主站是否已激活 |
Master 线程循环流程
Datagram 收发流程
并发控制
| 锁 | 保护对象 | 使用场景 |
|---|---|---|
master_sem |
主站配置操作 | 域创建/删除、从站配置、请求队列操作 |
io_sem |
Datagram 收发 | IDLE 阶段的收发回调保护 |
device_sem |
设备绑定/解绑 | 网卡设备 attach/detach 操作 |
ext_queue_sem |
外部 Datagram 队列 | 非应用层 Datagram 的入队操作 |
scan_sem |
扫描状态 | 扫描进行中标志保护 |
config_sem |
配置状态 | 配置进行中标志保护 |
深入源码
ec_master_init() — 主站初始化
位置: master/master.c:140–411
初始化流程:
- 设置索引号,初始化所有互斥锁 (
master_sem,device_sem,io_sem等)- 初始化设备 (
ec_device_init)、MAC 地址、DC 相关数据报- 初始化 FSM (
ec_fsm_master_init)- 初始化外部数据报环形缓冲 (
EC_EXT_RING_SIZE = 32)- 创建字符设备 (
cdev) 和可选的 RTDM 设备- 初始化所有链表头 (configs, domains, sii_images, datagram_queue 等)
ec_master_idle_thread() — IDLE 线程主循环
位置: master/master.c:1926–1987
ec_master_operation_thread() — OPERATION 线程主循环
位置: master/master.c:1993–2064
与 IDLE 线程的关键差异:
- 不自行收发: 由应用程序通过回调函数控制收发时机
- 高精度定时: 使用
hrtimer实现精确周期- Slave FSM 由应用调度: 当
rt_slave_requests为真时,由ecrt_master_exec_requests()执行- 外部 Datagram 注入: 通过
injection_seq_fsm/injection_seq_rt序列号协调
ec_master_send_datagrams() — Datagram 发送
位置: master/master.c:1171–1315
从 datagram_queue 中取出待发送数据报,将其填充到以太网帧中:
- 获取发送缓冲区
ec_device_tx_data() - 写入 EtherCAT 帧头 (2 字节: Length + Type)
- 遍历队列,将多个 Datagram 串联到同一帧中(直到超出
EC_MAX_DATA_SIZE = 1486字节限制) - 为每个 Datagram 分配唯一的
index,记录发送时间戳 - 调用
ec_device_send()将帧发送到网卡
ec_master_receive_datagrams() — Datagram 接收
位置: master/master.c:1325–1582
处理网卡接收到的 EtherCAT 帧:
- 验证帧头 (EtherType=0x88A4) 和帧长度
- 遍历帧中的 Datagram,通过
index匹配已发送队列 - 拷贝数据到对应 Datagram 的
data缓冲区 - 更新 Working Counter (
working_counter) - 更新接收时间戳 (
jiffies_received,cycles_received) - 将 Datagram 从
sent队列移至完成状态
ec_master_enter_idle_phase() — 进入 IDLE 阶段
位置: master/master.c:771–820
- 安装内部收发回调 (
ec_master_internal_send_cb) - 重置从站响应计数器
- 创建 EoE 接口(如果启用)
- 创建并启动 IDLE 内核线程
ec_master_enter_operation_phase() — 进入 OPERATION 阶段
位置: master/master.c:852–917
- 等待从站配置完成 (
config_busy清零) - 将所有在线从站设置为 PREOP 状态
- 切换到应用程序回调 (
app_send_cb/app_receive_cb) - 创建并启动 OPERATION 内核线程
DC 相关数据报
| 数据报 | 用途 | 命令类型 |
|---|---|---|
ref_sync_datagram |
参考时钟同步到主站时钟 | FPWR (0x0910) |
sync_datagram |
DC 漂移补偿 (32 位) | ARMW / FRMW |
sync64_datagram |
获取 64 位参考时钟系统时间 | FRMW |
sync_mon_datagram |
DC 同步监控 | BRD (0x092C) |
二、Domain 模块
3.2 — domain.c / domain.h — 过程数据域管理
概览
Domain 是什么?
Domain (域) 是 IgH EtherCAT 主站中管理过程数据的核心抽象。每个 Domain 拥有独立的逻辑地址空间,通过 FMMU 将逻辑地址映射到各从站的物理内存。应用程序通常将需要同步更新的从站 I/O 分配到同一个 Domain 中。
核心理念
独立地址空间: 每个 Domain 的逻辑地址从 0 开始,互不干扰
FMMU 映射: Domain 收集所有 FMMU 配置,通过 FMMU 实现逻辑→物理地址转换
Datagram 封装: Domain 完成时自动生成 LRW/LRD/LWR Datagram(主/备设备双份)
WKC 校验: 每个 Domain 维护独立的 Working Counter
多 Domain FMMU 内存布局
图:Domain 0 和 Domain 1 各自拥有独立的逻辑地址空间,通过 FMMU 映射到不同从站
技术详情
ec_domain 结构体字段
| 字段 | 类型 | 说明 |
|---|---|---|
list |
struct list_head |
链表节点(挂载到 master->domains) |
master |
ec_master_t * |
所属主站 |
index |
unsigned int |
域索引号 |
fmmu_configs |
struct list_head |
FMMU 配置链表 |
data_size |
size_t |
过程数据总大小(字节) |
data |
uint8_t * |
过程数据缓冲区指针 |
data_origin |
ec_origin_t |
数据缓冲区来源(内部/外部) |
logical_base_address |
uint32_t |
逻辑基地址(由 ec_domain_finish() 分配) |
datagram_pairs |
struct list_head |
Datagram 对列表(主设备/备设备各一份) |
working_counter[2] |
uint16_t |
各设备最后一次 WKC 值 |
expected_working_counter |
uint16_t |
期望 WKC 值 |
working_counter_changes |
unsigned int |
WKC 变化计数 |
redundancy_active |
unsigned int |
冗余是否激活 |
offset_used[EC_DIR_COUNT] |
uint32_t |
各方向(输入/输出)已使用的偏移 |
sc_in_work |
const ec_slave_config_t * |
当前正在注册的从站配置 |
Domain 生命周期
FMMU 配置与偏移计算
当应用程序调用 ecrt_slave_config_reg_pdo_entry() 时:
- 根据 PDO 方向(输入/输出)选择对应的 Sync Manager
- 创建
ec_fmmu_config并添加到 Domain 的fmmu_configs链表 - Domain 为该 FMMU 分配逻辑偏移:
logical_domain_offset = offset_used[dir] - 更新
offset_used[dir] += fmmu_data_size - 更新
data_size = max(offset_used[EC_DIR_INPUT], offset_used[EC_DIR_OUTPUT])
WKC 计算
expected_working_counter在ec_domain_finish()中计算。每个 FMMU 根据方向贡献 WKC 增量:读操作 +1,写操作 +2,读写 +3。期望 WKC 为所有 FMMU 贡献之和。
深入源码
ec_domain_init()
位置: master/domain.c:63–91
初始化 Domain 结构体:设置 master 指针和 index,初始化所有链表头,将 data_size 置 0,初始化各方向的 offset_used 为 0。
ec_domain_add_fmmu_config()
位置: master/domain.c:131–175
关键逻辑:
- 计算 FMMU 数据大小:
fmmu_data_size = ec_pdo_list_total_size(&pdos) - 若
allow_overlapping_pdos启用:输出 FMMU 偏移从输入偏移开始(共享空间) - 否则:各方向独立递增偏移
- 调用
ec_fmmu_set_domain_offset_size()设置 FMMU 的逻辑偏移和数据大小 - 将 FMMU 添加到
fmmu_configs链表 - 更新
data_size为各方向偏移的最大值
ec_domain_finish()
位置: master/domain.c:308–402
域完成时的关键操作:
- 分配过程数据缓冲区
data(若data_origin == EC_ORIG_INTERNAL) - 设置逻辑基地址
logical_base_address(由 Master 统一分配) - 计算期望 WKC: 遍历所有 FMMU,累加读/写方向贡献
- 创建 Datagram pairs: 若
data_size ≤ EC_MAX_DATA_SIZE,创建一个 LRW Datagram;否则拆分为多个 Datagram - 每个 Datagram 创建两份(主设备 + 备设备),形成 pair
⚠ Datagram 拆分
当域数据超过
EC_MAX_DATA_SIZE(1486 字节) 时,ec_domain_finish()将域拆分为多个 Datagram。每个 Datagram 覆盖一段逻辑地址范围,通过datagram_pairs链表管理。
三、Slave 模块
3.3 — slave.c / slave.h + slave_config.c / slave_config.h
概览
Slave 模块概述
Slave 模块管理 EtherCAT 总线上的每个从站设备。IgH 中从站管理分为两个核心概念:
- ec_slave — 总线上实际检测到的从站,包含硬件信息和运行时状态
- ec_slave_config — 应用程序对从站的配置请求(即使从站离线也保留)
设计理念
配置请求 (
ec_slave_config) 与实际从站 (ec_slave) 解耦。应用程序在主站激活前即可创建配置,当从站上线时自动匹配应用配置。这支持了热插拔场景——从站断线后配置保留,重新上线后自动恢复。
配置请求生命周期
技术详情
ec_slave 结构体关键字段
| 分类 | 字段 | 类型 | 说明 |
|---|---|---|---|
| 寻址 | ring_position |
uint16_t |
环位置(自动递增地址) |
station_address |
uint16_t |
配置站地址(扫描时分配) | |
effective_alias |
uint16_t |
有效别名地址 | |
| 状态 | current_state |
ec_slave_state_t |
当前 AL 状态 |
requested_state |
ec_slave_state_t |
请求的 AL 状态 | |
error_flag |
unsigned int |
错误标志(停止处理) | |
| 基础信息 | base_type |
uint8_t |
从站类型 |
base_fmmu_count |
uint8_t |
支持的 FMMU 数量 | |
base_sync_count |
uint8_t |
支持的 Sync Manager 数量 | |
base_dc_supported |
uint8_t |
是否支持分布式时钟 | |
transmission_delay |
uint32_t |
DC 传输延迟 (ns) | |
| SII | vendor_words |
uint16_t * |
前 16 个 SII 字(厂商信息) |
sii_image |
ec_sii_image_t * |
完整 SII 镜像 | |
sdo_dictionary |
struct list_head |
SDO 字典 | |
| 请求队列 | sdo_requests |
struct list_head |
SDO 请求列表 |
foe_requests |
struct list_head |
FoE 请求列表 | |
soe_requests |
struct list_head |
SoE 请求列表 | |
eoe_requests |
struct list_head |
EoE 请求列表 | |
| 邮箱 | configured_rx/tx_mailbox_* |
uint16_t |
配置的邮箱偏移和大小 |
| FSM | fsm |
ec_fsm_slave_t |
从站状态机实例 |
ec_slave_config 结构体关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
alias / position |
uint16_t |
从站标识(别名:位置) |
vendor_id / product_code |
uint32_t |
厂商 ID 和产品码(匹配条件) |
slave |
ec_slave_t * |
绑定的实际从站(NULL=离线) |
sync_configs[EC_MAX_SYNC_MANAGERS] |
ec_sync_config_t |
Sync Manager 配置 |
fmmu_configs[EC_MAX_FMMUS] |
ec_fmmu_config_t |
FMMU 配置(最多 16 个) |
used_fmmus |
uint8_t |
已使用的 FMMU 数量 |
dc_assign_activate |
uint16_t |
DC AssignActivate 字 |
dc_sync[EC_SYNC_SIGNAL_COUNT] |
ec_sync_signal_t |
DC 同步信号配置 (SYNC0/SYNC1) |
sdo_configs |
struct list_head |
SDO 配置列表 |
sdo_requests / foe_requests |
struct list_head |
运行时请求列表 |
voe_handlers |
struct list_head |
VoE 处理器列表 |
watchdog_divider |
uint16_t |
看门狗分频 (40ns 间隔) |
allow_overlapping_pdos |
uint8_t |
允许 PDO 空间重叠 |
SII 信息提取
从站扫描期间,IgH 通过 fsm_sii 读取 EEPROM 内容,然后调用以下函数解析各 Category:
| 函数 | Category | 提取内容 |
|---|---|---|
ec_slave_fetch_sii_strings() |
0x001A | 字符串表(名称、描述等) |
ec_slave_fetch_sii_general() |
0x001E | 设备组、名称、CoE 标志、物理层 |
ec_slave_fetch_sii_syncs() |
0x0029 | Sync Manager 描述 |
ec_slave_fetch_sii_pdos() |
0x0032/0x0033 | TxPDO / RxPDO 映射 |
深入源码
ec_slave_init()
位置: master/slave.c:62–163
初始化从站结构体:设置 master 指针、ring_position、station_address。初始化所有链表头(sdo_dictionary, sdo_requests, foe_requests, soe_requests, eoe_requests 等)。初始化从站 FSM ec_fsm_slave_init(&slave->fsm, slave)。
ec_slave_clear()
位置: master/slave.c:250–349
清理从站资源:中止所有待处理请求(SDO/FoE/SoE/EoE/寄存器),释放 SDO 字典,释放 SII 镜像和 vendor_words。
ec_slave_config_attach()
位置: master/slave_config.c:270–330
配置绑定到实际从站的流程:
- 通过
alias:position查找对应的ec_slave - 验证
vendor_id和product_code匹配 - 设置
sc->slave = slave,建立双向关联 - 如果从站已配置,设置
force_config标志触发重新配置
ec_slave_config_detach()
位置: master/slave_config.c:336–359
断开配置与从站的关联:置 sc->slave = NULL,使所有待处理请求过期(返回错误)。
ec_slave_calc_transmission_delays_rec()
位置: master/slave.c:1234–1265
递归计算 DC 传输延迟:从主站开始,沿拓扑树向下传播,计算每个从站相对于参考时钟的 transmission_delay。用于分布式时钟偏移补偿。
端口数据结构
| 字段 | 说明 |
|---|---|
ports[4].desc |
端口描述符 (Unused/EtherCAT/MII 等) |
ports[4].link |
端口链路状态 |
ports[4].next_slave |
通过此端口连接的下游从站 |
ports[4].receive_time |
端口接收时间(DC 延迟测量用) |
ports[4].delay_to_next_dc |
到下一个 DC 从站的延迟 |
upstream_port |
面向主站的端口索引 |

浙公网安备 33010602011771号