跳到主要内容

CiA 401 — 通用 I/O 模块

CiA 401 (IEC 61131-9) 是基于 CoE 的通用数字/模拟 I/O 设备协议。

CiA 401 运行在 CoE (SDO) 之上,仅当从站支持 CoE 时可用。

通过 slave.CoE.CiA401 访问。从站不支持 CiA 401 时为 null。​

IO 属性​

通过索引器直接读写 IO 通道,所有通道号从 0 开始。

类别属性类型读写说明
数字DI[n]bool只读数字输入通道 n
DO[n]bool读写数字输出通道 n
模拟AI[n]int只读模拟输入通道 n
AO[n]short读写模拟输出通道 n

示例:

var io = slave.CoE.CiA401;
if (io == null) return;

// 数字 IO
bool sensor = io.DI[0];
io.DO[0] = true;
io.DO[1] = sensor;

// 模拟 IO
int adc = io.AI[0];
io.AO[0] = 16384;
实时控制

高频率 I/O 控制应使用 PDO 映射 而非 SDO。SDO 适合参数配置和诊断。

DI​

public DIAccessor DI { get; }

数字输入索引器。DI[channel] 按通道号 (从 0 开始) 读取单通道高/低电平,内部按 8 通道一组转 SDO 0x6000。

相关结构:

public class DIAccessor
{
public bool this[int channel] { get; } // 只读, true = 高电平
}

示例:

bool ch0 = io.DI[0];
bool ch5 = io.DI[5];

DO​

public DOAccessor DO { get; }

数字输出索引器。DO[channel] 既可读回上次写入的状态,也可设置目标状态 (写 SDO 0x6200),不影响同组其他通道。

相关结构:

public class DOAccessor
{
public bool this[int channel] { get; set; }
}

示例:

io.DO[0] = true;
io.DO[1] = io.DI[0];
bool current = io.DO[0];

AI​

public AIAccessor AI { get; }

模拟输入索引器。AI[channel] 读 SDO 0x6400(子索引 = 通道 + 1),按返回长度自动适应 16/32 位,返回有符号原始值。如需无符号 16 位或无符号 32 位,使用 ReadAIUnsigned / ReadAI32Unsigned。

相关结构:

public class AIAccessor
{
public int this[int channel] { get; } // 只读
}

示例:

int adc0 = io.AI[0];
double voltage = adc0 * 10.0 / 32768;

AO​

public AOAccessor AO { get; }

模拟输出索引器。AO[channel] 读写 16 位有符号值 (SDO 0x6411)。32 位高精度设备使用 WriteAO32 / ReadAO32。

相关结构:

public class AOAccessor
{
public short this[int channel] { get; set; }
}

示例:

io.AO[0] = 16384;            // 写入 50% 量程
short echo = io.AO[0]; // 读回当前设定值

方法 API​

索引器内部调用以下方法,也可直接使用。所有写入方法在 SDO 写入失败时抛 InvalidOperationException(从站配置未改变)。

ReadDI​

public bool ReadDI(int channel)

读取数字输入。内部按 8 通道一组自动计算组号和位偏移。

参数:

  • channel — 通道号(从 0 开始)

WriteDO​

public void WriteDO(int channel, bool state)

设置数字输出,读改写同组字节,不影响同组其他通道。同组旧值读失败时抛 InvalidOperationException,不按 0 基线改写本组。

参数:

  • channel — 通道号(从 0 开始)
  • state — 目标状态

ReadDO​

public bool ReadDO(int channel)

读取数字输出状态(SDO 0x6200,按 8 通道一组)。读失败返回 false 且与真值不可区分,判失败请用 TryReadDO。

参数:

  • channel — 通道号(从 0 开始)

WriteVirtualDI(int diIndex, bool state)​

public void WriteVirtualDI(int diIndex, bool state)

写入虚拟 DI 命令(0x2120:01-04,厂商对象)。该对象 SDO 写会被 abort(0x06020000),方法内部自动回退 PDO 写入;SDO 与 PDO 均失败时抛 InvalidOperationException。

参数:

  • diIndex — 虚拟 DI 子索引(1-4 → 0x2120:01-04)
  • state — 目标状态(true = 1)

ReadAI​

public int ReadAI(int channel)

读取模拟输入值。自动适应 16 位和 32 位设备。

参数:

  • channel — 通道号(从 0 开始)

