CiA 401 -- 通用 I/O 模块
CiA 401 (IEC 61131-9) 是基于 CoE 的通用数字/模拟 I/O 设备协议。
CiA 401 运行在 CoE (SDO) 之上,仅当从站支持 CoE 时可用。通过 GetSlaveCoEDetails(master, slave) 返回值判断从站是否支持 CoE。
IO 属性
CiA 401 设备有 4 类标准 IO,每类通过 SDO 索引和 PDO 偏移访问。
DI
数字输入(Digital Input)。SDO 索引 0x6000(UINT8 8位)/ 0x6001(UINT16 16位)/ 0x6020(UINT32 32位)。subindex 标识通道(1-based)。每个通道返回一个 bit 状态。
DO
数字输出(Digital Output)。SDO 索引 0x6200(UINT8)/ 0x6201(UINT16)/ 0x6220(UINT32)。subindex 标识通道,可读可写。
AI
模拟输入(Analog Input)。SDO 索引 0x6400(INT16,16 位有符号,slave/cia401.h 宏 CIA401_OD_AI),0x6401 为同设备类型别名(CIA401_OD_AI_16BIT)。subindex 标识通道,仅读。无符号读取由 cia401_read_ai_unsigned() 包装同一索引完成。
AO
模拟输出(Analog Output)。SDO 索引 0x6411(INT16)/ 0x6412(INT32)。subindex 标识通道,可读可写。
PDO 数据访问
CiA 401 的数字/模拟 IO 数据通过 PDO 映射交换。具体偏移量取决于从站的 PDO 映射配置。金路径是进 OP 后 GetIO 一次钉页:拿到 live IOmap 指针后按从站手册对 packed 结构取通道,总线一刷新再读就是新值。CopySlaveInputPdo 是快照(不是钉页);整帧写入用 WriteSlaveOutputPdo(单一实时 owner 的完整帧),局部更新走 CopySlaveOutputPdoVersioned / CommitSlaveOutputPdoVersioned。详见 PDO 输入输出。
int32_t out_size = 0, in_size = 0;
uint8_t* out_ptr = NULL;
uint8_t* in_ptr = NULL;
if (ec_pdo_io_is_ok(GetIO(master, slave, &out_size, &out_ptr, &in_size, &in_ptr))
&& in_ptr && out_ptr) {
uint8_t di = in_ptr[0]; /* 数字输入,偏移依映射 */
int16_t ai;
memcpy(&ai, in_ptr + 2, 2); /* 模拟输入示例 */
out_ptr[0] = 0xFF; /* 按映射写输出帧 */
}
高频率 I/O 控制应使用 PDO 映射 而非 SDO。SDO 适合参数配置和诊断。
方便函数 API (slave/cia401.h)
slave/cia401.h 提供一组 static inline 函数, 把 SDO 索引/子索引封装为按通道访问的形式。
这些是 slave/cia401.h 里的 static inline 普通 C 函数,直接调用即可(无句柄、无函数指针表),内部转调薄壳导出。
读类函数直接返回值 (失败时返回 0); 写类函数返回 BOOL。
cia401_read_di / cia401_write_do
uint8_t cia401_read_di(uint16_t master, uint16_t slave, int channel);
uint8_t cia401_read_do(uint16_t master, uint16_t slave, int channel);
BOOL cia401_write_do(uint16_t master, uint16_t slave, int channel, uint8_t value);
按通道 (从 0 开始) 读数字输入 / 读回数字输出 / 写数字输出 (读-改-写, 不影响同组其他通道)。cia401_read_di / cia401_read_do 返回通道电平 (1/0)。
cia401_read_ai / cia401_write_ao
int16_t cia401_read_ai(uint16_t master, uint16_t slave, int channel);
uint16_t cia401_read_ai_unsigned(uint16_t master, uint16_t slave, int channel);
int32_t cia401_read_ai32(uint16_t master, uint16_t slave, int channel);
uint32_t cia401_read_ai32_unsigned(uint16_t master, uint16_t slave, int channel);
int16_t cia401_read_ao(uint16_t master, uint16_t slave, int channel);
BOOL cia401_write_ao(uint16_t master, uint16_t slave, int channel, int16_t value);
按通道读模拟输入 (有符号/无符号, 16/32 位; cia401_read_ai32 自适应 16/32 位返回) / 读回模拟输出 / 写 16 位模拟输出。
32 位模拟输出
int32_t cia401_read_ao32(uint16_t master, uint16_t slave, int channel);
BOOL cia401_write_ao32(uint16_t master, uint16_t slave, int channel, int32_t value);
32 位模拟输出 (0x6412) 读写。
模拟输入中断与限值 (slave/cia401.h)
cia401_get/set_global_interrupt_enable
BOOL cia401_get_global_interrupt_enable(uint16_t master, uint16_t slave);
BOOL cia401_set_global_interrupt_enable(uint16_t master, uint16_t slave, BOOL enabled);
模拟输入全局中断使能 (SDO 0x6423)。get 返回当前使能状态; set 返回写入是否成功。
cia401_get/set_ai_upper_limit
int16_t cia401_get_ai_upper_limit(uint16_t master, uint16_t slave, int channel);
BOOL cia401_set_ai_upper_limit(uint16_t master, uint16_t slave, int channel, int16_t value);
设置/读取通道上限告警值 (SDO 0x6424, 有符号 16 位),AI 值超过上限时触发中断。
cia401_get/set_ai_lower_limit
int16_t cia401_get_ai_lower_limit(uint16_t master, uint16_t slave, int channel);
BOOL cia401_set_ai_lower_limit(uint16_t master, uint16_t slave, int channel, int16_t value);
设置/读取通道下限告警值 (SDO 0x6425, 有符号 16 位),AI 值低于下限时触发中断。
批量 IO 函数 (slave/cia401.h)
以下函数 group 参数为 SDO 子索引 (1-based)。
uint16_t cia401_read_di16(uint16_t master, uint16_t slave, uint8_t group); /* 0x6001 */
uint32_t cia401_read_di32(uint16_t master, uint16_t slave, uint8_t group); /* 0x6020 */
uint16_t cia401_read_do16(uint16_t master, uint16_t slave, uint8_t group); /* 0x6201 */
BOOL cia401_write_do16(uint16_t master, uint16_t slave, uint8_t group, uint16_t value);
uint32_t cia401_read_do32(uint16_t master, uint16_t slave, uint8_t group); /* 0x6220 */
BOOL cia401_write_do32(uint16_t master, uint16_t slave, uint8_t group, uint32_t value);
读类函数直接返回值, 写类函数返回 BOOL。
数字输入配置 (slave/cia401.h)
uint16_t cia401_get_di_polarity(uint16_t master, uint16_t slave, uint8_t group); /* 0x6002 */
BOOL cia401_set_di_polarity(uint16_t master, uint16_t slave, uint8_t group, uint8_t polarity);
uint16_t cia401_get_di_filter(uint16_t master, uint16_t slave, uint8_t group); /* 0x6003 */
BOOL cia401_set_di_filter(uint16_t master, uint16_t slave, uint8_t group, uint8_t filter);
设置/读取数字输入极性 (0x6002, 每位 1=反转) 与滤波使能 (0x6003)。
错误状态读取 (slave/cia401.h)
本节 DO 错误函数的 group 从 0 开始(内部 +1 作 SDO 子索引, 对应 DO 通道 0-7、8-15…); 与上文批量 IO 函数的 1-based group(直接作子索引)不同, 混用会偏一组。
/* 数字输出错误模式 (0=保持, 1=安全值) / 错误值 */
uint8_t cia401_get_do_error_mode(uint16_t master, uint16_t slave, uint8_t group);
BOOL cia401_set_do_error_mode(uint16_t master, uint16_t slave, uint8_t group, uint8_t mode);
uint16_t cia401_get_do_error_value(uint16_t master, uint16_t slave, uint8_t group);
BOOL cia401_set_do_error_value(uint16_t master, uint16_t slave, uint8_t group, uint8_t value);
/* 模拟输出错误模式 / 错误值 */
uint8_t cia401_get_ao_error_mode(uint16_t master, uint16_t slave, int channel);
BOOL cia401_set_ao_error_mode(uint16_t master, uint16_t slave, int channel, uint8_t mode);
int16_t cia401_get_ao_error_value(uint16_t master, uint16_t slave, int channel);
BOOL cia401_set_ao_error_value(uint16_t master, uint16_t slave, int channel, int16_t value);
错误处理
DOErrorMode
数字输出错误模式(SDO 0x6206)。0=保持最后值,1=切换到安全值。
#define CIA401_ERROR_MODE_HOLD 0
#define CIA401_ERROR_MODE_SAFE_VALUE 1
DOErrorValue
数字输出错误值(SDO 0x6207)。错误模式为 1 时使用此值作为安全输出。
AOErrorMode
模拟输出错误模式(SDO 0x6443),同 DOErrorMode 语义。
AOErrorValue
模拟输出错误值(SDO 0x6444)。
标准对象索引常量
slave/cia401.h 中定义的对象索引宏 (注意带 CIA401_OD_ 前缀):
| 宏 | 值 | 说明 |
|---|---|---|
| CIA401_OD_DI | 0x6000 | 数字输入 (8 位组) |
| CIA401_OD_VIRTUAL_DI | 0x2120 | 虚拟数字输入 (cia401_write_virtual_di 专用) |
| CIA401_OD_DI_16BIT | 0x6001 | 16 位数字输入组 |
| CIA401_OD_DI_POLARITY | 0x6002 | 数字输入极性 |
| CIA401_OD_DI_FILTER | 0x6003 | 数字输入滤波使能 |
| CIA401_OD_DI_INTERRUPT | 0x6005 | 数字输入中断触发 |
| CIA401_OD_DI_INTERRUPT_EDGE | 0x6006 | 数字输入中断边沿 |
| CIA401_OD_DI_32BIT | 0x6020 | 32 位数字输入组 |
| CIA401_OD_DO | 0x6200 | 数字输出 (8 位组) |
| CIA401_OD_DO_16BIT | 0x6201 | 16 位数字输出组 |
| CIA401_OD_DO_POLARITY | 0x6202 | 数字输出极性 |
| CIA401_OD_DO_ERROR_MODE | 0x6206 | 数字输出错误模式 |
| CIA401_OD_DO_ERROR_VALUE | 0x6207 | 数字输出错误值 |
| CIA401_OD_DO_32BIT | 0x6220 | 32 位数字输出组 |
| CIA401_OD_AI | 0x6400 | 模拟输入 (16 位有符号) |
| CIA401_OD_AI_16BIT | 0x6401 | 模拟输入 16 位 (别名) |
| CIA401_OD_AI_SI_UNIT | 0x6420 | 模拟输入量程 |
| CIA401_OD_AI_GLOBAL_INTERRUPT | 0x6423 | 模拟输入全局中断使能 |
| CIA401_OD_AI_UPPER_LIMIT | 0x6424 | 模拟输入中断上限 |
| CIA401_OD_AI_LOWER_LIMIT | 0x6425 | 模拟输入中断下限 |
| CIA401_OD_AO | 0x6411 | 模拟输出 (16 位有符号) |
| CIA401_OD_AO_32BIT | 0x6412 | 32 位模拟输出 |
| CIA401_OD_AO_SI_UNIT | 0x6430 | 模拟输出量程 |
| CIA401_OD_AO_ERROR_MODE | 0x6443 | 模拟输出错误模式 |
| CIA401_OD_AO_ERROR_VALUE | 0x6444 | 模拟输出错误值 |
错误模式枚举宏: CIA401_ERROR_MODE_HOLD (0) / CIA401_ERROR_MODE_SAFE_VALUE (1)。
类型化 SDO 读写
ethercat_advanced.h 提供类型化 SDO 包装:
#include "ethercat_advanced.h"
uint8_t polarity;
sdo_read_u8(master, 1, 0x6002, 0x01, &polarity);
int16_t ai;
sdo_read_i16(master, 1, 0x6401, 0x01, &ai);
sdo_write_i16(master, 1, 0x6411, 0x01, 16384);
完整示例
#include "ethercat.h"
#include "ethercat_advanced.h"
#include <stdio.h>
int main(void) {
uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");
SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
Start(master);
uint16_t slave = 1;
/* PDO 数字 / 模拟 IO:进 OP 后 GetIO 一次钉页,指针直读写 */
int32_t out_size = 0, in_size = 0;
uint8_t* out_ptr = NULL;
uint8_t* in_ptr = NULL;
if (ec_pdo_io_is_ok(GetIO(master, slave, &out_size, &out_ptr, &in_size, &in_ptr))
&& in_ptr && out_ptr) {
uint8_t di = in_ptr[0]; /* 数字输入,偏移依映射 */
int16_t ai;
memcpy(&ai, in_ptr + 2, 2); /* 模拟输入示例 */
printf("DI=0x%02X\n", di);
out_ptr[0] = 0xFF; /* 按映射写输出帧 */
}
/* 配置错误模式 (slave/cia401.h 内联函数, 普通 C 函数直接调用) */
cia401_set_do_error_mode(master, slave, 0, CIA401_ERROR_MODE_SAFE_VALUE);
cia401_set_do_error_value(master, slave, 0, 0x00);
/* 模拟输入告警 */
cia401_set_global_interrupt_enable(master, slave, TRUE);
cia401_set_ai_upper_limit(master, slave, 0, 30000);
cia401_set_ai_lower_limit(master, slave, 0, 1000);
getchar();
Stop(master);
Dispose(master);
return 0;
}