【原创】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 三阶段生命周期

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

初始化流程:

  1. 设置索引号,初始化所有互斥锁 (master_sem, device_sem, io_sem 等)
  2. 初始化设备 (ec_device_init)、MAC 地址、DC 相关数据报
  3. 初始化 FSM (ec_fsm_master_init)
  4. 初始化外部数据报环形缓冲 (EC_EXT_RING_SIZE = 32)
  5. 创建字符设备 (cdev) 和可选的 RTDM 设备
  6. 初始化所有链表头 (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 中取出待发送数据报,将其填充到以太网帧中:

  1. 获取发送缓冲区 ec_device_tx_data()
  2. 写入 EtherCAT 帧头 (2 字节: Length + Type)
  3. 遍历队列,将多个 Datagram 串联到同一帧中(直到超出 EC_MAX_DATA_SIZE = 1486 字节限制)
  4. 为每个 Datagram 分配唯一的 index,记录发送时间戳
  5. 调用 ec_device_send() 将帧发送到网卡

ec_master_receive_datagrams() — Datagram 接收

位置: master/master.c:1325–1582

处理网卡接收到的 EtherCAT 帧:

  1. 验证帧头 (EtherType=0x88A4) 和帧长度
  2. 遍历帧中的 Datagram,通过 index 匹配已发送队列
  3. 拷贝数据到对应 Datagram 的 data 缓冲区
  4. 更新 Working Counter (working_counter)
  5. 更新接收时间戳 (jiffies_received, cycles_received)
  6. 将 Datagram 从 sent 队列移至完成状态

ec_master_enter_idle_phase() — 进入 IDLE 阶段

位置: master/master.c:771–820

  1. 安装内部收发回调 (ec_master_internal_send_cb)
  2. 重置从站响应计数器
  3. 创建 EoE 接口(如果启用)
  4. 创建并启动 IDLE 内核线程

ec_master_enter_operation_phase() — 进入 OPERATION 阶段

位置: master/master.c:852–917

  1. 等待从站配置完成 (config_busy 清零)
  2. 将所有在线从站设置为 PREOP 状态
  3. 切换到应用程序回调 (app_send_cb / app_receive_cb)
  4. 创建并启动 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 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() 时:

  1. 根据 PDO 方向(输入/输出)选择对应的 Sync Manager
  2. 创建 ec_fmmu_config 并添加到 Domain 的 fmmu_configs 链表
  3. Domain 为该 FMMU 分配逻辑偏移:logical_domain_offset = offset_used[dir]
  4. 更新 offset_used[dir] += fmmu_data_size
  5. 更新 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

关键逻辑:

  1. 计算 FMMU 数据大小: fmmu_data_size = ec_pdo_list_total_size(&pdos)
  2. 若 allow_overlapping_pdos 启用:输出 FMMU 偏移从输入偏移开始(共享空间)
  3. 否则:各方向独立递增偏移
  4. 调用 ec_fmmu_set_domain_offset_size() 设置 FMMU 的逻辑偏移和数据大小
  5. 将 FMMU 添加到 fmmu_configs 链表
  6. 更新 data_size 为各方向偏移的最大值

ec_domain_finish()

位置: master/domain.c:308–402

域完成时的关键操作:

  1. 分配过程数据缓冲区 data(若 data_origin == EC_ORIG_INTERNAL)
  2. 设置逻辑基地址 logical_base_address(由 Master 统一分配)
  3. 计算期望 WKC: 遍历所有 FMMU,累加读/写方向贡献
  4. 创建 Datagram pairs: 若 data_size ≤ EC_MAX_DATA_SIZE,创建一个 LRW Datagram;否则拆分为多个 Datagram
  5. 每个 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

配置绑定到实际从站的流程:

  1. 通过 alias:position 查找对应的 ec_slave
  2. 验证 vendor_id 和 product_code 匹配
  3. 设置 sc->slave = slave,建立双向关联
  4. 如果从站已配置,设置 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 面向主站的端口索引
posted @ 2026-04-14 21:24  沐多  阅读(713)  评论(0)    收藏  举报