WriteAO​

public void WriteAO(int channel, short value)

写入模拟输出值。

参数:

  • channel — 通道号(从 0 开始)
  • value — 16 位输出值

ReadAO​

public short ReadAO(int channel)

读取 16 位模拟输出值(SDO 0x6411)。读失败返回 0 且与真值不可区分,判失败请用 TryReadAO。

参数:

  • channel — 通道号(从 0 开始)

ReadAIUnsigned​

public ushort ReadAIUnsigned(int channel)

读取模拟输入值(无符号 16 位)。适用于 AI 数据范围为 0~65535 的设备。

参数:

  • channel — 通道号(从 0 开始)

返回值:

  • ushort — 无符号 16 位模拟值

ReadAI32Unsigned​

public uint ReadAI32Unsigned(int channel)

读取模拟输入值(无符号 32 位)。适用于高精度 AI 设备,数据范围 0~4294967295。

参数:

  • channel — 通道号(从 0 开始)

返回值:

  • uint — 无符号 32 位模拟值

示例:

var io = slave.CoE.CiA401;

// 有符号读取 (默认, 可能高位值变负数)
int aiSigned = io.ReadAI(0);

// 无符号 16 位读取 (0~65535)
ushort aiU16 = io.ReadAIUnsigned(0);

// 无符号 32 位读取 (高精度设备)
uint aiU32 = io.ReadAI32Unsigned(0);

模拟输入中断与限值​

ReadGlobalInterruptEnable / WriteGlobalInterruptEnable​

public bool ReadGlobalInterruptEnable()
public void WriteGlobalInterruptEnable(bool enable)

读取或设置模拟输入全局中断使能 (0x6423)。

参数:

  • enable — true = 使能, false = 禁用

ReadAIUpperLimit / WriteAIUpperLimit​

public int ReadAIUpperLimit(int channel)
public void WriteAIUpperLimit(int channel, short value)

读取或写入模拟输入中断上限值 (0x6424)。当 AI 值超过上限时触发中断。

参数:

  • channel — 通道号(从 0 开始)
  • value — 上限值

ReadAILowerLimit / WriteAILowerLimit​

public int ReadAILowerLimit(int channel)
public void WriteAILowerLimit(int channel, short value)

读取或写入模拟输入中断下限值 (0x6425)。当 AI 值低于下限时触发中断。

参数:

  • channel — 通道号(从 0 开始)
  • value — 下限值

示例:

var io = slave.CoE.CiA401;

// 启用全局中断
io.WriteGlobalInterruptEnable(true);

// 设置通道 0 的上下限
io.WriteAIUpperLimit(0, 30000);
io.WriteAILowerLimit(0, 1000);

// 读取当前限值配置
int upper = io.ReadAIUpperLimit(0);
int lower = io.ReadAILowerLimit(0);
bool enabled = io.ReadGlobalInterruptEnable();

批量 IO 方法​

除逐通道操作外,还支持 16/32 位批量读写,一次操作多个通道。

ReadDI16(int group)​

public ushort ReadDI16(int group)

读取 16 位数字输入(16 个通道)。

参数:

  • group — 组号(子索引,从 1 开始,每 16 个通道为一组:组 1 = 通道 0-15)

返回值:

  • ushort — 16 位输入值,每位对应一个通道

ReadDI32(int group)​

public uint ReadDI32(int group)

读取 32 位数字输入(32 个通道)。

参数:

  • group — 组号(子索引,从 1 开始,每 32 个通道为一组:组 1 = 通道 0-31)

返回值:

  • uint — 32 位输入值,每位对应一个通道

示例:

var io = slave.CoE.CiA401;

ushort di16 = io.ReadDI16(1); // 读取通道 0-15
uint di32 = io.ReadDI32(1); // 读取通道 0-31
bool ch5 = (di16 & (1 << 5)) != 0; // 检查通道 5

ReadDO16 / WriteDO16​

public ushort ReadDO16(int group)
public void WriteDO16(int group, ushort value)

读取或写入 16 位数字输出(16 个通道)。

参数:

  • group — 组号(子索引,从 1 开始,每 16 个通道为一组:组 1 = 通道 0-15)
  • value — 16 位输出值

ReadDO32 / WriteDO32​

public uint ReadDO32(int group)
public void WriteDO32(int group, uint value)

读取或写入 32 位数字输出(32 个通道)。

