IgH EtherCAT主站中Master、Slave及Domain模块具体介绍是什么?
摘要:目录一、Master 模块概览Master 模块是什么?Master 三阶段生命周期技术详情线程模型关键数据结构ec_master 结构体字段Master 线程循环流程Datagram 收发流程并发控制深入源码ec_master_init(
目录一、Master 模块概览Master 模块是什么?Master 三阶段生命周期技术详情线程模型关键数据结构ec_master 结构体字段Master 线程循环流程Datagram 收发流程并发控制深入源码ec_master_init() — 主站初始化ec_master_idle_thread() — IDLE 线程主循环ec_master_operation_thread() — OPERATION 线程主循环ec_master_send_datagrams() — Datagram 发送ec_master_receive_datagrams() — Datagram 接收ec_master_enter_idle_phase() — 进入 IDLE 阶段ec_master_enter_operation_phase() — 进入 OPERATION 阶段DC 相关数据报二、Domain 模块概览Domain 是什么?多 Domain FMMU 内存布局技术详情ec_domain 结构体字段Domain 生命周期FMMU 配置与偏移计算深入源码ec_domain_init()ec_domain_add_fmmu_config()ec_domain_finish()三、Slave 模块概览Slave 模块概述配置请求生命周期技术详情ec_slave 结构体关键字段ec_slave_config 结构体关键字段SII 信息提取深入源码ec_slave_init()ec_slave_clear()ec_slave_config_attach()ec_slave_config_detach()ec_slave_calc_transmission_delays_rec()端口数据结构
一、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 三阶段生命周期
stateDiagram-v2
[*] --> ORPHANED: ec_master_init()
ORPHANED --> IDLE: ec_master_enter_idle_phase()
IDLE --> ORPHANED: ec_master_leave_idle_phase()
IDLE --> OPERATION: ec_master_enter_operation_phase()
OPERATION --> IDLE: ec_master_leave_operation_phase()
state ORPHANED {
note1: 无网卡设备绑定
note2: 仅字符设备可用
}
state IDLE {
note3: IDLE 线程运行
note4: 自动扫描从站
note5: 自动配置从站
}
state OPERATION {
note6: OP 线程运行
note7: 应用程序控制
note8: 实时数据交换
}
技术详情
线程模型
线程
运行阶段
职责
优先级
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 线程循环流程
flowchart TD
A[ec_master_idle_thread] --> B[接收 Datagram]
B --> C{收到数据?}
C -->|是| D[ec_master_receive_datagrams]
C -->|否| E[跳过]
D --> F[执行 Master FSM]
E --> F
F --> G[执行 Slave FSM 列表]
G --> H[注入外部 Datagram]
H --> I[ec_master_send_datagrams]
I --> J[休眠 send_interval]
J --> B
K[ec_master_operation_thread] --> L[执行 Master FSM]
L --> M{rt_slave_requests?}
M -->|否| N[执行 Slave FSM 列表]
M -->|是| O[由应用层执行]
N --> P[外部 Datagram 注入]
O --> P
P --> Q[统计输出]
Q --> R[hrtimer 等待]
R --> K
Datagram 收发流程
flowchart LR
subgraph 发送
S1[Datagram 队列] --> S2[ec_master_send_datagrams]
S2 --> S3[填充帧头 + 多 Datagram 串联]
S3 --> S4[ec_device_send]
end
subgraph 接收
R1[网卡接收中断] --> R2[ec_master_receive_datagrams]
R2 --> R3[验证帧完整性]
R3 --> R4[匹配已发送 Datagram]
R4 --> R5[更新 WKC + 时间戳]
end
S4 -->|网络| R1
并发控制
锁
保护对象
使用场景
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
flowchart TD
A[开始循环] --> B[ecrt_master_receive]
B --> C[ec_master_receive_datagrams]
C --> D[ecrt_master_send]
D --> E[ec_master_send_datagrams]
E --> F{FSM datagram 就绪?}
F -->|是| G[执行 ec_fsm_master_exec]
F -->|否| H[跳过 FSM]
G --> I[执行 slave FSM 执行列表]
H --> I
I --> J[ec_master_send_ext]
J --> K[休眠 send_interval ms]
K --> A
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 生命周期
flowchart TD
A[ecrt_master_create_domain] --> B[ec_domain_init]
B --> C[应用程序注册 PDO]
C --> D[ecrt_slave_config_reg_pdo_entry]
D --> E[ec_domain_add_fmmu_config]
E --> F{更多 PDO?}
F -->|是| C
F -->|否| G[ecrt_master_activate]
G --> H[ec_domain_finish]
H --> I[分配逻辑基地址]
I --> J[计算 data_size]
J --> K[创建 Datagram pairs]
K --> L[域就绪,周期性交换数据]
L --> M[ecrt_master_deactivate]
M --> N[ec_domain_clear]
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) 解耦。应用程序在主站激活前即可创建配置,当从站上线时自动匹配应用配置。这支持了热插拔场景——从站断线后配置保留,重新上线后自动恢复。
配置请求生命周期
flowchart TD
A[应用: ecrt_master_slave_config] --> B[创建 ec_slave_config]
B --> C[配置 SDO/PDO/DC]
C --> D[ecrt_master_activate]
D --> E[总线扫描完成]
E --> F{匹配 slave?}
F -->|是| G[ec_slave_config_attach]
G --> H[从站配置应用]
H --> I[运行: 过程数据交换]
F -->|否| J[配置保留,等待从站]
J --> K{从站上线?}
K -->|是| G
I --> L{从站离线?}
L -->|是| M[ec_slave_config_detach]
M --> J
技术详情
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
面向主站的端口索引
