【原创】IgH EtherCAT主站详解(二)--DC分布时钟与AL状态

一、分布式时钟 (Distributed Clock, DC)

1.4 — DC 全网精确时间同步原理、64 位系统时间、漂移补偿、SYNC 信号(基于 N32H7x5EC)


概览

为什么需要分布式时钟?

在工业自动化中,多个从站需要在完全相同的时刻采集输入信号或更新输出信号。即使微秒级的时间差异也会导致运动控制中的位置误差或测量数据不一致。EtherCAT 分布式时钟 (Distributed Clock, DC) 机制实现全网纳秒级精确时间同步。

N32H7x5EC DC 核心特性

系统时间: 64 位,从 2000-01-01 00:00 开始,基本单位 1 ns
内部更新速率: 100 MHz
同步抖动: 约 12 ns
同步信号: SYNC0 + SYNC1,可配置周期和脉冲长度
锁存信号: LATCH0/LATCH1,采样率 100 MHz,抖动约 11 ns

DC 同步时序

图:DC 传播延迟测量与 SYNC0/SYNC1 信号时序关系


技术详情

64 位系统时间

EtherCAT DC 使用 64 位纳秒计数器表示系统时间,从 2000-01-01 00:00 开始计时,基本单位为 1 ns。64 位时间可表示约 584 年的范围,无需处理溢出问题。每个具有 DC 功能的从站都维护独立的本地时钟。

时钟同步关键概念

概念 说明
系统时间 从 2000-01-01 00:00 开始,64 位,基本单位 1 ns
参考时钟 主站后第一个具有 DC 功能的 ESC
本地时钟 每个 DC 从站独立运行的自由时钟
偏移 (Offset) 本地时钟与参考时钟的初始差异
漂移 (Drift) 时钟源的微小频率偏差,需持续补偿

公式:tLocal copy of System Time = tLocal time + tOffset

DC 寄存器映射表

地址 寄存器名 大小 说明
0x0900–0x0903 ESC_DCRXTIMP0 4 B DC 接收时间端口 0
0x0904–0x0907 ESC_DCRXTIMP1 4 B DC 接收时间端口 1
0x0910–0x0917 ESC_DCSYSTIM 8 B DC 系统时间 (64 位纳秒)
0x0918–0x091F DC 本地时间 8 B 本地时间 = 系统时间 - 偏移
0x0920–0x0927 ESC_DCSYSTIMOST 8 B DC 系统时间偏移
0x0928–0x092B ESC_DCSYSTIMDLY 4 B DC 系统时间延迟
0x092C–0x092F ESC_DCSYSTIMDIFF 4 B DC 系统时间差
0x0930–0x0933 ESC_DCSCSAT 4 B DC 速度计数器启动值
0x0934–0x0937 ESC_DCSCDIFF 4 B DC 速度计数器差
0x0938 ESC_DCSTDFD 1 B DC 系统时间差滤波深度
0x0939 ESC_DCSCFD 1 B DC 速度计数器滤波深度
0x0980 ESC_DCCUCTRL 1 B DC 循环单元控制
0x0981 ESC_DCACT 1 B DC 激活 (SYNC0/SYNC1 使能)
0x0982–0x0983 ESC_DCSSPLEN 2 B DC 脉冲长度
0x0984 ESC_DCACTSTS 1 B DC 激活状态
0x098E ESC_DCSYNC0STS 1 B DC SYNC0 状态
0x098F ESC_DCSYNC1STS 1 B DC SYNC1 状态
0x0990–0x0997 ESC_DCSTCOPE 8 B DC 起始时间循环操作
0x0998–0x099F ESC_DCNSYNC1P 8 B DC Next SYNC1 脉冲
0x09A0–0x09A3 ESC_DCSYNC0CT 4 B DC SYNC0 周期时间
0x09A4–0x09A7 ESC_DCSYNC1CT 4 B DC SYNC1 周期时间
0x09AE ESC_DCLATCH0CTRL 1 B DC Latch0 控制
0x09AF ESC_DCLATCH1CTRL 1 B DC Latch1 控制
0x09B0–0x09B7 8 B DC Latch0 时间正边沿
0x09B8–0x09BF 8 B DC Latch0 时间负边沿
0x09C0–0x09C7 8 B DC Latch1 时间正边沿
0x09C8–0x09CF 8 B DC Latch1 时间负边沿
0x09F0–0x09F7 ESC_DCECATBCET 8 B ECAT 缓冲区更改事件时间
0x09F8–0x09FF ESC_DCPDIBSET 8 B PDI 缓冲区起始事件时间
0x0A00–0x0A07 ESC_DCPDIBCET 8 B PDI 缓冲区更改事件时间