参数:

  • group — 组号(子索引,从 1 开始,每 32 个通道为一组:组 1 = 通道 0-31)
  • value — 32 位输出值

示例:

var io = slave.CoE.CiA401;

// 16 位批量操作
io.WriteDO16(1, 0x00FF); // 通道 0-7 输出高,8-15 输出低
ushort do16 = io.ReadDO16(1); // 读回当前值

// 32 位批量操作
io.WriteDO32(1, 0x0000FFFF); // 通道 0-15 输出高
uint do32 = io.ReadDO32(1); // 读回当前值

ReadAO32 / WriteAO32​

public int ReadAO32(int channel)
public void WriteAO32(int channel, int value)

读取或写入 32 位模拟输出值。适用于高精度模拟输出设备。

参数:

  • channel — 通道号(从 0 开始)
  • value — 32 位输出值

示例:

var io = slave.CoE.CiA401;
io.WriteAO32(0, 65536); // 写入 32 位高精度值
int ao = io.ReadAO32(0); // 读回当前值

读失败判别(Try 前缀方法)​

普通读取方法(ReadDI / ReadAI / ReadDO16 等)读失败时返回 0 / false / Hold 占位,与真值不可区分,不可作为工艺状态参与联锁。以下 Try 前缀方法返回 false 表示读取失败(out 参数无意义),返回 true 时 out 参数才是真值。

类别方法返回类型读写说明
数字TryReadDI(int channel, out bool value)bool只读数字输入单通道(0x6000)
数字TryReadDI16(int group, out ushort value)bool只读16 位数字输入组(0x6001,组号 = 子索引,从 1 开始)
数字TryReadDI32(int group, out uint value)bool只读32 位数字输入组(0x6020,组号 = 子索引,从 1 开始)
数字TryReadDO(int channel, out bool value)bool只读数字输出单通道回读(0x6200)
数字TryReadDO16(int group, out ushort value)bool只读16 位数字输出组回读(0x6201)
数字TryReadDO32(int group, out uint value)bool只读32 位数字输出组回读(0x6220)
模拟TryReadAI(int channel, out int value)bool只读模拟输入,自动适应 16/32 位(0x6400)
模拟TryReadAIUnsigned(int channel, out ushort value)bool只读模拟输入无符号 16 位(0x6400)
模拟TryReadAI32Unsigned(int channel, out uint value)bool只读模拟输入无符号 32 位(0x6400)
模拟TryReadAIUpperLimit(int channel, out int value)bool只读模拟输入中断上限(0x6424)
模拟TryReadAILowerLimit(int channel, out int value)bool只读模拟输入中断下限(0x6425)
模拟TryReadAO(int channel, out short value)bool只读16 位模拟输出回读(0x6411)
模拟TryReadAO32(int channel, out int value)bool只读32 位模拟输出回读(0x6412)
错误TryGetDOErrorMode(int group, out CiA401ErrorMode mode)bool只读数字输出错误模式(0x6206)
错误TryGetDOErrorValue(int group, out byte value)bool只读数字输出安全值(0x6207)

示例:

var io = slave.CoE.CiA401;

// 分判「读失败」与「真值 0」
if (io.TryReadAI(0, out int ai))
Console.WriteLine($"AI0 = {ai}");
else
Console.WriteLine("AI0 读取失败, 不可当测量值使用");

数字输入配置​

SetDIPolarity / GetDIPolarity​

public void SetDIPolarity(int group, byte polarity)
public byte GetDIPolarity(int group)

设置或读取数字输入极性 (0x6002)。可按位反转输入信号逻辑。

参数:

  • group — 组号(子索引,从 1 开始)
  • polarity — 极性掩码,1 = 反转对应通道

示例:

var io = slave.CoE.CiA401;
io.SetDIPolarity(1, 0x0F); // 反转通道 0-3 的极性
byte pol = io.GetDIPolarity(1); // 读取当前极性配置

SetDIFilter / GetDIFilter​

public void SetDIFilter(int group, byte filterEnable)
public byte GetDIFilter(int group)

设置或读取数字输入滤波使能 (0x6003)。用于消除输入信号抖动。

参数:

  • group — 组号(子索引,从 1 开始)
  • filterEnable — 滤波使能掩码

示例:

var io = slave.CoE.CiA401;
io.SetDIFilter(1, 10); // 设置滤波使能
byte flt = io.GetDIFilter(1); // 读取当前滤波配置

