外观
EtherCAT 故障恢复示例
2026-07-29
本示例在 RK3506 AMP 工程上演示 EtherCAT 主站的 故障检测 → 告警展示 → 故障清除 → 主站恢复 完整闭环,并保留 CiA 402 伺服的 CSP(周期同步位置)模式运动控制,便于在工业现场验证链路抖动、从站掉线、AL 错误等典型故障的容错能力。
EtherCAT 故障恢复
硬件连接
需要准备硬件:
- 睿擎工业开发平台支持板卡 1 块
- EtherCAT 伺服驱动器一套(推荐
汇川 SV660N或者LICHUAN-LC10E) - 串口调试器、jlink 调试各一套
用网线将伺服控制器 IN 口与开发板 ETH 网口连接。伺服电机的电源线和编码器线分别接入电源口和 CN3 连接口。

创建工程点击展开
依次点击 “文件” -> “新建” -> "RT-Thread RuiChing App 项目"。

在弹出新建向导中选择 开发版 、BSP: 、示例 、 调试器/下载器。选择好之后点击 “完成”。

点击 “完成” 后,等待工程创建完成。

创建完成。

构建工程点击展开
单击工程使工程进入 Active-Debug 模式。

点击工具栏上的构建按钮进行工程编译。

构建成功后,会显示构建成功的信息。

固件下载点击展开
固化设备树

固化 APP