DC 同步流程

flowchart TD A["ec_master_calc_dc()<br/>DC 同步入口"] --> B["ec_master_find_dc_ref_clock()"] B --> C["ec_master_calc_topology()"] C --> D["ec_master_calc_transmission_delays()"] D --> E["ecrt_master_activate()"] E --> F["Slave Config FSM<br/>DC 配置子状态机"] B -.->|"优先级 1"| B1["应用指定的参考时钟<br/>slave_config->dc_assign_activate"] B -.->|"优先级 2"| B2["第一个有 DC 能力的从站<br/>slave->has_dc"] C -.->|"遍历从站"| C1["计算拓扑延迟<br/>parent → child 延迟累加"] D -.->|"BRD 广播"| D1["测量每段传播延迟<br/>0x0910 系统时间锁存"] F --> F1["dc_clear_assign<br/>清除旧分配"] F1 --> F2["dc_cycle<br/>设置 SYNC 周期"] F2 --> F3["dc_sync_check<br/>等待时钟同步"] F3 --> F4["dc_start<br/>设置启动时间"] F4 --> F5["dc_assign<br/>激活 DC"] style A fill:#ebf8ff,stroke:#90cdf4 style B fill:#fefcbf,stroke:#f6e05e style C fill:#fefcbf,stroke:#f6e05e style D fill:#fefcbf,stroke:#f6e05e style F fill:#faf5ff,stroke:#d6bcfa

参考时钟选择流程

flowchart TD START["ec_master_find_dc_ref_clock()"] --> CHECK_APP{"应用是否指定了<br/>DC 参考时钟?"} CHECK_APP -->|"是"| APP_REF["使用应用指定的从站<br/>作为参考时钟"] CHECK_APP -->|"否"| SCAN["遍历所有从站"] SCAN --> HAS_DC{"找到第一个<br/>has_dc=1 的从站?"} HAS_DC -->|"是"| FIRST_DC["选择第一个有 DC 能力的从站<br/>作为参考时钟"] HAS_DC -->|"否"| NO_DC["无 DC 参考时钟<br/>跳过 DC 同步"] APP_REF --> INIT_DATAGRAM["初始化三个 DC 数据报"] FIRST_DC --> INIT_DATAGRAM INIT_DATAGRAM --> D1["ref_sync_datagram<br/>FPWR 0x0910<br/>同步参考时钟到主站"] INIT_DATAGRAM --> D2["sync_datagram<br/>FPRD 0x0910<br/>读取参考时钟用于漂移补偿"] INIT_DATAGRAM --> D3["sync64_datagram<br/>ARMW/FRMW 0x0910<br/>64 位系统时间读写"] style START fill:#ebf8ff,stroke:#90cdf4 style APP_REF fill:#f0fff4,stroke:#9ae6b4 style FIRST_DC fill:#f0fff4,stroke:#9ae6b4 style NO_DC fill:#fff5f5,stroke:#feb2b2 style INIT_DATAGRAM fill:#faf5ff,stroke:#d6bcfa

同步过程四步骤

DC 同步核心步骤

步骤 1 — 传播延迟测量:主站广播写操作,从站锁存本地时间 (0x0900/0x0904)
步骤 2 — 偏移补偿:将差异写入系统时间偏移寄存器 (0x0920)
步骤 3 — 重置时间控制环路:写入速度计数器起始值 (0x0930)
步骤 4 — 漂移补偿:周期性发送 ARMW/FRMW 命令,持续校正时钟