错误状态读取​

GetDOErrorMode / GetDOErrorValue​

public CiA401ErrorMode GetDOErrorMode(int group)
public byte GetDOErrorValue(int group)

读取数字输出错误模式和错误安全值的当前配置。读失败返回占位值(Hold / 0)并记日志,与真值不可区分;判失败请用 TryGetDOErrorMode / TryGetDOErrorValue。

参数:

  • group — 组号(从 0 开始)

示例:

var io = slave.CoE.CiA401;
var mode = io.GetDOErrorMode(0);
byte safeVal = io.GetDOErrorValue(0);
Console.WriteLine($"错误模式: {mode}, 安全值: 0x{safeVal:X2}");

错误处理​

DOErrorMode​

public void DOErrorMode(int group, CiA401ErrorMode mode)

设置数字输出错误模式。当 EtherCAT 通信丢失时,决定输出如何响应。

参数:

  • group — 组号(从 0 开始,每 8 个通道为一组:组 0 = 通道 0-7,组 1 = 通道 8-15)
  • mode — 错误模式

相关结构:

public enum CiA401ErrorMode : byte
{
Hold = 0, // 保持当前输出值
SafeValue = 1, // 切换到预设的安全值
}

DOErrorValue​

public void DOErrorValue(int group, byte value)

设置数字输出通信丢失时的安全值。仅在 SafeValue 模式下生效。

参数:

  • group — 组号(从 0 开始)
  • value — 安全输出值(8 位,每位对应一个通道)

AOErrorMode​

public void AOErrorMode(int channel, CiA401ErrorMode mode)

设置模拟输出错误模式。

参数:

  • channel — 通道号(从 0 开始)
  • mode — 错误模式

AOErrorValue​

public void AOErrorValue(int channel, short value)

设置模拟输出通信丢失时的安全值。仅在 SafeValue 模式下生效。

参数:

  • channel — 通道号(从 0 开始)
  • value — 16 位安全输出值

示例:

io.DOErrorMode(0, CiA401ErrorMode.SafeValue);
io.DOErrorValue(0, 0x00);

io.AOErrorMode(0, CiA401ErrorMode.SafeValue);
io.AOErrorValue(0, 0);

标准对象索引常量​

可搭配 slave.CoE.SDORead / SDOWrite 直接访问:

常量值说明
OD_VIRTUAL_DI0x2120虚拟 DI 命令(厂商对象,须走 PDO)
OD_DI0x6000数字输入
OD_DI_16BIT0x600116 位数字输入
OD_DI_POLARITY0x6002数字输入极性
OD_DI_FILTER0x6003数字输入滤波使能
OD_DI_INTERRUPT0x6005数字输入中断触发
OD_DI_INTERRUPT_EDGE0x6006数字输入中断边沿
OD_DI_32BIT0x602032 位数字输入
OD_DO0x6200数字输出
OD_DO_16BIT0x620116 位数字输出
OD_DO_POLARITY0x6202数字输出极性
OD_DO_ERROR_MODE0x6206数字输出错误模式
OD_DO_ERROR_VALUE0x6207数字输出错误值
OD_DO_32BIT0x622032 位数字输出
OD_AI0x6400模拟输入
OD_AI_SI_UNIT0x6420模拟输入量程
OD_AI_GLOBAL_INTERRUPT0x6423模拟输入全局中断使能
OD_AI_UPPER_LIMIT0x6424模拟输入中断上限
OD_AI_LOWER_LIMIT0x6425模拟输入中断下限
OD_AO0x6411模拟输出
OD_AO_32BIT0x641232 位模拟输出
OD_AO_SI_UNIT0x6430模拟输出量程
OD_AO_ERROR_MODE0x6443模拟输出错误模式
OD_AO_ERROR_VALUE0x6444模拟输出错误值

完整示例​

var io = slave.CoE.CiA401;
if (io == null) return;

// 数字 IO
bool sensor = io.DI[0];
bool limit = io.DI[5];

io.DO[0] = true;
io.DO[1] = sensor;

// 模拟 IO
int adc = io.AI[0];
double temp = adc * 0.1;
io.AO[0] = (short)(temp > 50 ? 0 : 16384);

// 安全配置
io.DOErrorMode(0, CiA401ErrorMode.SafeValue);
io.DOErrorValue(0, 0x00);