IgH EtherCAT主站并行启动、总体架构及软件分层是怎样的?

摘要:目录一、架构概览概览etherlab 是什么?系统全景架构核心能力一览目录结构总览技术详情技术能力详解协议栈完整性实时框架集成关键源文件索引版本信息架构相关章节导航二、软件分层架构概览四层架构概览技术详情各层接口详解应用层库层 (lib)
目录一、架构概览概览etherlab 是什么?系统全景架构核心能力一览目录结构总览技术详情技术能力详解协议栈完整性实时框架集成关键源文件索引版本信息架构相关章节导航二、软件分层架构概览四层架构概览技术详情各层接口详解应用层库层 (lib/)内核主站层 (master/)驱动层 (devices/)完整数据流向图深入源码ecrt.h API 到内核的映射关键结构体关系主站 Phase 转换三、模块依赖关系概览模块依赖全景技术详情核心模块清单FSM 状态机模块模块间关键关系深入源码头文件包含关系模块间数据流深入了解各模块四、并行启动与初始化概览启动流程三阶段整体启动流程技术详情Phase 详解EC_ORPHANED 阶段EC_IDLE 阶段EC_OPERATION 阶段从站并行状态切换(并行启动)多主站并行运行模块参数说明典型加载命令深入源码ec_init_module() 完整流程ecdev_offer() 设备匹配机制Phase 转换状态机线程模型详解深入了解 一、架构概览 第二章 —IgH EtherCAT Master 1.5.2 总体架构 概览 etherlab 是什么? IgH EtherCAT Master 1.5.2 开源 EtherCAT 主站实现,它作为 Linux 内核模块运行,完全符合 IEC/PAS 62407 国际标准,提供完整的 EtherCAT 主站协议栈。 核心定位 motorcortex-etherlab 是一个工业级 EtherCAT 主站,以 Linux 内核模块形式运行,支持多种实时框架(Xenomai、RTAI、RT-Preempt),可在同一台机器上运行多个主站实例,并通过统一的 ecrt.h API 同时服务于内核空间和用户空间应用程序。 系统全景架构 图:motorcortex-etherlab 系统全景架构(用户空间 ↔ 内核空间 ↔ 硬件) 核心能力一览 能力 说明 EtherCAT 协议栈 完整实现 EtherCAT 主站协议,支持所有 Datagram 类型(APRD/APWR/BRD/LRW 等 12 种) 多实时框架支持 Xenomai(内核空间 / 用户空间 RTDM)、RTAI、RT-Preempt,也可无实时扩展运行 原生网卡驱动 r8169、e1000/e1000e、igb、igc、8139too、stmmac 等多款网卡的原生适配版本 通用驱动 generic.c 可适配任何 Linux 内核支持的网卡芯片 过程数据域 (Domain) 多域支持,不同域可使用不同的采样率,自动计算 FMMU/SM 映射 邮箱协议 CoE (SDO/PDO/Emergency)、EoE、FoE、SoE、VoE 全部实现 DC 分布式时钟 纳秒级全网时间同步,支持 SYNC0/SYNC1 信号配置 冗余支持 双网卡冗余,支持环形/双线缆拓扑,自动故障检测与切换 命令行工具 ethercat 工具涵盖 30+ 子命令,用于总线监控、诊断、配置 EoE 虚拟网络 通过 EtherCAT 隧道以太网,支持桥接/路由网络架构 目录结构总览 flowchart LR root["motorcortex-etherlab/"] --> master["master/<br/>核心模块"] root --> devices["devices/<br/>网卡驱动"] root --> include_dir["include/<br/>ecrt.h API"] root --> lib["lib/<br/>用户空间库"] root --> tool["tool/<br/>命令行工具"] root --> examples["examples/<br/>示例代码"] root --> script["script/<br/>启动脚本"] master --> master_core["master.c, slave.c<br/>domain.c, fsm_*.c"] devices --> drv_native["r8169/, e1000e/<br/>igb/, generic.c"] tool --> cmd["Command*.cpp<br/>33+ 子命令"] examples --> ex_xenomai["xenomai/, user/<br/>mini/, dc_user/"] 技术详情 技术能力详解 协议栈完整性 IgH EtherCAT Master 实现了完整的 EtherCAT 协议栈,包括: 数据帧处理:支持所有 12 种 Datagram 寻址模式,帧的组装、发送、接收、解析全流程 从站管理:自动拓扑扫描、从站识别、EEPROM/SII 信息读取、配置生命周期管理 状态机驱动:10+ 个协作式状态机 (FSM) 驱动所有异步操作 DC 同步:分布式时钟传播延迟测量、参考时钟选取、漂移补偿、SYNC0/SYNC1 信号配置 实时框架集成 实时框架 内核空间 用户空间 接口 Xenomai ✓ ✓ (RTDM) rtdm_dev RTAI ✓ ✓ (RTDM) rtdm_dev RT-Preempt ✓ ✓ (ioctl) cdev 无实时扩展 ✓ ✓ (ioctl) cdev 关键源文件索引 目录 主要文件 职责 master/ master.c, slave.c, domain.c, fsm_*.c 核心主站逻辑、10+ 个 FSM 状态机、数据报管理 devices/ r8169/, e1000e/, generic.c 网卡驱动 EtherCAT 适配版本 include/ ecrt.h 实时应用 API 定义(Master/Domain/Slave Config/SDO/VoE) lib/ master.c, slave_config.c, domain.c 用户空间库,通过 ioctl 与内核通信 tool/ Command*.cpp ethercat 命令行工具,33+ 子命令实现 examples/ mini/, user/, xenomai/, dc_user/ 各平台示例程序与 DC 同步示例 script/ ifup-eoe.sh, sysconfig EoE 网络配置、系统服务脚本 版本信息 版本与许可 IgH EtherCAT Master 版本:1.5.2(ecrt.h API 版本 1.5.10) 支持内核版本:2.6.x 至 6.12(本项目以 6.1 为基准) License:GPL v2(内核模块)、LGPL v2.1(用户空间库 lib/) 作者:Florian Pose, Ingenieurgemeinschaft IgH 架构相关章节导航 页面 内容 链接 软件分层架构 四层架构详解 + 数据流向 layer-arch.html 模块依赖关系 核心模块清单 + 依赖全景图 module-dep.html 并行启动与初始化 insmod → 就绪完整流程 startup.html 二、软件分层架构 IgH EtherCAT Master 四层软件架构与数据流 概览 四层架构概览 IgH EtherCAT Master 采用清晰的四层软件架构,从上到下依次为:应用层、库层、内核主站层、驱动层。每一层通过明确定义的接口与相邻层交互,实现了关注点分离。 图:IgH EtherCAT Master 四层软件架构 层次 组件 职责 应用层 User Application / ethercat tool 业务逻辑、控制指令、总线监控 库层 lib/ (libecrt.so) ecrt.h API 封装、ioctl 系统调用转换 内核主站层 master/ (ec_master.ko) EtherCAT 协议核心、FSM 状态机、数据管理 驱动层 devices/ (网卡驱动) 网卡硬件抽象、帧的物理收发 技术详情 各层接口详解 以下时序图展示了应用程序从请求主站到周期性数据交换的完整跨层调用链: sequenceDiagram participant App as 用户应用 participant Lib as lib/ 用户空间库 participant Cdev as 字符设备 (cdev) participant Master as Master 内核模块 participant Device as 网卡驱动 (device.c) participant NIC as 网卡硬件 App->>Lib: ecrt_request_master(0) Lib->>Cdev: ioctl(EC_IOCTL_REQUEST) Cdev->>Master: ecrt_request_master_err() Master-->>Cdev: master 指针 Cdev-->>Lib: 文件描述符 Lib-->>App: ec_master_t* Note over App,NIC: 配置阶段 App->>Lib: ecrt_master_slave_config(...) Lib->>Cdev: ioctl(EC_IOCTL_SLAVE_CONFIG) Cdev->>Master: ecrt_master_slave_config_err() App->>Lib: ecrt_master_activate(master) Lib->>Cdev: ioctl(EC_IOCTL_ACTIVATE) Cdev->>Master: ec_master_enter_operation_phase() Note over App,NIC: 周期性运行阶段 App->>Lib: ecrt_master_send(master) Lib->>Cdev: ioctl(EC_IOCTL_SEND) Cdev->>Master: ecrt_master_send_datagrams() Master->>Device: ec_device_send() Device->>NIC: DMA 发送 NIC-->>Device: 接收完成 Device-->>Master: ec_master_receive_datagrams() App->>Lib: ecrt_master_receive(master) Lib->>Cdev: ioctl(EC_IOCTL_RECEIVE) Master-->>Lib: 处理后的数据 Lib-->>App: 过程数据已更新 应用层 用户空间应用:通过 ecrt.h API 与主站交互,典型流程为 request → config → activate → cyclic send/receive ethercat 工具:命令行工具通过 ioctl 与内核通信,提供总线监控、诊断、配置功能 内核空间应用:内核模块可直接调用 ecrt.h 的内核 API,无需 ioctl 开销 库层 (lib/) 将用户空间 API 调用转换为 ioctl 系统调用,通过 /dev/EtherCAT* 字符设备与内核通信 关键文件:lib/master.c、lib/slave_config.c、lib/domain.c 过程数据通过 mmap 内存映射传递,避免内核-用户空间数据拷贝开销 License 为 LGPL v2.1,允许商业应用链接 内核主站层 (master/) EtherCAT 协议核心实现:帧组装/解析、数据报队列管理、FSM 状态机驱动 10+ 个协作式 FSM 状态机管理所有异步操作(扫描、配置、通信) 过程数据 Domain 管理:自动 FMMU 地址分配、数据报成对管理 从站配置与监控:配置请求生命周期、AL 状态跟踪 关键入口:module.c(模块加载)、master.c(主站核心) 驱动层 (devices/) 抽象网卡硬件操作,为上层提供统一的 ec_device 接口 提供 poll 函数接口(ec_pollfunc_t),主站通过轮询获取接收数据 支持原生驱动(r8169、e1000e、igb、igc、8139too、stmmac 等)和通用驱动(generic.c) 发送使用 sk_buff TX Ring(16 个槽位),DMA 传递到网卡 完整数据流向图 flowchart TD subgraph 用户空间 A["用户应用<br/>ecrt_master_send()"] T["ethercat 工具<br/>ioctl()"] end subgraph 库层 L["lib/master.c<br/>ioctl 封装"] end subgraph 内核主站层 M["master.c<br/>数据报队列管理"] D["datagram.c<br/>数据报组装/解析"] F["fsm_master.c<br/>状态机驱动"] DM["domain.c<br/>过程数据映射"] end subgraph 驱动层 DEV["device.c<br/>网卡抽象"] DRV["网卡驱动<br/>(r8169, e1000e...)"] end subgraph 硬件 NIC["网卡 NIC"] SLAVES["EtherCAT 从站"] end A -->|"API 调用"| L T -->|"ioctl"| L L -->|"系统调用"| M M --> D M --> F M --> DM D -->|"帧数据"| DEV F -->|"FSM 数据报"| DEV DEV --> DRV DRV -->|"DMA"| NIC NIC -->|"EtherCAT 帧"| SLAVES SLAVES -->|"返回帧"| NIC NIC --> DRV DRV --> DEV DEV --> M M --> L L --> A 深入源码 ecrt.h API 到内核的映射 以下展示用户空间 API 调用如何经库层传递到内核处理的完整链路: include/ecrt.h + lib/master.c + master/module.c — API 调用链 // ========== include/ecrt.h — 用户空间 API 声明 ========== ec_master_t *ecrt_request_master(unsigned int master_index); // ========== lib/master.c — 用户空间库封装 ========== ec_master_t *ecrt_request_master(unsigned int master_index) { ec_master_t *master = ecrt_open_master(master_index); // ecrt_open_master() 内部: // fd = open("/dev/EtherCAT0", O_RDWR); // master->fd = fd; if (master) { if (ecrt_master_reserve(master)) { // ecrt_master_reserve() 内部: // ioctl(master->fd, EC_IOCTL_REQUEST, NULL); ecrt_release_master(master); return NULL; } } return master; } // ========== master/module.c — 内核模块处理 ========== ec_master_t *ecrt_request_master_err(unsigned int master_index) { ec_master_t *master; // 1. 检查 master_index 有效性 if (master_index >= master_count) return ERR_PTR(-EINVAL); master = &masters;[master_index]; // 2. 获取 master_sem 信号量 ec_lock_down_interruptible(&master;_sem); // 3. 检查 master->reserved 标志(互斥访问) if (master->reserved) { ec_lock_up(&master;_sem); return ERR_PTR(-EBUSY); } master->reserved = 1; ec_lock_up(&master;_sem); // 4. 检查 phase == EC_IDLE(设备已绑定) if (master->phase != EC_IDLE) return ERR_PTR(-ENODEV); // 5. 进入 OPERATION 阶段 ec_master_enter_operation_phase(master); return master; } 关键结构体关系 classDiagram class ec_master { +unsigned int index +unsigned int reserved +ec_master_phase_t phase +ec_device_t devices +ec_slave_t slaves +list_head configs +list_head domains +ec_fsm_master_t fsm +task_struct thread +list_head datagram_queue } class ec_device { +ec_master_t master +net_device dev +ec_pollfunc_t poll +sk_buff tx_skb +uint8_t link_state +u64 tx_count +u64 rx_count } class ec_domain { +ec_master_t master +size_t data_size +uint8_t data +list_head fmmu_configs +list_head datagram_pairs +uint16_t working_counter } class ec_slave { +ec_master_t master +uint16_t ring_position +uint16_t station_address +ec_slave_config_t config +uint8_t current_state +ec_fsm_slave_t fsm } class ec_slave_config { +uint16_t alias +uint16_t position +uint32_t vendor_id +uint32_t product_code +ec_sync_config_t sync_configs +ec_fmmu_config_t fmmu_configs +list_head sdo_configs +list_head voe_handlers } ec_master "1" --> "*" ec_device : devices ec_master "1" --> "*" ec_slave : slaves ec_master "1" --> "*" ec_domain : domains ec_slave "1" --> "0..1" ec_slave_config : config 主站 Phase 转换 主站生命周期中经历三个阶段 (Phase),每个阶段有明确的职责和线程模型: Phase 触发条件 运行线程 核心职责 EC_ORPHANED ec_master_init() 无 等待网卡设备绑定,不能执行任何总线操作 EC_IDLE ecdev_offer() MAC 匹配后 ec_master_enter_idle_phase() idle_thread Master FSM 循环执行,自动总线扫描,从站状态监控 EC_OPERATION ecrt_request_master() ec_master_enter_operation_phase() operation_thread 周期性数据交换,从站配置下发,FSM 执行配置/监控 stateDiagram-v2 [*] --> ORPHANED: ec_master_init() ORPHANED --> IDLE: ecdev_offer() MAC 匹配成功 IDLE --> OPERATION: ecrt_request_master() OPERATION --> IDLE: ecrt_release_master() IDLE --> ORPHANED: 网卡设备断开 state ORPHANED { [*] --> 等待设备 等待设备: 无网卡绑定 等待设备: 不能执行总线操作 } state IDLE { [*] --> 空闲线程运行 空闲线程运行: Master FSM 循环 空闲线程运行: 自动总线扫描 空闲线程运行: 从站状态监控 } state OPERATION { [*] --> 运行线程运行 运行线程运行: 周期性数据交换 运行线程运行: 从站配置下发 运行线程运行: FSM 配置/监控 } 三、模块依赖关系 核心模块清单、职责与依赖关系 概览 模块依赖全景 IgH EtherCAT Master 由 40+ 个源文件组成,可分为四大类:核心管理、FSM 状态机、配置管理、通信协议。下图展示了核心模块之间的调用与依赖关系: 图:IgH EtherCAT Master 核心模块依赖关系全景 模块组织原则 所有模块围绕 master.c 这一核心编排,采用协作式状态机 (FSM) 驱动异步操作。FSM 按层级嵌套:Master FSM → Slave FSM → 协议 FSM(CoE/EoE/FoE/SoE)。配置模块(FMMU、Sync Manager、PDO)由 Slave Config FSM 在配置阶段驱动。 技术详情 核心模块清单 模块 源文件 职责 Module 入口 module.c 内核模块加载/卸载,MAC 地址解析,ecdev_offer() 设备匹配 Master 核心 master.c / master.h 主站生命周期管理(ORPHANED/IDLE/OPERATION),线程调度,数据报收发 Device 设备 device.c / device.h 网卡设备抽象层,sk_buff TX Ring 管理,帧发送/接收 Domain 域 domain.c / domain.h 过程数据域管理,FMMU 地址分配,数据报对管理 Slave 从站 slave.c / slave.h 从站信息管理,AL 状态监控,SII 数据缓存 Slave Config slave_config.c / slave_config.h 从站配置请求,SDO/PDO/SM/FMMU 配置管理,VoE/SDO/FoE handler Datagram datagram.c / datagram.h 数据报构造/解析,12 种寻址模式,WKC 检查,队列管理 FMMU Config fmmu_config.c / fmmu_config.h FMMU 配置(逻辑地址 → 物理地址映射),读/写使能控制 Sync/SM Config sync_config.c / sync.h 同步管理器配置,邮箱/过程数据缓冲区控制 PDO pdo.c / pdo_entry.c / pdo_list.c PDO 映射表管理,PDO 分配/映射配置 Mailbox mailbox.c / mailbox.h 邮箱通信基础设施,双缓冲区握手机制,协议复用 Ethernet/EoE ethernet.c / eoe_request.c EoE 虚拟网络接口,Linux net_device 注册,帧分片/重组 Cdev cdev.c / cdev.h 字符设备(/dev/EtherCAT*),ioctl 命令分发 FSM 状态机模块 FSM 是 IgH EtherCAT Master 的核心驱动机制,所有异步操作(扫描、配置、通信)都通过协作式状态机实现。每个 FSM 在每个周期执行一步,不会阻塞。 FSM 源文件 状态数 职责 调用者 Master FSM fsm_master.c 15 总线监控,触发扫描/配置,管理 SII 写入请求 idle_thread / operation_thread Slave FSM fsm_slave.c 12 单个从站状态监控,请求分发到子 FSM Master FSM Slave Config fsm_slave_config.c 24 从站完整配置流程(Init → PreOp → SafeOp → Op) Master FSM Slave Scan fsm_slave_scan.c 19 从站扫描与识别,读取 SII,构建 PDO 映射 Master FSM State Change fsm_change.c 9 AL 状态转换请求-确认机制 Slave Config FSM CoE FSM fsm_coe.c 21 CoE SDO 上传/下载,分段传输,Emergency 消息 Slave FSM EoE FSM fsm_eoe.c 7 EoE 数据帧分片/重组收发 EoE 线程 / Slave FSM FoE FSM fsm_foe.c 14 FoE 文件传输,固件升级 Slave FSM SoE FSM fsm_soe.c 12 SoE IDN 读写操作 Slave FSM SII FSM fsm_sii.c 8 EEPROM/SII 读/写操作 Slave Scan FSM, Master FSM PDO FSM fsm_pdo.c / fsm_pdo_entry.c — PDO 分配/映射配置读取 Slave Scan FSM 模块间关键关系 classDiagram direction TB class module_c { +ec_init_module() +ec_cleanup_module() +ecdev_offer() } class master_c { +ec_master_init() +ec_master_clear() +idle_thread() +operation_thread() +ec_master_send_datagrams() +ec_master_receive_datagrams() } class device_c { +ec_device_init() +ec_device_attach() +ec_device_poll() +ec_device_send() } class domain_c { +ec_domain_init() +ec_domain_add_fmmu_config() +ec_domain_finish() } class slave_c { +ec_slave_init() +ec_slave_clear() } class slave_config_c { +ec_slave_config_init() +ec_slave_config_attach() +ec_slave_config_process() } class datagram_c { +ec_datagram_init() +ec_datagram_prealloc() +ec_datagram_create() } class fsm_master_c { +ec_fsm_master_init() +ec_fsm_master_exec() } class fsm_slave_c { +ec_fsm_slave_init() +ec_fsm_slave_exec() } module_c --> master_c : 创建/管理 master_c --> device_c : 收发帧 master_c --> domain_c : 过程数据 master_c --> slave_c : 从站管理 master_c --> datagram_c : 数据报队列 master_c --> fsm_master_c : 状态机驱动 slave_c --> slave_config_c : 配置绑定 fsm_master_c --> fsm_slave_c : 从站 FSM 分发 深入源码 头文件包含关系 以下展示 master/master.h 的核心依赖,它是整个内核主站层的中枢头文件: master/master.h — 核心头文件包含 // master/master.h 的核心依赖 #include <linux/list.h> #include <linux/timer.h> #include <linux/kthread.h> #include "device.h" // 网卡设备抽象 (ec_device) #include "domain.h" // 过程数据域 (ec_domain) #include "ethernet.h" // EoE 处理 (ec_eoe_handler) #include "fsm_master.h" // Master 状态机 (ec_fsm_master) #include "locks.h" // 同步锁抽象 (ec_lock_t) #include "cdev.h" // 字符设备 (ec_cdev) #ifdef EC_RTDM #include "rtdm.h" // RTDM 设备接口 #endif 模块间数据流 下图展示了数据在各模块间的流转路径,从用户请求到最终硬件发送: flowchart LR subgraph 入口 MOD[module.c] end subgraph 核心 MAS[master.c] DEV[device.c] DOM[domain.c] SLA[slave.c] SLC[slave_config.c] DAT[datagram.c] end subgraph FSM FM[fsm_master.c] FS[fsm_slave.c] FSC[fsm_slave_config.c] FSS[fsm_slave_scan.c] FC[fsm_change.c] FCOE[fsm_coe.c] FEOE[fsm_eoe.c] FFOE[fsm_foe.c] FSOE[fsm_soe.c] FSII[fsm_sii.c] end subgraph 配置 FMMU[fmmu_config.c] SYNC[sync_config.c] PDO[pdo.c / pdo_entry.c] MBX[mailbox.c] end MOD -->|"创建"| MAS MAS -->|"管理"| DEV MAS -->|"管理"| DOM MAS -->|"管理"| SLA MAS -->|"管理"| DAT MAS -->|"驱动"| FM FM -->|"触发"| FS FM -->|"触发"| FSS FS -->|"分发"| FCOE FS -->|"分发"| FEOE FS -->|"分发"| FFOE FS -->|"分发"| FSOE FSC -->|"使用"| FC FSC -->|"使用"| FCOE FSC -->|"使用"| FMMU FSC -->|"使用"| SYNC FSS -->|"使用"| FSII DOM -->|"使用"| FMMU SLC -->|"使用"| PDO FCOE -->|"使用"| MBX FEOE -->|"使用"| MBX FFOE -->|"使用"| MBX FSOE -->|"使用"| MBX 深入了解各模块 Master 模块详解— ec_master 结构体与线程模型 Domain 模块详解— 过程数据域与 FMMU 布局 Slave 模块详解— 从站管理与配置生命周期 Datagram 模块详解— 数据报生命周期与传递机制 四、并行启动与初始化 第二章 — 从 insmod 到主站就绪,各从站并行完成状态切换 概览 启动流程三阶段 IgH EtherCAT Master 的启动过程分为三个清晰的阶段,每个阶段由不同的事件触发。其中最关键的第三阶段——从站配置——采用并行启动机制:各个 EtherCAT 从站独立且并行地从 INIT 状态切换到 OP 状态,从站之间无耦合,互不等待。 阶段 触发命令 关键动作 1. 内核模块加载 insmod ec_master.ko main_devices=XX:XX... 创建主站实例,分配字符设备号,等待网卡设备 2. 网卡驱动绑定 insmod r8169-ethercat.ko 网卡驱动调用 ecdev_offer(),MAC 匹配后绑定设备 3. 应用请求主站 ecrt_request_master(0) 主站进入 OPERATION 阶段,开始周期性数据交换 核心概念:设备匹配机制 主站启动时处于 ORPHANED(孤立)状态,等待网卡驱动通过 ecdev_offer() 上报设备。当网卡的 MAC 地址与模块参数 main_devices 中指定的地址匹配时,设备绑定成功,主站进入 IDLE 阶段并自动开始总线扫描。 整体启动流程 flowchart TD A["insmod ec_master.ko<br/>main_devices=XX:XX:XX"] --> B["ec_init_module()<br/>分配主站实例"] B --> C["等待网卡设备<br/>(ORPHANED 阶段)"] C --> D["insmod r8169-ethercat.ko<br/>网卡驱动加载"] D --> E["ecdev_offer()<br/>MAC 地址匹配"] E --> F["ec_device_attach()<br/>设备绑定"] F --> G["IDLE 阶段<br/>空闲线程启动"] G --> H["自动总线扫描<br/>从站发现与识别"] H --> I["应用程序<br/>ecrt_request_master()"] I --> J["OPERATION 阶段<br/>运行线程启动"] J --> K["从站并行配置<br/>各从站独立切换状态"] K --> K1["从站 0: Init→PreOp→SafeOp→Op"] K --> K2["从站 1: Init→PreOp→SafeOp→Op"] K --> K3["从站 N: Init→PreOp→SafeOp→Op"] style A fill:#ebf8ff,stroke:#90cdf4 style C fill:#fff5f5,stroke:#feb2b2 style G fill:#f0fff4,stroke:#9ae6b4 style J fill:#faf5ff,stroke:#d6bcfa style K fill:#fffff0,stroke:#fbd38d style K1 fill:#f0fff4,stroke:#9ae6b4 style K2 fill:#f0fff4,stroke:#9ae6b4 style K3 fill:#f0fff4,stroke:#9ae6b4 技术详情 Phase 详解 EC_ORPHANED 阶段 主站已通过 ec_master_init() 完成初始化(分配数据结构、初始化 FSM、创建字符设备) 但尚未有网卡设备绑定,master->devices[EC_DEVICE_MAIN].dev == NULL 不能执行任何总线操作,无线程运行 等待网卡驱动调用 ecdev_offer() 提供设备 EC_IDLE 阶段 网卡设备通过 ec_device_attach() 绑定成功 idle_thread 内核线程启动,循环执行: 调用 ec_fsm_master_exec() 驱动 Master FSM Master FSM 自动执行总线扫描、从站状态监控 处理 SII 写入请求等异步操作 可通过 ethercat 工具查看总线信息 EC_OPERATION 阶段 应用程序通过 ecrt_request_master() 独占请求主站 operation_thread 运行线程启动(或由应用程序自行驱动 FSM) 应用程序通过 ecrt_master_activate() 触发从站配置 从站经历 Init → Pre-Op → Safe-Op → Op 状态转换 应用程序接管周期性 ecrt_master_send() / ecrt_master_receive() 从站并行状态切换(并行启动) 在 OPERATION 阶段,主站通过 fsm_slave_config 为每个从站下发配置,驱动其状态从 INIT 逐步切换到 OP。IgH 的核心设计是:各个从站的状态切换过程相互独立、无耦合,每个从站按照自身节奏完成 INIT → Pre-Op → Safe-Op → Op 的转换,不需要等待其他从站。 并行启动的含义 "并行启动"指的是总线上多个 EtherCAT 从站各自独立地完成状态切换。从站 A 的状态转换不依赖于从站 B 的状态,从站之间不存在时序耦合。即使某个从站因配置错误停留在 Pre-Op,其他从站仍可正常进入 Op 状态并开始过程数据交换。 以下时序图展示了三个从站并行完成状态切换的过程: sequenceDiagram participant Master as Master FSM participant S0 as 从站 0 (伺服) participant S1 as 从站 1 (I/O) participant S2 as 从站 2 (传感器) rect rgb(235, 244, 255) Note over Master,S2: 各从站独立完成 INIT → Pre-Op Master->>S0: fsm_slave_config: INIT → Pre-Op Master->>S1: fsm_slave_config: INIT → Pre-Op Master->>S2: fsm_slave_config: INIT → Pre-Op S0-->>Master: Pre-Op 就绪 S1-->>Master: Pre-Op 就绪 S2-->>Master: Pre-Op 就绪 end rect rgb(240, 255, 244) Note over Master,S2: 各从站独立完成 Pre-Op → Safe-Op Master->>S0: fsm_slave_config: Pre-Op → Safe-Op Master->>S1: fsm_slave_config: Pre-Op → Safe-Op Master->>S2: fsm_slave_config: Pre-Op → Safe-Op S0-->>Master: Safe-Op 就绪 S1-->>Master: Safe-Op 就绪 S2-->>Master: Safe-Op 就绪 end rect rgb(250, 245, 255) Note over Master,S2: 各从站独立完成 Safe-Op → OP Master->>S0: fsm_slave_config: Safe-Op → OP Master->>S1: fsm_slave_config: Safe-Op → OP Master->>S2: fsm_slave_config: Safe-Op → OP S0-->>Master: OP 就绪 (过程数据可读写) S1-->>Master: OP 就绪 (过程数据可读写) Note over S2: 从站 2 配置异常<br/>停留在 Safe-Op Note over S0,S1: 从站 0、1 不受影响<br/>正常进入 OP end 多主站并行运行 IgH 支持在同一台机器上同时运行最多 32 个主站实例,每个主站绑定不同的网卡。以下是双主站并行运行的时序: sequenceDiagram participant User as 管理员 participant Module as ec_master 模块 participant M0 as Master 0 participant M1 as Master 1 participant Drv as 网卡驱动 participant App as 应用程序 User->>Module: insmod ec_master.ko<br/>main_devices=MAC0,MAC1 Module->>M0: ec_master_init(0, MAC0) Module->>M1: ec_master_init(1, MAC1) Note over M0,M1: ORPHANED 阶段,等待设备 User->>Drv: insmod r8169-ethercat.ko Drv->>Module: ecdev_offer(net_dev0, poll, module) Module->>M0: MAC 匹配!ec_device_attach() Note over M0: 进入 IDLE 阶段 Drv->>Module: ecdev_offer(net_dev1, poll, module) Module->>M1: MAC 匹配!ec_device_attach() Note over M1: 进入 IDLE 阶段 Note over M0,M1: 各自独立运行空闲线程<br/>各自独立扫描总线 App->>M0: ecrt_request_master(0) Note over M0: 进入 OPERATION 阶段<br/>各从站并行状态切换 App->>M1: ecrt_request_master(1) Note over M1: 进入 OPERATION 阶段<br/>各从站并行状态切换 模块参数说明 参数 类型 说明 示例 main_devices charp[] 主网卡 MAC 地址列表,逗号分隔 00:11:22:33:44:55 backup_devices charp[] 备用网卡 MAC 地址(冗余模式) 00:aa:bb:cc:dd:ee debug_level uint 调试级别(0=关闭,1=基本信息,2=详细) 1 pcap_size ulong PCAP 调试缓冲区大小(字节),0=禁用 0 eoe_interfaces charp[] EoE 接口名称列表 eoe0 eoe_autocreate bool 自动创建 EoE 接口(默认 true) 1 典型加载命令 Shell — 典型的主站加载流程 # 1. 加载 EtherCAT 主站模块(绑定两个网卡) insmod ec_master.ko main_devices=00:11:22:33:44:55,00:aa:bb:cc:dd:ee # 2. 加载 EtherCAT 适配的网卡驱动 insmod r8169-ethercat.ko # 查看主站状态 ethercat master # 输出示例: # Master0 # Phase: Idle # Slaves: 3 # ... # Master1 # Phase: Idle # Slaves: 0 深入源码 ec_init_module() 完整流程 以下逐行分析 master/module.c 中的模块初始化函数: master/module.c — ec_init_module() int __init ec_init_module(void) { int i, ret = 0; // 1. 打印版本信息 EC_INFO("Master driver %s\n", EC_MASTER_VERSION); // 2. 初始化全局主站信号量(保护 masters[] 数组) ec_lock_init(&master;_sem); // 3. 分配字符设备号区域(/dev/EtherCAT0, /dev/EtherCAT1, ...) if (master_count) { if (alloc_chrdev_region(&device;_number, 0, master_count, "EtherCAT")) { EC_ERR("Failed to obtain device number(s)!\n"); ret = -EBUSY; goto out_return; } } // 4. 创建 sysfs 设备类(/sys/class/EtherCAT/) class = class_create(THIS_MODULE, "EtherCAT"); // 5. 清零 MAC 地址数组并解析模块参数 memset(macs, 0x00, sizeof(uint8_t) * MAX_MASTERS * 2 * ETH_ALEN); for (i = 0; i < master_count; i++) { ret = ec_mac_parse(macs[i][0], main_devices[i], 0); if (ret) goto out_class; if (i < backup_count) { ret = ec_mac_parse(macs[i][1], backup_devices[i], 1); if (ret) goto out_class; } } // 6. 初始化静态变量(Datagram 索引计数器等) ec_master_init_static(); // 7. 分配并初始化所有主站实例 masters = kmalloc(sizeof(ec_master_t) * master_count, GFP_KERNEL); for (i = 0; i < master_count; i++) { ret = ec_master_init(&masters;[i], i, macs[i][0], macs[i][1], device_number, class, debug_level); if (ret) goto out_free_masters; } EC_INFO("%u master%s waiting for devices.\n", master_count, (master_count == 1 ? "" : "s")); return ret; out_free_masters: for (i--; i >= 0; i--) ec_master_clear(&masters;[i]); kfree(masters); out_class: class_destroy(class); out_cdev: if (master_count) unregister_chrdev_region(device_number, master_count); out_return: return ret; } ecdev_offer() 设备匹配机制 当网卡驱动加载后,它会在 probe 阶段调用 ecdev_offer() 将自己"推销"给 EtherCAT 主站模块: master/module.c — ecdev_offer() ec_device_t *ecdev_offer( struct net_device *net_dev, // 网卡驱动的 net_device ec_pollfunc_t poll, // 设备的 poll 函数 struct module *module) // 网卡驱动模块指针 { ec_master_t *master; unsigned int i, dev_idx; // 遍历所有主站实例 for (i = 0; i < master_count; i++) { master = &masters;[i]; // 获取设备信号量(保护并发访问) ec_lock_down_interruptible(&master-;>device_sem); // 遍历主站的设备槽位(主设备 + 可选的备份设备) for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master); dev_idx++) { // 检查设备槽位是否空闲 且 MAC 地址匹配(或为广播地址) if (!master->devices[dev_idx].dev && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr) || ec_mac_is_broadcast(master->macs[dev_idx]))) { EC_INFO("Accepting %s as %s device for master %u.\n", str, ec_device_names[dev_idx != 0], master->index); // 绑定设备到主站 ec_device_attach(&master-;>devices[dev_idx], net_dev, poll, module); // 重命名网卡接口:eth0 → ecm0 (main) 或 ecb0 (backup) snprintf(net_dev->name, IFNAMSIZ, "ec%c%u", ec_device_names[dev_idx != 0][0], master->index); return &master-;>devices[dev_idx]; // 匹配成功 } } ec_lock_up(&master-;>device_sem); } return NULL; // 无匹配的主站,拒绝此设备 } Phase 转换状态机 stateDiagram-v2 [*] --> ORPHANED: ec_master_init() ORPHANED --> IDLE: ecdev_offer() MAC 匹配成功<br/>ec_master_enter_idle_phase() IDLE --> OPERATION: ecrt_request_master()<br/>ec_master_enter_operation_phase() OPERATION --> IDLE: ecrt_release_master()<br/>ec_master_leave_operation_phase() IDLE --> ORPHANED: 网卡设备断开<br/>ec_device_detach() state ORPHANED { [*] --> 等待设备 等待设备: 无网卡绑定 等待设备: 不能执行总线操作 } state IDLE { [*] --> 空闲线程运行 空闲线程运行: Master FSM 循环执行 空闲线程运行: 自动总线扫描 空闲线程运行: 从站状态监控 } state OPERATION { [*] --> 从站并行配置 从站并行配置: 各从站独立: Init→PreOp→SafeOp→Op 从站并行配置: 从站之间无耦合,互不等待 从站并行配置: 周期性数据交换 从站并行配置: FSM 执行配置/监控 } 线程模型详解 线程 运行阶段 函数 职责 idle_thread IDLE ec_master_idle_thread() 循环调用 Master FSM,发送/接收帧,执行总线扫描和状态监控 operation_thread OPERATION ec_master_operation_thread() 当应用未自行驱动 FSM 时运行,执行 FSM 和数据报处理 eoe_thread IDLE/OPERATION ec_master_eoe_thread() 处理 EoE 数据收发(需启用 EC_EOE 编译选项) 应用程序接管 FSM 驱动 在 OPERATION 阶段,应用程序可以通过 ecrt_master_send() 和 ecrt_master_receive() 自行驱动数据报收发。此时 operation_thread 仍会运行,负责处理非应用数据报(如 SDO 请求、从站配置等外部 FSM 操作)。 深入了解 Master 模块详解— idle_thread / operation_thread 源码分析 Master FSM— 状态机如何驱动扫描和配置 启动配置流程— 从 insmod 到所有从站进入 OP 的完整流程