传播延迟测量

sequenceDiagram participant M as 主站 Master participant S0 as 从站 0<br/>(DC) participant S1 as 从站 1 participant S2 as 从站 2<br/>(DC) Note over M,S2: 步骤 1:BWR 广播,从站锁存到达时间 M->>S0: BWR 0x0910 (触发时间锁存) Note right of S0: 锁存 t₀ 到 0x0900 S0->>S1: 转发帧 Note right of S1: 锁存 t₁ 到 0x0900 S1->>S2: 转发帧 Note right of S2: 锁存 t₂ 到 0x0900 S2-->>S1: 返回帧 S1-->>S0: 返回帧 S0-->>M: 返回帧 Note over M,S2: 步骤 2:FPRD 读取各从站锁存时间 M->>S0: FPRD 0x0900 ← t₀ M->>S1: FPRD 0x0900 ← t₁ M->>S2: FPRD 0x0900 ← t₂ Note over M,S2: 步骤 3:计算并写入传输延迟 Note right of M: delay₀ = t₀ (环路/2)<br/>delay₁ = t₁ - t₀<br/>delay₂ = t₂ - t₀ M->>S0: FPWR 0x0928 ← delay₀ M->>S1: FPWR 0x0928 ← delay₁ M->>S2: FPWR 0x0928 ← delay₂

DC 配置子状态机详解

状态 寄存器操作 说明
dc_clear_assign 0x0981 = 0 清除旧的 DC 激活,防止残留配置干扰
dc_cycle 0x09A0 (Sync0 周期) 写 0x09A4 (Sync1 周期) 设置 SYNC0/SYNC1 中断的周期时间
dc_sync_check 0x092C 循环读取系统时间差,等待 < 10μs(最多等 5s)
dc_start 0x0990 计算周期启动时间 = app_time + 100ms,写入起始时间
dc_assign 0x0981 写入 DC 激活值 (DCACT),启用 SYNC0/SYNC1 生成

从站时钟类型

类型 说明 DC 支持
Bus Shift Register 无 DC 能力,仅转发数据帧 不支持
Single-Cut-Through 有 DC,但不补偿传播延迟 部分支持
Double-Cut-Through 有 DC,补偿传播延迟 (最精确) 完全支持

同步信号 (SYNC0 / SYNC1)

SYNC0 是基础同步信号,支持 4 种模式:

模式 说明
循环生成 按周期时间 (0x09A0) 持续生成脉冲
单次触发 仅生成一次脉冲
循环应答 每次脉冲需要确认后才生成下一个
单次应答 单次脉冲带应答

SYNC1 依赖 SYNC0,可配置延迟。内部更新速率 100 MHz,同步抖动约 12 ns。脉冲长度由 0x0982 寄存器设置(>0 为周期模式)。

锁存信号 (LATCH0 / LATCH1)

LATCH0/LATCH1 支持上升沿和下降沿时间戳,采样率 100 MHz,抖动约 11 ns。

模式 说明
单事件模式 仅锁存第一个事件的时间戳
连续模式 持续锁存每个事件的时间戳

通信模式

模式 描述
自由运行 通信与应用独立运行
输出事件同步 应用与输出事件同步
同步信号同步 应用与 DC 同步信号同步

漂移补偿流程

每个通信周期,主站通过三个专用数据报持续校正全网时钟漂移:

数据报 类型 地址 方向 用途
ref_sync_datagram FPWR 0x0910 主站 → 参考从站 将主站时间写入参考时钟从站
sync_datagram FPRD 0x0910 参考从站 → 主站 读取参考时钟当前时间 (64 位)
sync64_datagram ARMW/FRMW 0x0910 参考从站 → 其他从站 读取参考时钟并传播到其他从站
sequenceDiagram participant M as 主站 Master participant Dev as ec_device (网卡) participant Ref as 参考时钟从站 participant S1 as 从站 1 participant S2 as 从站 2 Note over M,S2: 每个周期:ecrt_master_send() 触发 rect rgb(235, 248, 255) Note over M,Ref: 步骤 1:同步参考时钟 (ref_sync_datagram) M->>Dev: FPWR 0x0910 ← master_app_time (64 位) Dev->>Ref: 帧发送 (DMA) Note right of Ref: 参考时钟被同步到主站时间 end rect rgb(240, 255, 244) Note over M,Ref: 步骤 2:读取参考时间 (sync_datagram) M->>Dev: FPRD 0x0910 Dev->>Ref: 读取系统时间请求 Ref-->>Dev: 返回当前 64 位系统时间 T_ref Dev-->>M: 接收 T_ref Note right of M: 计算 app_time - T_ref<br/>得到漂移补偿偏移 Δ end rect rgb(250, 245, 255) Note over M,S2: 步骤 3:漂移补偿 (写入各从站偏移) M->>Dev: FPWR 0x0920 ← Δ₁ (S1 偏移) Dev->>S1: 校正 S1 系统时间偏移 M->>Dev: FPWR 0x0920 ← Δ₂ (S2 偏移) Dev->>S2: 校正 S2 系统时间偏移 end Note over M,S2: 纳秒级漂移被持续校正 (< 1μs 偏差)

漂移补偿算法流程

flowchart TD START["周期开始<br/>ecrt_master_send()"] --> REF_SYNC["ref_sync_datagram<br/>FPWR 0x0910 → 参考从站<br/>写入主站 app_time (64 位)"] REF_SYNC --> SEND["发送所有数据报<br/>ec_master_send_datagrams()"] SEND --> RECV["接收返回帧<br/>ec_master_receive_datagrams()"] RECV --> READ_REF["处理 sync_datagram<br/>读取参考时钟 T_ref (64 位)"] READ_REF --> CALC["计算漂移<br/>Δ = app_time - T_ref<br/>- 传播延迟补偿"] CALC --> CHECK{"|Δ| < 阈值?"} CHECK -->|"是"| APPLY["写入各从站 0x0920<br/>系统时间偏移寄存器"] CHECK -->|"否"| LIM["限幅处理<br/>防止过冲"] LIM --> APPLY APPLY --> NEXT["下一周期继续补偿"] NEXT --> START style START fill:#ebf8ff,stroke:#90cdf4 style CALC fill:#fefcbf,stroke:#f6e05e style APPLY fill:#f0fff4,stroke:#9ae6b4 style LIM fill:#fff5f5,stroke:#feb2b2

深入源码

源码:master/sync.h — ec_sync 结构体

master/sync.h — ec_sync_t

typedef struct {
    ec_slave_t *slave;               // 所属从站
    uint16_t physical_start_address; // 物理起始地址
    uint16_t default_length;         // 默认数据长度
    uint8_t control_register;        // 控制寄存器值
    uint8_t enable;                  // 使能位
    ec_pdo_list_t pdos;              // PDO 分配列表
} ec_sync_t;

源码:master/fsm_slave_config.c — DC 配置状态机

DC 配置是 Slave Config FSM 的一部分,发生在 PREOP → SAFEOP 转换过程中。相关常量:

master/fsm_slave_config.c — DC 相关常量

#define EC_DC_MAX_SYNC_DIFF_NS  10000       // 最大允许时钟偏差: 10μs
#define EC_DC_SYNC_WAIT_MS      5000        // 等待时钟同步最长: 5秒
#define EC_DC_START_OFFSET      100000000ULL // 周期启动偏移: 100ms

DC 配置状态机流程

