3. EtherCAT主站状态机初始化与从站状态转换

状态机转换的核心对象与顺序

EtherCAT 从站上电后处于 INIT 状态,主站必须按 INIT → PREOP → SAFEOP → OP 的顺序逐级请求,每级都通过写 AL Control(0x0120)寄存器发起,再轮询 AL Status(0x0130)确认从站真正进入目标状态。跳级或逆序请求会被从站拒绝,并在 AL Status Code 中给出错误码。

本案例针对单个从站,用主站依次发起三次状态请求,记录每次从请求发出到状态位匹配的耗时,并读取 AL Status Code 判断是否出错。关键寄存器与状态编码如下:

对象地址/取值含义
AL Control0x0120主站写入目标状态请求
AL Status0x0130从站当前状态 + 错误标志
AL Status Code0x0134转换失败时的错误码
INIT0x01初始状态,无过程数据通信
PREOP0x02可访问邮箱,可读写 SDO
SAFEOP0x04过程数据输入有效,输出保持安全值
OP0x08过程数据输入输出全部有效
EtherCAT 从站状态转换与耗时记录 INIT 0x01 PREOP 0x02 SAFEOP 0x04 OP 0x08 写0x0120=02 写0x0120=04 写0x0120=08 轮询 AL Status (0x0130):状态位 == 目标值 且 错误标志位 == 0 若超时或错误标志置位 → 读取 AL Status Code (0x0134) 记录错误码 耗时记录 t1: INIT→PREOP t2: PREOP→SAFEOP t3: SAFEOP→OP t_total = t1+t2+t3 常见 AL Status Code 判读 0x0000:无错误,转换成功 0x0011:无效状态请求(跳级/逆序) 0x0016:邮箱通信未就绪(PREOP 失败) 0x0024:过程数据看门狗超时(SAFEOP/OP 失败)

主站初始化流程实现

下面用一段可运行的伪代码结构展示主站如何依次请求状态并记录耗时。实际工程中,寄存器读写通过主站库(如 SOEM 的 ec_readstate/ec_writestate 或 IgH EtherCAT Master 的 ecrt_master_slave_config 接口)完成,这里用 readReg/writeReg 抽象表示。

// 目标状态序列
const uint8_t STATE_SEQ[3] = {0x02, 0x04, 0x08}; // PREOP, SAFEOP, OP
const char* STATE_NAME[3] = {"PREOP", "SAFEOP", "OP"};

// 单次状态转换:请求 + 轮询确认 + 耗时记录
int transitionTo(uint16_t slave, uint8_t target, double* elapsed_ms) {
    struct timespec t0, t1;
    clock_gettime(CLOCK_MONOTONIC, &t0);

    writeReg(slave, 0x0120, target);          // 写 AL Control

    uint16_t status;
    do {
        status = readReg(slave, 0x0130);      // 读 AL Status
        if (status & 0x0010) {                // 错误标志位
            uint16_t code = readReg(slave, 0x0134);
            printf("  错误: AL Status Code = 0x%04X\n", code);
            return -1;
        }
        usleep(1000);                         // 1ms 轮询间隔
    } while ((status & 0x000F) != target);    // 状态位匹配

    clock_gettime(CLOCK_MONOTONIC, &t1);
    *elapsed_ms = (t1.tv_sec - t0.tv_sec) * 1000.0
                + (t1.tv_nsec - t0.tv_nsec) / 1e6;
    return 0;
}

// 主初始化流程
void initSlave(uint16_t slave) {
    double total = 0.0;
    for (int i = 0; i < 3; i++) {
        double ms = 0.0;
        printf("请求 %s ... ", STATE_NAME[i]);
        if (transitionTo(slave, STATE_SEQ[i], &ms) == 0) {
            printf("成功, 耗时 %.2f ms\n", ms);
            total += ms;
        } else {
            printf("失败\n");
            return;
        }
    }
    printf("总耗时: %.2f ms\n", total);
}

运行与结果判读

在从站正常上电、邮箱配置正确的前提下,典型观察结果是:INIT→PREOP 耗时通常在几毫秒到几十毫秒(取决于邮箱初始化),PREOP→SAFEOP 和 SAFEOP→OP 通常更快,因为过程数据通道已建立。若某一步耗时异常长或返回错误码,可按以下方向排查:

  • 0x0011:检查是否跳过了中间状态,必须严格按 INIT→PREOP→SAFEOP→OP 顺序。
  • 0x0016:邮箱通信未就绪,检查 EEPROM 中邮箱配置是否与主站一致。
  • 0x0024:过程数据看门狗超时,检查 SAFEOP→OP 时 PDO 映射和看门狗时间设置。
耗时记录建议使用单调时钟(CLOCK_MONOTONIC),避免系统时间调整影响测量。轮询间隔不宜过短,1ms 是常见折中值,既能快速响应又不会占满总线。

验证清单

检查项通过条件
状态顺序三次请求依次为 0x02、0x04、0x08,无跳级
状态确认每次轮询到 AL Status 低 4 位等于目标值
错误标志转换过程中 AL Status 的 bit4 始终为 0
耗时记录t1、t2、t3 均有数值,且 t_total = t1+t2+t3
错误码若失败,AL Status Code 非 0x0000 且可对应到具体原因