CiA 402 -- 伺服驱动器
CiA 402 (IEC 61800-7-204) 是基于 CoE 的伺服驱动器设备协议。
CiA 402 运行在 CoE (SDO) 之上,仅当从站支持 CoE 时可用。
状态枚举
typedef enum {
CIA402_NOT_READY = 0, /* 未就绪 */
CIA402_SWITCH_ON_DISABLED = 1, /* 开关禁用 */
CIA402_READY_TO_SWITCH_ON = 2, /* 准备开关 */
CIA402_SWITCHED_ON = 3, /* 已开关 */
CIA402_OPERATION_ENABLED = 4, /* 运行使能 */
CIA402_QUICK_STOP_ACTIVE = 5, /* 快速停止 */
CIA402_FAULT_REACTION = 6, /* 故障响应 */
CIA402_FAULT = 7, /* 故障 */
CIA402_UNKNOWN = 99 /* 未知 */
} cia402_state_t;
操作模式枚举
typedef enum {
CIA402_PP = 1, /* 轮廓位置模式 */
CIA402_VL = 2, /* 速度模式 */
CIA402_PV = 3, /* 轮廓速度模式 */
CIA402_PT = 4, /* 轮廓转矩模式 */
CIA402_HM = 6, /* 回零模式 */
CIA402_IP = 7, /* 插补位置模式 */
CIA402_CSP = 8, /* 周期同步位置模式 */
CIA402_CSV = 9, /* 周期同步速度模式 */
CIA402_CST = 10 /* 周期同步转矩模式 */
} cia402_mode_t;
PDO 初始化
CiA402_InitializePdoOffsets()
int CiA402_InitializePdoOffsets(uint16_t master, uint16_t slave);
预扫描 CiA 402 标准对象(ControlWord/StatusWord/PositionActual 等)在 IOmap 中的字节偏移并缓存。OP 状态后调用一次,后续所有 CiA402_* 函数走零拷贝路径,无需每次重新扫描。等价于 cia402_scan_pdo() 的封装。
状态控制
CiA402_Enable()
int CiA402_Enable(uint16_t master, uint16_t slave, int max_retries);
一步使能。DLL 内部自动执行完整的状态链转换(故障清除 -> Shutdown -> Switch On -> Enable Operation)。
参数:
master(uint16_t) — 主站索引slave(uint16_t) — 从站索引max_retries(int) — 最大重试次数
返回值:
int— 0 成功,非 0 失败
CiA402_Disable()
int CiA402_Disable(uint16_t master, uint16_t slave);
int CiA402_DisableOperation(uint16_t master, uint16_t slave);
int CiA402_QuickStop(uint16_t master, uint16_t slave);
CiA402_Disable()完全失能(写 ControlWord =0x0000,禁用电压)CiA402_DisableOperation()仅退出 Operation Enabled 回到 Switched On(保电不输出力矩)CiA402_QuickStop()触发 Quick Stop(Controlword Bit2=0),按0x605A选项码减速停止
CiA402_FaultReset()
int CiA402_FaultReset(uint16_t master, uint16_t slave);
故障复位。自动产生 ControlWord Bit 7 上升沿,确保故障清除可靠完成。
参数:
master(uint16_t) — 主站索引slave(uint16_t) — 从站索引
返回值:
int— 0 成功,非 0 失败
CiA402_WriteControlWord()
int CiA402_WriteControlWord(uint16_t master, uint16_t slave, uint16_t cw);
写入控制字 (0x6040)。
参数:
cw(uint16_t) — 控制字值
返回值:
int— 0 成功,非 0 失败
状态读取
StateDrive
由 CiA402_ParseState() 返回的当前驱动器状态枚举值(cia402_state_t),等价于 C# 的 slave.CoE.CiA402.StateDrive。典型流程: 读 StatusWord → ParseState → 判断当前是否处于 OPERATION_ENABLED / FAULT 等。
Statusword
通过 CiA402_ReadStatusWord() 读 0x6041 实时状态字。位含义见 状态字位掩码常量。Bit10 (TargetReached)、Bit12 (Set-point Ack / HomingAttained)、Bit13 (Following Error / HomingError) 是 PP / HM 握手关键。
Controlword
通过 CiA402_WriteControlWord() 写 0x6040 控制字。Bit4 (NewSetpoint / StartHoming)、Bit6 (PP 相对/绝对)、Bit8 (Halt) 是常用应用层位。
CiA402_ReadStatusWord()
uint16_t CiA402_ReadStatusWord(uint16_t master, uint16_t slave);
读取状态字 (0x6041)。
返回值:
uint16_t— StatusWord 值
CiA402_ParseState()
cia402_state_t CiA402_ParseState(uint16_t statusword);
从 StatusWord 解析当前驱动器状态。
参数:
statusword(uint16_t) — StatusWord 值
返回值:
cia402_state_t— 驱动器状态
CiA402_GetEnableCommand()
uint16_t CiA402_GetEnableCommand(uint16_t statusword);
根据当前 StatusWord 计算下一步使能所需的 ControlWord。
参数:
statusword(uint16_t) — StatusWord 值
返回值:
uint16_t— 应写入的 ControlWord 值
操作模式
CiA402_SetMode()
int CiA402_SetMode(uint16_t master, uint16_t slave, int8_t mode);
设置操作模式 (0x6060)。
参数:
mode(int8_t) — 操作模式 (cia402_mode_t)
返回值:
int— 0 成功,非 0 失败
CiA402_GetMode()
int8_t CiA402_GetMode(uint16_t master, uint16_t slave);
读取当前操作模式 (0x6061)。
返回值:
int8_t— 操作模式 (cia402_mode_t)
PDO 映射扫描 (ethercat_advanced.h)
cia402_pdo_map_t
typedef struct {
int controlword; /* 0x6040 RxPDO 字节偏移 (-1=未映射) */
int target_position; /* 0x607A */
int target_velocity; /* 0x60FF */
int target_torque; /* 0x6071 */
int modes_of_operation; /* 0x6060 */
int statusword; /* 0x6041 TxPDO 字节偏移 (-1=未映射) */
int position_actual; /* 0x6064 */
int velocity_actual; /* 0x606C */
int torque_actual; /* 0x6077 */
int modes_display; /* 0x6061 */
} cia402_pdo_map_t;
cia402_scan_pdo()
int cia402_scan_pdo(dll_t* dll, uint16_t master, uint16_t slave,
cia402_pdo_map_t* map);
扫描 CiA 402 PDO 映射,自动定位各对象在 IOmap 中的字节偏移。
参数:
dll(dll_t*) — DLL 实例master(uint16_t) — 主站索引slave(uint16_t) — 从站索引map(cia402_pdo_map_t*) — 输出映射信息
返回值:
int— 0 成功,非 0 失败
PDO 映射便捷读写
int cia402_read_statusword(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, uint16_t* out);
int cia402_write_controlword(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, uint16_t val);
int cia402_read_position(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int32_t* out);
int cia402_write_target_position(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int32_t val);
int cia402_read_velocity(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int32_t* out);
int cia402_write_target_velocity(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int32_t val);
int cia402_read_torque(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int16_t* out);
int cia402_write_target_torque(dll_t* dll, uint16_t master, uint16_t slave,
const cia402_pdo_map_t* map, int16_t val);
所有函数返回 0 成功,非 0 失败。
示例:
#include "ethercat_advanced.h"
/* 扫描 PDO 映射 */
cia402_pdo_map_t map;
if (cia402_scan_pdo(&dll, master, 1, &map) == 0) {
printf("StatusWord 偏移: %d\n", map.statusword);
printf("ControlWord 偏移: %d\n", map.controlword);
printf("实际位置偏移: %d\n", map.position_actual);
printf("目标位置偏移: %d\n", map.target_position);
/* 通过映射读取状态字 */
uint16_t sw;
cia402_read_statusword(&dll, master, 1, &map, &sw);
printf("StatusWord: 0x%04X\n", sw);
/* 写入目标位置 */
cia402_write_target_position(&dll, master, 1, &map, 100000);
}
运动参数 (SDO 访问)
以下参数通过 SDO 读写,适合参数配置。高频控制请使用 PDO 映射。
| 对象 | 函数模式 | 类型 | 说明 |
|---|---|---|---|
| 0x6064 | SDOread(master, slave, 0x6064, 0) | int32 | 实际位置 |
| 0x606C | SDOread(master, slave, 0x606C, 0) | int32 | 实际速度 |
| 0x6077 | SDOread(master, slave, 0x6077, 0) | int16 | 实际转矩(千分之额定) |
| 0x607A | SDOwrite(master, slave, 0x607A, 0) | int32 | 目标位置 |
| 0x60FF | SDOwrite(master, slave, 0x60FF, 0) | int32 | 目标速度 |
| 0x6071 | SDOwrite(master, slave, 0x6071, 0) | int16 | 目标转矩 |
| 0x6081 | SDOwrite | uint32 | 轮廓速度 |
| 0x6083 | SDOwrite | uint32 | 轮廓加速度 |
| 0x6084 | SDOwrite | uint32 | 轮廓减速度 |
| 0x6085 | SDOwrite | uint32 | 快速停止减速度 |
| 0x607E | SDOwrite | uint8 | 极性(Bit6=速度反转, Bit7=位置反转) |
| 0x6086 | SDOwrite | int16 | 运动轮廓类型(0=梯形, 1=S形) |
| 0x605A | SDOwrite | int16 | 快速停止选项码 |
| 0x6072 | SDOwrite | uint16 | 最大转矩(千分之额定) |
| 0x6076 | SDOwrite | uint32 | 电机额定转矩(mNm) |
| 0x60B0 | SDOwrite | int32 | 位置偏移(CSP 模式叠加) |
| 0x60B1 | SDOwrite | int32 | 速度偏移(CSV 模式叠加) |
| 0x60C2:01 | SDOwrite | uint8 | 插补时间周期值 |
| 0x60C2:02 | SDOwrite | int8 | 插补时间周期指数(周期 = 值 × 10^指数 秒) |
| 0x60E0 | SDOwrite | uint16 | 正方向力矩限制(千分之额定) |
| 0x60E1 | SDOwrite | uint16 | 负方向力矩限制(千分之额定) |
回零参数
| 对象 | 类型 | 说明 |
|---|---|---|
| 0x6098 | int8 | 回零方法 |
| 0x607C | int32 | 回零偏移 |
| 0x6099:01 | uint32 | 回零搜索速度 |
| 0x6099:02 | uint32 | 回零爬行速度 |
| 0x609A | uint32 | 回零加速度 |
触探功能 (Touch Probe)
ConfigureTouchProbe()
/* 通过 SDO 写 0x60B8 配置 Touch Probe 功能字 */
uint16_t fn = 0x0001; /* Bit0=启用探针1, Bit1=边沿选择 */
SDOwrite(master, slave, 0x60B8, 0, FALSE, sizeof(fn), &fn, EC_TIMEOUTRXM);
| 对象 | 类型 | 说明 |
|---|---|---|
| 0x60B8 | uint16 | Touch Probe 功能控制(Bit0=启用, Bit1=边沿选择) |
| 0x60B9 | uint16 | Touch Probe 状态(只读) |
| 0x60BA | int32 | 正边沿捕获位置(只读) |
| 0x60BB | int32 | 负边沿捕获位置(只读) |
数字 IO
| 对象 | 类型 | 说明 |
|---|---|---|
| 0x60FD | uint32 | 数字输入(只读,32 位位图) |
| 0x60FE:01 | uint32 | 数字输出(读写,32 位位图) |
软件位置限制
| 对象 | 类型 | 说明 |
|---|---|---|
| 0x607D:01 | int32 | 最小位置限制 |
| 0x607D:02 | int32 | 最大位置限制 |
快速运动控制
NewSetpoint()
PP 模式发送新定位命令: 写 0x607A 目标位置 + 触发 Controlword Bit4 (NewSetpoint)。relative=TRUE 时同时设置 Bit6 (Relative)。
ClearNewSetpoint()
PP 模式清除 NewSetpoint 标志(Controlword Bit4=0)。在 TargetReached (Statusword Bit10=1) 后调用,完成 Set-point Ack 握手。
StartHoming()
HM 模式启动回零: 通过 SDO 配置 0x6098 (HomingMethod) / 0x607C (HomeOffset) 后, Controlword Bit4=1 启动。HomingAttained (Statusword Bit12) 表示完成, HomingError (Bit13) 表示错误。
PP / HM 握手由应用通过 PDO/SDO 操作 Controlword 完成,典型流程:
/* PP 模式发送新设定点 */
uint16_t cw = 0x000F; /* Operation Enable */
cw |= 0x0010; /* Bit4: New Setpoint */
if (relative) cw |= 0x0040; /* Bit6: Relative */
dll.CiA402_WriteControlWord(master, slave, cw);
/* TargetReached 后清 Bit4: */
dll.CiA402_WriteControlWord(master, slave, cw & ~0x0010);
/* HM 启动: 通过 SDO 配置 0x6098/0x607C 后, Controlword Bit4=1 启动 */
回零相关参数:
0x6098(int8) — 回零方法 (HomingMethod)0x607C(int32) — 回零偏移 (HomeOffset)
回零状态位 (Statusword):
- Bit 12 — HomingAttained
- Bit 13 — HomingError
驱动器信息
SupportedDriveModes
通过 SDO 0x6502 (uint32) 读取支持的驱动模式位掩码: Bit 0=PP, Bit 1=VL, Bit 2=PV, Bit 3=PT, Bit 5=HM, Bit 6=IP, Bit 7=CSP, Bit 8=CSV, Bit 9=CST。
IsModeSupported
(SupportedDriveModes >> bit) & 1 即可判断指定模式是否可用。
SupportedHomingMethods
通过 SDO 0x60E3 (int8 数组) 读取驱动器支持的回零方法编号列表。subindex 0 为列表长度, 1..N 为各方法编号。
TxPDO 数据有效性
通过 SDO 读取 0x603E 判断 TxPDO 数据是否有效。非零表示驱动器 TxPDO 数据不可信,例如编码器未就绪时位置值无意义。
uint8_t invalid = 0;
int sz = sizeof(invalid);
SDOread(master, slave, 0x603E, 0, FALSE, &sz, &invalid, EC_TIMEOUTRXM);
if (invalid == 0) {
/* TxPDO 数据有效,可安全使用 */
int32_t pos;
cia402_read_position(&dll, master, slave, &map, &pos);
}
驱动器信息表
0x6502(uint32) — 支持的驱动模式位掩码(Bit0=PP, Bit7=CSP, Bit8=CSV, Bit9=CST)0x60E3(int8[]) — 支持的回零方法列表0x603E(uint8) — TxPDO 数据有效性(非零=数据无效)
同步功能
| 对象 | 类型 | 访问 | 说明 |
|---|---|---|---|
| 0x60D9:01 | uint16 | 读写 | 同步设置 (SynchronizationSettings),同步使能位掩码 |
| 0x60DA | uint16 | 只读 | 驱动同步状态 (DriveSyncStatus),指示驱动器是否已同步到主站时钟 |
/* 读取驱动器同步状态 */
uint16_t sync_status = 0;
int sz = sizeof(sync_status);
SDOread(master, slave, 0x60DA, 0, FALSE, &sz, &sync_status, EC_TIMEOUTRXM);
printf("同步状态: 0x%04X\n", sync_status);
控制字命令常量
| 常量值 | 说明 |
|---|---|
| 0x0006 | Shutdown — 关机 |
| 0x0007 | Switch On — 开启 |
| 0x000F | Enable Operation — 使能运行 |
| 0x0000 | Disable Voltage — 禁用电压 |
| 0x0002 | Quick Stop — 快速停止 |
| 0x0080 | Fault Reset — 故障复位 |
状态字位掩码常量
| Bit | 掩码 | 说明 |
|---|---|---|
| Bit 0 | 0x0001 | Ready to Switch On |
| Bit 1 | 0x0002 | Switched On |
| Bit 2 | 0x0004 | Operation Enabled |
| Bit 3 | 0x0008 | Fault |
| Bit 4 | 0x0010 | Voltage Enabled |
| Bit 5 | 0x0020 | Quick Stop |
| Bit 7 | 0x0080 | Warning |
| Bit 9 | 0x0200 | Remote |
| Bit 10 | 0x0400 | Target Reached |
| Bit 12 | 0x1000 | Homing Attained / Set-point Ack |
| Bit 13 | 0x2000 | Homing Error / Following Error |
完整示例
#define DYNAMIC_LOAD
#include "ethercat.h"
#include "ethercat_advanced.h"
#include <stdio.h>
int main(int argc, char* argv[])
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, argv[1], "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 15000);
dll.Start(master);
uint16_t slave = 1;
/* 检查初始状态 */
uint16_t sw = dll.CiA402_ReadStatusWord(master, slave);
cia402_state_t state = dll.CiA402_ParseState(sw);
printf("当前状态: %d (SW=0x%04X)\n", state, sw);
/* 故障复位 */
if (state == CIA402_FAULT) {
dll.CiA402_FaultReset(master, slave);
}
/* 设置 CSP 模式 */
dll.CiA402_SetMode(master, slave, CIA402_CSP);
printf("操作模式: %d\n", dll.CiA402_GetMode(master, slave));
/* 一步使能 */
if (dll.CiA402_Enable(master, slave, 50) == 0) {
printf("驱动使能成功\n");
/* 扫描 PDO 映射 */
cia402_pdo_map_t map;
if (cia402_scan_pdo(&dll, master, slave, &map) == 0) {
/* 读取实际位置 */
int32_t actual;
cia402_read_position(&dll, master, slave, &map, &actual);
printf("实际位置: %d\n", actual);
/* 写入目标位置 */
cia402_write_target_position(&dll, master, slave, &map, actual + 1000);
}
}
getchar();
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}
PP 模式 — 轮廓位置控制
#define DYNAMIC_LOAD
#include "ethercat.h"
#include "ethercat_advanced.h"
int main(int argc, char* argv[])
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, argv[1], "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 15000);
dll.Start(master);
uint16_t slave = 1;
/* 设置 PP 模式 */
dll.CiA402_SetMode(master, slave, CIA402_PP);
/* 配置轮廓参数 (SDO) */
uint32_t vel = 10000, acc = 50000, dec = 50000;
SDOwrite(master, slave, 0x6081, 0, FALSE, sizeof(vel), &vel, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x6083, 0, FALSE, sizeof(acc), &acc, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x6084, 0, FALSE, sizeof(dec), &dec, EC_TIMEOUTRXM);
/* 使能 */
if (dll.CiA402_Enable(master, slave, 50) == 0) {
printf("驱动使能成功\n");
/* 发送定位命令: 写目标位置 0x607A, 然后 Controlword Bit4=NewSetpoint */
int32_t target = 100000;
SDOwrite(master, slave, 0x607A, 0, FALSE, sizeof(target), &target, EC_TIMEOUTRXM);
uint16_t cw = 0x001F; /* Operation Enable + NewSetpoint */
dll.CiA402_WriteControlWord(master, slave, cw);
/* 等待到达 */
uint16_t sw;
do {
sw = dll.CiA402_ReadStatusWord(master, slave);
} while ((sw & 0x0400) == 0); /* 等待 TargetReached (Bit 10) */
printf("目标已到达\n");
dll.CiA402_WriteControlWord(master, slave, cw & ~0x0010); /* 清 NewSetpoint */
}
getchar();
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}
HM 模式 — 回零
#define DYNAMIC_LOAD
#include "ethercat.h"
int main(int argc, char* argv[])
{
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, argv[1], "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 15000);
dll.Start(master);
uint16_t slave = 1;
/* 设置 HM 模式 */
dll.CiA402_SetMode(master, slave, CIA402_HM);
/* 设置回零参数 */
int8_t method = 35; /* 当前位置回零 */
int32_t offset = 0;
uint32_t search_speed = 5000, zero_speed = 500, homing_acc = 10000;
SDOwrite(master, slave, 0x6098, 0, FALSE, sizeof(method), &method, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x607C, 0, FALSE, sizeof(offset), &offset, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x6099, 1, FALSE, sizeof(search_speed), &search_speed, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x6099, 2, FALSE, sizeof(zero_speed), &zero_speed, EC_TIMEOUTRXM);
SDOwrite(master, slave, 0x609A, 0, FALSE, sizeof(homing_acc), &homing_acc, EC_TIMEOUTRXM);
/* 使能 */
if (dll.CiA402_Enable(master, slave, 50) == 0) {
printf("驱动使能成功,开始回零...\n");
/* 启动回零: Controlword Bit4=Start Homing */
dll.CiA402_WriteControlWord(master, slave, 0x001F);
/* 等待回零完成 */
uint16_t sw;
do {
sw = dll.CiA402_ReadStatusWord(master, slave);
if (sw & 0x2000) { /* Bit 13: HomingError */
printf("回零错误!\n");
break;
}
} while ((sw & 0x1000) == 0); /* Bit 12: HomingAttained */
if (sw & 0x1000) {
printf("回零完成\n");
}
}
getchar();
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}