stateDiagram-v2 [*] --> dc_clear_assign: 进入 DC 配置 dc_clear_assign --> dc_cycle: 清除 DC 激活<br/>(写 0x0981 = 0) dc_cycle --> dc_sync_check: 设置 SYNC0/SYNC1 周期<br/>(写 0x09A0, 0x09A4)<br/>设置脉冲长度 (0x0982) dc_sync_check --> dc_start: 时间差 < 10μs<br/>读取 0x092C 确认 dc_sync_check --> dc_sync_check: 等待同步<br/>(循环读取 0x092C) dc_start --> dc_assign: 设置启动时间<br/>(写 0x0990) dc_assign --> safeop: 激活 DC<br/>(写 0x0981 DCACT) note left of dc_clear_assign: 写入 0x0981\n清除旧的 DC 激活 note left of dc_cycle: 写入周期\nSYNC0 (0x09A0) 和 SYNC1 (0x09A4) note left of dc_sync_check: 读取 0x092C 检查系统时间差\n等待时钟同步偏差 < 10μs note left of dc_start: 写入 0x0990\n周期操作启动时间 note left of dc_assign: 写入 0x0981\nDC 激活 (DCACT)

DC 配置关键步骤

  1. dc_clear_assign: 写入 0x0981 清除旧的 DC 激活

  2. dc_cycle: 写入 0x09A0/0x09A4 设置 SYNC0/SYNC1 周期,写入 0x0982 设置脉冲长度

  3. dc_sync_check: 循环读取 0x092C 等待时钟同步 (<10μs)

  4. dc_start: 计算并写入 0x0990 周期操作启动时间

  5. dc_assign: 写入 0x0981 激活 DC 功能 (DCACT 寄存器)

ec_master_calc_dc() 完整调用链

以下展示主站 DC 同步的完整内核调用链:

flowchart TD subgraph "ec_master_calc_dc()" A["ec_master_calc_dc()"] --> B["ec_master_find_dc_ref_clock()"] B --> B1{"找到 DC 参考时钟?"} B1 -->|"否"| NO_DC["返回,不执行 DC"] B1 -->|"是"| C["ec_master_calc_topology()"] C --> C1["遍历所有从站<br/>计算 parent → child 延迟"] C1 --> D["ec_master_calc_transmission_delays()"] D --> D1["测量各段传播延迟<br/>(0x0900 端口接收时间)"] D1 --> D2["写入各从站 0x0928 延迟寄存器"] end subgraph "DC 数据报初始化" D2 --> E["ec_datagram_fpwr()<br/>ref_sync_datagram<br/>地址: 0x0910 (64 位)"] D2 --> F["ec_datagram_fprd()<br/>sync_datagram<br/>地址: 0x0910 (64 位)"] D2 --> G["ec_datagram_armw()<br/>sync64_datagram<br/>地址: 0x0910 (64 位)"] end subgraph "运行时周期补偿" E --> H["每个周期:<br/>ec_master_sync_slave_clocks()"] H --> H1["ref_sync: FPWR 主站时间 → 参考从站 0x0910"] H --> H2["sync: FPRD 读参考时间 0x0910 → 计算偏移"] H --> H3["写入各从站 0x0920 系统时间偏移寄存器"] end style A fill:#ebf8ff,stroke:#90cdf4 style NO_DC fill:#fff5f5,stroke:#feb2b2 style E fill:#f0fff4,stroke:#9ae6b4 style F fill:#f0fff4,stroke:#9ae6b4 style G fill:#f0fff4,stroke:#9ae6b4 style H fill:#faf5ff,stroke:#d6bcfa

SYNC0/SYNC1 信号时序与从站采样

sequenceDiagram participant M as 主站 Master participant Ref as 参考时钟从站 participant S1 as 从站 1 participant S2 as 从站 2 Note over M,S2: 周期 T = 1ms (例如) rect rgb(235, 248, 255) Note over Ref: SYNC0 ↑ (T₀) Note over S1: SYNC0 ↑ (T₀ + delay₁) Note over S2: SYNC0 ↑ (T₀ + delay₂) Note right of Ref: 所有从站在<br/>精确同一时刻<br/>触发数据采样<br/>(抖动 ~12ns) end M->>Ref: 发送 EtherCAT 帧 Ref->>S1: 转发 S1->>S2: 转发 Note over M,S2: 帧处理:输入数据被锁存 S2-->>S1: 返回帧 S1-->>Ref: 返回帧 Ref-->>M: 返回帧 rect rgb(240, 255, 244) Note over Ref: SYNC0 ↑ (T₀ + T) Note over S1: SYNC0 ↑ (T₀ + T + delay₁) Note over S2: SYNC0 ↑ (T₀ + T + delay₂) Note right of Ref: 输出数据在<br/>SYNC0 时刻更新 end

