【原创】IgH EtherCAT主站详解(十一)--主站启动配置流程

启动配置流程

4.1 — 从 insmod 加载到所有从站进入 OP 的完整流程


概览

EtherCAT 主站启动全景

EtherCAT 主站的启动过程可以概括为 5 个阶段:从内核模块加载开始,经过设备绑定、总线扫描、从站配置,最终进入实时运行阶段。整个过程由内核线程和状态机自动驱动,应用程序只需在适当的时机调用 ecrt API 接管控制权。

五阶段启动流程

阶段 1: 内核模块加载 (insmod) → 分配主站实例
阶段 2: 网卡设备绑定 → ORPHANED → IDLE
阶段 3: 总线扫描 + 从站识别 → EEPROM + PDO
阶段 4: 从站配置 → Init → Pre-Op → Safe-Op → Op
阶段 5: 应用激活 → IDLE → OPERATION (实时通信)

启动流程总图

flowchart TD A[insmod ec_master.ko] --> B[ec_init_module] B --> C["ec_master_init() × N<br/>分配 N 个主站实例"] C --> D["等待网卡设备绑定<br/>(ORPHANED 阶段)"] D --> E["网卡 attach<br/>ecdev_offer()"] E --> F["ec_master_enter_idle_phase()<br/>ORPHANED → IDLE"] F --> G["启动 IDLE 内核线程<br/>EtherCAT-IDLE"] G --> H["Master FSM 开始运行<br/>BRD 广播探测从站"] H --> I["从站数量变化?<br/>触发拓扑扫描"] I -->|是| J["Slave Scan FSM<br/>扫描/识别/构建 PDO"] I -->|否| H J --> K["扫描完成<br/>从站信息就绪"] K --> L["Slave Config FSM<br/>配置各从站到 Pre-Op"] L --> M["应用程序调用<br/>ecrt_master_activate()"] M --> N["ec_master_enter_operation_phase()<br/>IDLE → OPERATION"] N --> O["启动 OP 内核线程<br/>EtherCAT-OP"] O --> P["实时周期通信开始<br/>应用程序控制收发"] style A fill:#c6f6d5,stroke:#68d391 style F fill:#ebf8ff,stroke:#90cdf4 style J fill:#fefcbf,stroke:#ecc94b style L fill:#fefcbf,stroke:#ecc94b style P fill:#c6f6d5,stroke:#68d391

技术详情

阶段 1: 内核模块加载

通过 insmod ec_master.ko main_devices=XX:XX:XX:XX:XX:XX 加载主站模块。

ec_init_module() 执行流程:

  1. 注册字符设备区域 (alloc_chrdev_region)
  2. 创建设备类 /sys/class/EtherCAT/
  3. 解析 MAC 地址参数(主网卡 + 备用网卡)
  4. 为每个主站调用 ec_master_init():初始化锁、设备、FSM、链表、字符设备
  5. 主站进入 ORPHANED 阶段,等待网卡设备绑定

阶段 2: 网卡设备绑定

当网卡驱动的 probe 函数被调用时,IgH 适配代码调用 ecdev_offer() 将网卡设备与主站绑定:

sequenceDiagram participant DRV as 网卡驱动 participant DEV as ec_device participant MST as ec_master participant FSM as Master FSM DRV->>DEV: ecdev_offer(dev, poll_cb, ...) DEV->>MST: ec_master_find_device(mac) MST->>MST: ec_device_attach(devices[MAIN]) MST->>MST: 检查两个设备是否都已绑定 alt 两个设备都已绑定 MST->>MST: ec_master_enter_idle_phase() MST->>FSM: 启动 IDLE 线程 MST-->>DRV: 返回 0 (绑定成功) else 还有设备未绑定 MST-->>DRV: 返回 0 (等待其他设备) end

阶段 3: 总线扫描

IDLE 线程启动后,Master FSM 自动开始总线监控。当检测到从站响应数量变化时,触发完整的拓扑扫描流程:

  1. BRD 广播: 读取寄存器 0x0130 获取在线从站数量
  2. 地址分配: 使用 APWR 为每个从站分配递增的站地址
  3. DC 延迟测量: 测量每个从站的传播延迟
  4. 从站扫描: Slave Scan FSM 逐个扫描从站(读 EEPROM、构建 PDO 映射)

详见 4.2 拓扑扫描流程

阶段 4: 从站配置

扫描完成后,Master FSM 自动触发从站配置流程,将每个从站从 Init 状态推进到目标状态。

flowchart LR subgraph "从站配置 5 阶段" A[Init] -->|fsm_slave_config| B[Init → Pre-Op] B --> C[配置邮箱 SyncManager] C --> D[配置 SDO] D --> E[配置 PDO/FMMU] E --> F["Pre-Op → Safe-Op"] F --> G["配置 DC 同步"] G --> H["Safe-Op → Op"] end

并行配置: 每个从站有独立的 Slave FSM,从站之间无耦合关系。所有从站的 Init → Pre-Op → Safe-Op → Op 过程各自独立执行,互不阻塞。

阶段 5: 应用激活与实时运行

应用程序通过 ecrt API 激活主站:

