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 面向主站的端口索引