同步信号初始化步骤(编程指南)

N32H7x5EC 同步信号初始化流程

  1. 在 ESC 配置寄存器启用 DC SYNC 输出单元(bit2 = 1)

  2. 设置 SYNC/Latch PDI 配置寄存器(输出 SYNC0/1,配置驱动参数)

  3. 设置脉冲长度寄存器 0x0982(>0 为周期模式)

  4. 将同步单元分配给 ECAT 或 PDI

  5. 设置 SYNC0 周期时间 0x09A0 和 SYNC1 周期时间 0x09A4

  6. 设置周期操作启动时间 0x0990(应晚于当前时间)

  7. 设置 DC 激活寄存器 0x0981 启用 SYNC0/SYNC1 生成

SYNC0 vs SYNC1

SYNC0:主同步信号,触发所有从站的数据采样和输出更新。周期由 0x09A0 寄存器设置,内部更新速率 100 MHz,抖动约 12 ns。

SYNC1:辅助同步信号,依赖 SYNC0,可配置延迟。周期由 0x09A4 寄存器设置,通常为 SYNC0 周期的整数倍。

DC 中断方式

DC 同步信号中断有两种方式:

方式 抖动 说明
映射到 AL 事件请求寄存器 ~40 ns 合并为单个 IRQ_A (ECAT_OPB_IRQ)
直接连到微控制器中断输入 ~12 ns IRQ_B (ECAT_SYNC0_INT), IRQ_C (ECAT_SYNC1_INT),精度更高

二、AL 状态机 (Application Layer State Machine)

1.5 — EtherCAT 从站应用层状态机、状态转换条件与错误处理


概览

AL 状态机简介

每个 EtherCAT 从站具有一个应用层状态机 (AL State Machine),定义了从站的四种工作状态。主站通过向从站的 AL 状态寄存器 (0x0120) 写入目标状态来请求状态转换,从站完成内部初始化后自动转换到目标状态并确认。

完整 AL 状态机图

stateDiagram-v2 [*] --> Init: 从站上电 Init --> PreOp: 主站请求 0x02 PreOp --> Init: 主站请求 0x01 / 错误回退 PreOp --> SafeOp: 配置完成,主站请求 0x04 SafeOp --> PreOp: 主站请求 0x02 / 错误回退 SafeOp --> Op: 数据验证通过,主站请求 0x08 Op --> SafeOp: 主站请求 0x04 / 错误回退 Init --> Bootstrap: 主站请求 0x03 Bootstrap --> Init: 完成 / 超时 Op --> Error: 通信故障 / WKC 异常 SafeOp --> Error: 配置不匹配 PreOp --> Error: 邮箱初始化失败 Error --> Init: 主站请求 0x01 (错误恢复)

各状态能力对比

状态 代码 邮箱通信 过程数据输入 过程数据输出 说明
Init 0x01 不可用 不可用 不可用 上电初始状态,ESC 寄存器可访问
Pre-Op 0x02 可用 不可用 不可用 可配置从站参数 (SDO/PDO 映射)
Safe-Op 0x04 可用 可读 安全值 输入数据有效,输出处于安全状态
Op 0x08 可用 可读 可写 正常运行,完整数据交换

技术详情

状态转换请求-确认机制

sequenceDiagram participant M as 主站 Master participant S as 从站 Slave Note over M,S: 状态转换请求-确认流程 M->>S: 写 AL 状态请求 (0x0120)<br/>写入目标状态 Note over S: 从站开始内部初始化 loop 轮询检查 (超时 5s) M->>S: 读 AL 状态 (0x0130) S-->>M: 返回当前状态 end Note over M,S: 状态匹配 → 转换成功 Note over M,S: ACK_ERR 标志 → 需要确认错误 M->>S: 写当前状态确认 (0x0120)<br/>清除 ACK_ERR 标志

ESC 相关寄存器