步骤 API 调用 作用
1 ecrt_master_create() 打开主站字符设备
2 ecrt_master_create_domain() 创建过程数据域
3 ecrt_master_slave_config() 获取从站配置句柄
4 ecrt_slave_config_pdos() 注册 PDO 映射
5 ecrt_domain_reg_pdo_entry() 注册 PDO 入口,获取偏移
6 ecrt_master_activate() 激活主站 → IDLE→OPERATION
7 ecrt_domain_data() 获取过程数据内存指针
8 ecrt_master_send/receive() 周期性收发过程数据

IDLE 线程 vs OPERATION 线程

特性 IDLE 线程 OPERATION 线程
函数 ec_master_idle_thread() ec_master_operation_thread()
运行阶段 IDLE OPERATION
收发控制 线程自行收发 由应用程序回调控制
调度优先级 普通 (SCHED_OTHER) 实时 (SCHED_FIFO/RT)
主要工作 扫描从站 + 配置从站 执行 FSM + 统计输出
收发回调 ec_master_internal_send/receive_cb app_send/receive_cb

深入源码

ec_init_module() — 模块加载入口

位置: master/module.c:112–187

核心流程:

  1. alloc_chrdev_region() 注册字符设备号
  2. class_create("EtherCAT") 创建 sysfs 设备类
  3. 解析 main_devices[] 和 backup_devices[] MAC 地址参数
  4. 为每个主站调用 ec_master_init(&masters;[i], i, main_mac, backup_mac, ...)
  5. 所有主站初始状态为 EC_ORPHANED

ec_master_init() — 主站初始化

位置: master/master.c:140–411

初始化所有互斥锁 (master_sem, device_sem, io_sem)、设备、FSM、外部数据报环形缓冲、字符设备、链表头等。

ec_master_enter_idle_phase() — 进入 IDLE 阶段

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

关键步骤:

  1. 安装内部收发回调 (ec_master_internal_send/receive_cb)
  2. 设置 master->phase = EC_IDLE
  3. 重置 slaves_responding[] 以触发首次扫描
  4. 创建 EoE 接口(如果配置了 eoe_interfaces)
  5. 启动 EtherCAT-IDLE 内核线程

ec_master_idle_thread() — IDLE 线程主循环

位置: master/master.c:1926–1987

flowchart TD A["while (!kthread_should_stop())"] --> B["ecrt_master_receive()<br/>(io_sem 保护)"] B --> C["ec_lock_down(master_sem)"] C --> D["ec_fsm_master_exec(&fsm;)<br/>执行 Master FSM"] D --> E["ec_master_exec_slave_fsms()<br/>执行 Slave FSM 列表"] E --> F["ec_lock_up(master_sem)"] F --> G["ecrt_master_send()<br/>(io_sem 保护)"] G --> H{"FSM 空闲?"} H -->|是| I["nanosleep(send_interval)"] H -->|否| J["nanosleep(发送字节时间)"] I --> A J --> A

ec_master_enter_operation_phase() — 进入 OPERATION 阶段

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

关键步骤:

  1. 等待从站配置完成 (config_busy 清零)
  2. 等待扫描完成 (scan_busy 清零)
  3. 禁止新的扫描 (allow_scan = 0)
  4. 将所有在线从站请求设置为 PREOP
  5. 设置 master->phase = EC_OPERATION
  6. 清空应用回调指针(由 ecrt_master_activate() 设置)

ecrt_master_activate() — 应用激活

位置: master/master.c:2880–3060

由用户空间库 lib/master.c:ecrt_master_activate() 通过 ioctl 调用。

  1. 冻结从站配置(不允许新的配置请求)
  2. 为每个 Domain 分配 FMMU、计算逻辑地址偏移
  3. 为每个 Domain 分配过程数据内存
  4. 调用 ec_master_enter_operation_phase() 切换到 OPERATION
  5. 启动 EoE 线程(如有)
  6. 启动 EtherCAT-OP 内核线程

ec_master_operation_thread() — OPERATION 线程

位置: master/master.c:1993–2064

与 IDLE 线程的关键区别:不自行收发数据(由应用程序回调控制),使用 hrtimer 高精度定时,Slave FSM 由应用层或内核线程调度(取决于 rt_slave_requests)。

启动时序图

sequenceDiagram participant MOD as module.c participant MST as master.c participant FSM as fsm_master.c participant SCAN as fsm_slave_scan.c participant CFG as fsm_slave_config.c participant APP as 应用程序 MOD->>MST: ec_init_module() → ec_master_init() Note over MST: ORPHANED 阶段 MST->>MST: ec_master_find_device() → attach MST->>MST: ec_master_enter_idle_phase() Note over MST: ORPHANED → IDLE MST->>MST: 启动 EtherCAT-IDLE 线程 loop IDLE 线程循环 MST->>FSM: ec_fsm_master_exec() FSM->>FSM: BRD 广播读取从站数 FSM->>SCAN: 检测到从站数变化,触发扫描 SCAN->>SCAN: 逐从站扫描(EEPROM+PDO) SCAN-->>FSM: 扫描完成 FSM->>CFG: 触发从站配置 CFG->>CFG: Init → Pre-Op → Safe-Op → Op end APP->>MST: ecrt_master_activate() MST->>MST: ec_master_enter_operation_phase() Note over MST: IDLE → OPERATION MST->>MST: 启动 EtherCAT-OP 线程 APP->>MST: 周期性 send/receive
posted @ 2026-04-14 21:48  沐多  阅读(470)  评论(0)    收藏  举报