示例目的
- 演示主站侧故障订阅机制:通过
ecat_fault_wait/ecat_fault_get_history拉取故障队列,按类型分级处理。 - 演示告警分类与可读化输出:把
ec_fault_record_t中的level/type/state/ WKC / 端口快照翻译成人类可读的文本与建议。 - 演示安全停机 + 重启链路:致命故障(
ERROR/FATAL级、SLAVE_LOST、LINK_DOWN、DATAGRAM_ERROR)会自动请求停机;恢复命令会等待链路稳定、校验从站数量后重新拉起 OP。 - 演示运动受控于故障状态:
motor_run在故障未清除时会主动拒绝,避免带病运行。
控制命令
| 命令 | 用途 | 前置条件 / 备注 |
|---|---|---|
ecat_fault_demo | 创建监控线程并启动示例。 | 仅在主站未运行时调用。 |
ecat_master_stop | 请求主站停机。 | 必须 ecat_master_ready && ecat_master_running。会同步把 servo_run 拉低。 |
ecat_master_recover | 在故障清除后请求主站重新拉起 OP。 | 详见恢复流程说明;任何一项不满足都会立即拒绝并打印原因。 |
ecat_fault_show | 打印当前锁存故障 + 最近 8 条历史。 | 主站必须已 ready。 |
ecat_fault_clear | 清空故障记录并复位链路标志。 | 主站必须已 ready。 |
motor_run | 允许 PDO 回调进入运动状态机。 | 主站必须 running;故障锁存时会被拒绝。 |
motor_stop | 立即停止运动(仍保持 OP 状态)。 | 任何时刻可用。 |
motor_dir <0|1> | 设置运动方向。 | 0 反向、1 正向,默认 1。 |
核心示例代码
初始化 EtherCAT 主站与故障监控线程
- 网络接口配置:指定 EtherCAT 主站使用的网络接口,如 "e1"。
- 主站结构体初始化:通过
ecat_master_init(&demo_master)函数初始化主站结构体。 - 创建监控线程:线程名
ecat_demo,栈 20 KB,优先级 15,绑 CPU 3。
ethercat_fault_recovery.c
static void ethercat_entry(void *pram)
{
(void)pram;
ethercat_fault_demo_start("e1");
}
static void ecat_fault_demo_cmd(void)
{
rt_thread_t tid = RT_NULL;
tid = rt_thread_create("ecat_demo", ethercat_entry, RT_NULL, 20480, 15, 10);
if (tid != RT_NULL)
{
rt_thread_control(tid, RT_THREAD_CTRL_BIND_CPU, (void *)2);
rt_thread_startup(tid);
}
else
{
rt_kprintf("create ethercat thread fail.\n");
}
}
MSH_CMD_EXPORT_ALIAS(ecat_fault_demo_cmd, ecat_fault_demo, start EtherCAT fault recovery demo);主站启动流程
- 获取从站数量:调用
ecat_slavecount(master)获取总线上的从站数量。 - 配置从站 PDO 映射:定义 CiA 402 CSP 模式的 RPDO/TPDO 映射。
- 启动主站并检查状态:使用
ecat_master_start()和ecat_check_state()确保从站进入 OP 状态。 - 获取故障序列号:通过
ecat_fault_get_sequence()获取初始故障序列号用于后续轮询。
ethercat_fault_recovery.c
static rt_err_t start_master_operational(ec_master_t *master)
{
int slave_count;
uint16_t state;
rt_err_t err;
slave_count = ecat_slavecount(master);
if (slave_count <= 0)
{
return -RT_ERROR;
}
cia402_config_init();
for (int i = 0; i < slave_count; i++)
{
err = ecat_slave_config(master, i, &cia402_slave_config);
if (err != RT_EOK)
{
rt_kprintf("ethercat slave %d config failed, err:%d\n", i, err);
return err;
}
}
err = ecat_master_start(master);
if (err != RT_EOK)
{
rt_kprintf("ethercat master start failed, err:%d\n", err);
return err;
}
for (int i = 0; i < slave_count; i++)
{
state = EC_STATE_OPERATIONAL;
err = ecat_check_state(master, i, &state, ECAT_OP_TIMEOUT_MS);
if (err != RT_EOK)
{
rt_kprintf("Slave %d did not reach operational mode, err:%d\n", i, err);
return err;
}
}
ecat_fault_get_sequence(master, &ecat_fault_sequence);
ecat_master_ready = 1;
ecat_master_running = 1;
if (ecat_expected_slave_count < 0)
{
ecat_expected_slave_count = slave_count;
}
return RT_EOK;
}故障轮询与处理
- 等待故障事件:使用
ecat_fault_wait()等待新的故障记录。 - 拉取故障历史:调用
ecat_fault_get_history()批量获取故障记录。 - 故障分类处理:根据故障类型决定是否自动停机、记录链路状态。
ethercat_fault_recovery.c
static void poll_fault_records(ec_master_t *master)
{
rt_err_t err;
uint32_t new_sequence = ecat_fault_sequence;
ec_fault_record_t records[ECAT_FAULT_HISTORY_BATCH];
uint32_t actual_count = 0;
err = ecat_fault_wait(master,
ecat_fault_sequence,
ECAT_FAULT_WAIT_MS,
&new_sequence);
if (err != RT_EOK)
{
return;
}
ecat_fault_sequence = new_sequence;
do
{
actual_count = 0;
err = ecat_fault_get_history(master,
ecat_last_fault_id,
records,
sizeof(records) / sizeof(records[0]),
&actual_count);
if (err != RT_EOK || actual_count == 0)
{
break;
}
for (uint32_t i = 0; i < actual_count; i++)
{
handle_fault_record(&records[i]);
ecat_last_fault_id = records[i].fault_id;
}
} while (actual_count == (sizeof(records) / sizeof(records[0])));
}故障详情打印与建议
- 故障等级与类型转换:将数值转换为可读字符串(INFO/WARNING/ERROR/FATAL)。
- 从站状态与端口信息:打印从站 AL 状态码、WKC、端口链路状态等。
- 处理建议:根据故障类型给出针对性的排查建议。
ethercat_fault_recovery.c
static void print_fault_detail(const ec_fault_record_t *fault)
{
rt_kprintf("[Fault] id=%u seq=%u level=%s type=%s slave=%u code=0x%x reason=%s\n",
fault->fault_id,
fault->sequence,
fault_level_name(fault->level),
fault_type_name(fault->type),
fault->slave_index,
fault->error_code,
fault->reason);
if (fault->slave_index == EC_FAULT_NO_SLAVE)
{
if (fault->actual_wkc || fault->expected_wkc)
{
rt_kprintf("[Fault] master wkc=%u/%u\n",
fault->actual_wkc,
fault->expected_wkc);
}
}
else
{
rt_kprintf("[Fault] slave alias=%u vendor=0x%x product=0x%x state=%s al=0x%x wkc=%u/%u\n",
fault->alias,
fault->vendor_id,
fault->product_code,
fault_state_name(fault->current_state),
fault->al_status_code,
fault->actual_wkc,
fault->expected_wkc);
for (int i = 0; i < EC_FAULT_PORT_COUNT; i++)
{
rt_kprintf("[Fault] port%d desc=0x%x link=%u loop=%u signal=%u next=%u delay=%u\n",
i,
fault->ports[i].desc,
fault->ports[i].link_up,
fault->ports[i].loop_closed,
fault->ports[i].signal_detected,
fault->ports[i].next_slave,
fault->ports[i].delay_to_next_dc);
}
}
print_fault_suggestion(fault);
}主站恢复流程
- 等待链路恢复:轮询主站状态,等待链路 UP 且有从站响应。
- 等待从站数量稳定:确保从站数量不再变化后再进行恢复。
- 校验从站数量:恢复后从站数量必须与初始扫描时一致。
- 重新启动主站:调用
start_master_operational()重新拉起 OP。
ethercat_fault_recovery.c
static rt_err_t recover_master(ec_master_t *master)
{
rt_err_t err;
{
int retry = 0;
ec_master_state_t state_info;
while (retry < ECAT_RECOVERY_LINK_WAIT_RETRY)
{
ecat_master_state(master, &state_info);
if (state_info.link_up && state_info.slaves_responding > 0)
{
break;
}
rt_thread_mdelay(ECAT_RECOVERY_POLL_MS);
retry++;
}
if (retry >= ECAT_RECOVERY_LINK_WAIT_RETRY)
{
rt_kprintf("[Recovery] Timeout - no slaves found\n");
return -RT_ETIMEOUT;
}
}
{
int prev = -1;
int cur, retry = 0;
while (retry < ECAT_RECOVERY_STABLE_WAIT_RETRY)
{
cur = ecat_slavecount(master);
if (cur > 0 && cur == prev)
{
break;
}
prev = cur;
rt_thread_mdelay(ECAT_RECOVERY_POLL_MS);
retry++;
}
rt_thread_mdelay(ECAT_RECOVERY_SETTLE_MS);
}
err = check_recovery_slave_count(master);
if (err != RT_EOK)
{
return err;
}
err = start_master_operational(master);
if (err != RT_EOK)
{
rt_kprintf("[Recovery] Failed to recover master: %d\n", err);
return err;
}
link_down_detected = 0;
link_up_detected = 0;
return RT_EOK;
}PDO 回调与 CiA 402 状态机
- 伺服状态机切换:根据状态字执行 CiA 402 状态机切换。
- 目标位置更新:在 OP 状态下根据方向标志周期性更新目标位置。
ethercat_fault_recovery.c
static void pdo_callback(uint16_t slave_index, uint8_t *output, uint8_t *input)
{
struct rpdo_csp *rmap = (struct rpdo_csp *)output;
struct tpdo_csp *tmap = (struct tpdo_csp *)input;
(void)slave_index;
if (servo_run == 0)
{
do {
servo_switch_op(rmap, tmap);
rmap->control_word = 0x2;
rmap++; tmap++;
} while ((uint8_t *)rmap < input);
return;
}
do {
servo_switch_op(rmap, tmap);
if (rmap->control_word == 0x7) {
rmap->mode_byte = CIA402_MODE_CSP;
rmap->dest_pos = tmap->cur_pos;
}
if (rmap->control_word == 0xf) {
rmap->dest_pos = tmap->cur_pos;
if (servo_dir == 0) {
rmap->dest_pos -= CIA402_TARGET_STEP;
} else {
rmap->dest_pos += CIA402_TARGET_STEP;
}
}
rmap++; tmap++;
} while ((uint8_t *)rmap < input);
}示例运行
启动示例
msh > ecat_fault_demo启动成功后会看到类似输出:
Found slaves count:N
EtherCAT fault recovery demo started.正常运动
msh > motor_run
msh > motor_dir 1伺服按 CIA402_TARGET_STEP 每周期步进目标位置;可用 motor_dir 0 反向,motor_stop 立即停止。
模拟链路故障(可选)
- 拔掉 EtherCAT 网线或关闭从站电源。
- 主栈产生
MASTER_LINK_DOWN/SLAVE_LOST事件,监控线程会自动打印详情并请求停机。
查看故障
msh > ecat_fault_show打印当前锁存的故障和最近 8 条历史记录。
清除故障并恢复
msh > ecat_fault_clear
msh > ecat_master_recover恢复成功后打印 [Monitor] Recovery OK, N slaves。
主动停机
msh > ecat_master_stop故障类型速查
| 类型 | 级别(默认) | 触发动作 | 处理建议 |
|---|---|---|---|
TOPOLOGY_MISMATCH | ERROR | 自动停机 | 检查扫描到的从站顺序、别名、地址。 |
PORT_LINK_ABNORMAL | WARNING | 仅记录 | 检查端口 link / signal,必要时重新插拔。 |
SLAVE_LOST | ERROR | 自动停机 | 检查从站供电、网线;恢复后 ecat_fault_clear + ecat_master_recover。 |
SLAVE_STATE_CHANGED | INFO | 仅记录 | 关注从站自身状态切换。 |
SLAVE_STATE_ABNORMAL | ERROR | 自动停机 | 查看 AL status code,修复从站故障后清错。 |
SLAVE_AL_ERROR | ERROR | 自动停机 | 同上。 |
SLAVE_WKC_LOW / MASTER_WKC_LOW | WARNING | 仅记录 | 检查通信质量、屏蔽、周期负载。 |
DATAGRAM_TIMEOUT / DATAGRAM_ERROR | ERROR | 自动停机 | 检查线缆、交换机、周期时间。 |
MASTER_LINK_DOWN | ERROR | 自动停机 | 物理链路已断开,等待物理恢复。 |
MASTER_LINK_UP | INFO | 仅记录 | 链路恢复提示,需 ecat_fault_clear 后再恢复主站。 |
COMM_QUALITY_WARNING / COMM_QUALITY_ERROR | WARNING/ERROR | 仅记录或停机 | 评估 CRC 错误率、端口抖动。 |
常见问题
- 看到
ethercat master is not ready:监控线程还没把从站拉到 OP,先等几秒再发命令。 ecat_master_recover提示slave count mismatch:恢复后从站数量与最初不同,确认现场没有增减设备后再恢复。- 运动后立刻停机:很可能是
fault_should_stop_master()判定为致命故障,先ecat_fault_show看类型,再针对性处理。 link is not confirmed up:检测到过掉线但还没收到MASTER_LINK_UP,确认物理链路和从站都已上电后再重试。create ethercat thread fail.:栈或优先级资源不足,确认RT_USING_HEAP与RT_THREAD_PRIORITY_32等配置。