地址 名称 读/写 说明
0x0120 AL Control 主站写入目标 AL 状态
0x0130 AL Status 从站当前 AL 状态 (含 ACK_ERR 位)
0x0134 AL Status Code 状态转换错误码 (详细错误信息)

常见 AL 状态码 (0x0134)

错误码 说明 常见原因
0x0000 无错误 正常状态
0x0001 无错误 (状态转换完成) 转换成功
0x0011 无效的状态转换 请求了不允许的转换
0x0012 未知状态 请求了不支持的状态
0x0013 Bootstrap 不支持 从站不支持 Bootstrap 模式
0x0014 无有效输出数据 过程数据输出未配置
0x0015 无效的输出配置 PDO 映射与 SM 配置不匹配
0x001A 无效的输入配置 输入 PDO 映射错误
0x001B 无效的 Sync Manager 配置 SM 地址或长度配置错误
0x001C 无有效的输入数据 输入过程数据无效
0x0020 从站需要重新扫描 从站配置变更

各状态转换要求

转换 前提条件 从站动作
Init → Pre-Op ESC 基本通信可用 初始化邮箱 (SM0/SM1),加载 EEPROM 配置
Pre-Op → Safe-Op SDO 配置完成,PDO 映射有效 初始化 FMMU,配置过程数据 SM,启动 DC (如支持)
Safe-Op → Op 输入数据有效,WKC 正常 使能输出数据,开始正常数据交换
任意 → Error 通信故障、WKC 异常、配置不匹配 设置 ACK_ERR 标志,停止输出
Error → Init 主站发送 Init 状态请求 完全复位,清除所有配置

深入源码

源码:master/fsm_change.h

master/fsm_change.h — ec_fsm_change 结构体

struct ec_fsm_change {
    ec_slave_t *slave;            // 目标从站
    ec_datagram_t *datagram;      // 使用的 datagram
    unsigned int retries;         // 超时重试次数

    void (*state)(ec_fsm_change_t *, ec_datagram_t *); // 当前状态函数
    ec_fsm_change_mode_t mode;    // 模式: 完整转换 / 仅确认
    ec_slave_state_t requested_state; // 请求的目标状态
    ec_slave_state_t old_state;   // 转换前的状态
    unsigned long jiffies_start;  // 转换计时起始
    uint8_t take_time;            // 是否记录发送时间戳
    uint8_t spontaneous_change;   // 检测到自发状态变化
};

State Change FSM 状态机 (9 个状态)

stateDiagram-v2 [*] --> start: 开始状态转换 state "发送状态请求" as start state "验证发送" as check state "检查当前状态" as status state "处理错误码" as code state "确认当前状态" as ack state "验证确认" as check_ack state "转换完成" as done state "转换失败" as error start --> check: 写入请求状态到 AL Control (0x0120) check --> status: 验证 Datagram WKC 检查 check --> check: 超时重试 status --> done: 当前状态 = 请求状态(转换成功) status --> code: 状态不匹配, 读取错误码 status --> error: 超时 (5秒) code --> ack: 读取 AL Status Code (0x0134) ack --> check_ack: 写入当前状态确认(清除 ACK_ERR) check_ack --> done: ACK_ERR 已清除 check_ack --> error: 确认失败 / 超时 done --> [*] error --> [*]

关键实现细节

FSM 实现要点

  • 超时机制: 使用 jiffies_start 记录起始时间,状态转换超时 EC_AL_STATE_CHANGE_TIMEOUT 为 5 秒

  • 重试机制: Datagram 发送失败时自动重试 EC_FSM_RETRIES

  • 两种模式: EC_FSM_CHANGE_MODE_FULL 完整转换, EC_FSM_CHANGE_MODE_ACK_ONLY 仅确认

  • 自发变化检测: spontaneous_change 标志检测从站主动上报的状态变化

  • ESC 寄存器: 写 0x0120 (AL Control), 读 0x0130 (AL Status), 读 0x0134 (AL Status Code)

posted @ 2026-04-14 21:05  沐多  阅读(1117)  评论(2)    收藏  举报