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 索引 0x6401(INT16,16 位有符号)。subindex 标识通道,仅读。0x6402(UINT16)为无符号读取。
AO
模拟输出(Analog Output)。SDO 索引 0x6411(INT16)/ 0x6412(INT32)。subindex 标识通道,可读可写。
PDO 数据访问
CiA 401 的数字/模拟 IO 数据通过 PDO 映射交换。具体偏移量取决于从站的 PDO 映射配置。使用 GetIO() 获取 IO 指针后,根据从站手册确定各通道的偏移量;类型化 PDO 读写函数可直接按偏移量访问:
uint8_t di = dll.PDOReadInputU8(master, slave, 0); /* 数字输入 */
dll.PDOWriteOutputU8(master, slave, 0, 0xFF); /* 数字输出 */
int16_t ai = dll.PDOReadInputI16(master, slave, 2); /* 模拟输入 */
dll.PDOWriteOutputI16(master, slave, 0, 16384); /* 模拟输出 */
高频率 I/O 控制应使用 PDO 映射 而非 SDO。SDO 适合参数配置和诊断。
方法 API
便捷函数 (在 ethercat_advanced.h 中) 把 SDO 索引/子索引封装为按通道访问的形式,所有函数返回 0 成功、非 0 失败。
ReadDI
int cia401_read_di(dll_t* dll, uint16_t master, uint16_t slave,
int channel, BOOL* out);
按通道读取数字输入。通道号从 0 开始。
WriteDO
int cia401_write_do(dll_t* dll, uint16_t master, uint16_t slave,
int channel, BOOL value);
按通道写数字输出。
ReadAI
int cia401_read_ai(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int16_t* out);
按通道读取模拟输入(INT16,16 位有符号)。
WriteAO
int cia401_write_ao(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int16_t value);
按通道写模拟输出(INT16)。
ReadAIUnsigned
int cia401_read_ai_unsigned(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t* out);
按通道读取无符号模拟输入(UINT16)。
ReadAI32Unsigned
int cia401_read_ao32(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t* out);
int cia401_write_ao32(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t value);
32 位模拟输出读写(INT32)。32 位模拟输入用 sdo_read_u32(&dll, master, slave, 0x6403, ch, &out)。
模拟输入中断与限值
ReadGlobalInterruptEnable / WriteGlobalInterruptEnable
int cia401_set_global_interrupt_enable(dll_t* dll, uint16_t master,
uint16_t slave, BOOL enable);
int cia401_get_global_interrupt_enable(dll_t* dll, uint16_t master,
uint16_t slave, BOOL* out);
模拟输入全局中断使能(SDO 0x6423)。
ReadAIUpperLimit / WriteAIUpperLimit
int cia401_set_ai_upper_limit(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t limit);
int cia401_get_ai_upper_limit(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t* out);
设置/读取通道上限告警值(SDO 0x6424),AI 值超过上限时触发中断。
ReadAILowerLimit / WriteAILowerLimit
int cia401_set_ai_lower_limit(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t limit);
int cia401_get_ai_lower_limit(dll_t* dll, uint16_t master, uint16_t slave,
int channel, int32_t* out);
设置/读取通道下限告警值(SDO 0x6425),AI 值低于下限时触发中断。
批量 IO 方法
ReadDI16(int group)
int cia401_read_di16(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t* out);
一次读 16 位数字输入(SDO 0x6001),channel 为 group 索引(1-based)。
ReadDI32(int group)
int cia401_read_di32(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint32_t* out);
一次读 32 位数字输入(SDO 0x6020)。
ReadDO16 / WriteDO16
int cia401_read_do16(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t* out);
int cia401_write_do16(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t value);
16 位数字输出读写(SDO 0x6201)。
ReadDO32 / WriteDO32
int cia401_read_do32(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint32_t* out);
int cia401_write_do32(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint32_t value);
32 位数字输出读写(SDO 0x6220)。
ReadAO32 / WriteAO32
参见 ReadAI32Unsigned。
数字输入配置
SetDIPolarity / GetDIPolarity
int cia401_set_di_polarity(dll_t* dll, uint16_t master, uint16_t slave,
int channel, BOOL inverted);
int cia401_get_di_polarity(dll_t* dll, uint16_t master, uint16_t slave,
int channel, BOOL* out);
设置/读取数字输入极性(SDO 0x6002)。inverted=TRUE 反相。
SetDIFilter / GetDIFilter
int cia401_set_di_filter(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t filter_ms);
int cia401_get_di_filter(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint16_t* out);
设置/读取数字输入软件滤波时间(SDO 0x6003),毫秒单位。
错误状态读取
GetDOErrorMode / GetDOErrorValue
/* 数字输出错误模式 (0=保持, 1=安全值) */
int cia401_set_do_error_mode(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint8_t mode);
int cia401_get_do_error_mode(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint8_t* out);
/* 数字输出错误值 */
int cia401_set_do_error_value(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint8_t value);
int cia401_get_do_error_value(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint8_t* out);
/* 模拟输出错误模式/值 */
int cia401_set_ao_error_mode(dll_t* dll, uint16_t master, uint16_t slave,
int channel, uint8_t mode);
int cia401_set_ao_error_value(dll_t* dll, 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)。
标准对象索引常量
| 常量 | 值 | 说明 |
|---|---|---|
| OD_DI | 0x6000 | 数字输入 |
| OD_DI_16BIT | 0x6001 | 16 位数字输入 |
| OD_DI_POLARITY | 0x6002 | 数字输入极性 |
| OD_DI_FILTER | 0x6003 | 数字输入滤波使能 |
| OD_DI_INTERRUPT | 0x6005 | 数字输入中断触发 |
| OD_DI_INTERRUPT_EDGE | 0x6006 | 数字输入中断边沿 |
| OD_DI_32BIT | 0x6020 | 32 位数字输入 |
| OD_DO | 0x6200 | 数字输出 |
| OD_DO_16BIT | 0x6201 | 16 位数字输出 |
| OD_DO_POLARITY | 0x6202 | 数字输出极性 |
| OD_DO_ERROR_MODE | 0x6206 | 数字输出错误模式 |
| OD_DO_ERROR_VALUE | 0x6207 | 数字输出错误值 |
| OD_DO_32BIT | 0x6220 | 32 位数字输出 |
| OD_AI | 0x6400 | 模拟输入 |
| OD_AI_SI_UNIT | 0x6420 | 模拟输入量程 |
| OD_AI_GLOBAL_INTERRUPT | 0x6423 | 模拟输入全局中断使能 |
| OD_AI_UPPER_LIMIT | 0x6424 | 模拟输入中断上限 |
| OD_AI_LOWER_LIMIT | 0x6425 | 模拟输入中断下限 |
| OD_AO | 0x6411 | 模拟输出 |
| OD_AO_32BIT | 0x6412 | 32 位模拟输出 |
| OD_AO_SI_UNIT | 0x6430 | 模拟输出量程 |
| OD_AO_ERROR_MODE | 0x6443 | 模拟输出错误模式 |
| OD_AO_ERROR_VALUE | 0x6444 | 模拟输出错误值 |
类型化 SDO 读写
ethercat_advanced.h 提供类型化 SDO 包装:
#include "ethercat_advanced.h"
uint8_t polarity;
sdo_read_u8(&dll, master, 1, 0x6002, 0x01, &polarity);
int16_t ai;
sdo_read_i16(&dll, master, 1, 0x6401, 0x01, &ai);
sdo_write_i16(&dll, master, 1, 0x6411, 0x01, 16384);
完整示例
#define DYNAMIC_LOAD
#include "ethercat.h"
#include "ethercat_advanced.h"
#include <stdio.h>
int main(void) {
dll_t dll;
LOAD_DLL(&dll, "Darra.Core.dll");
uint16_t master = dll.Initialize();
dll.SetNetwork(master, "\\Device\\NPF_{GUID}", "");
dll.SetStateSequence(master, EC_STATE_OPERATIONAL, 10000);
dll.Start(master);
uint16_t slave = 1;
/* PDO 数字 / 模拟 IO */
uint8_t di = dll.PDOReadInputU8(master, slave, 0);
int16_t ai = dll.PDOReadInputI16(master, slave, 2);
dll.PDOWriteOutputU8(master, slave, 0, 0xFF);
dll.PDOWriteOutputI16(master, slave, 0, 16384);
printf("DI=0x%02X AI=%d\n", di, ai);
/* 配置错误模式 */
cia401_set_do_error_mode(&dll, master, slave, 0, CIA401_ERROR_MODE_SAFE_VALUE);
cia401_set_do_error_value(&dll, master, slave, 0, 0x00);
/* 模拟输入告警 */
cia401_set_global_interrupt_enable(&dll, master, slave, TRUE);
cia401_set_ai_upper_limit(&dll, master, slave, 0, 30000);
cia401_set_ai_lower_limit(&dll, master, slave, 0, 1000);
getchar();
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}