PDO 输入输出
推荐
高性能场景使用 结构体映射(零拷贝指针 + struct cast),快速原型使用 类型化读写。
结构体映射(零拷贝等效)
C 语言无泛型,但可通过 GetIO() 获取指针后直接 cast 为结构体指针,实现与 C# OutputsMapping<T>() 等效的零拷贝访问。
/* 定义与从站 PDO 映射对应的结构体 */
#pragma pack(push, 1)
typedef struct {
uint16_t status_word;
int32_t actual_position;
int32_t actual_velocity;
} servo_input_t;
typedef struct {
uint16_t control_word;
int32_t target_position;
int32_t target_velocity;
} servo_output_t;
#pragma pack(pop)
/* 获取 IO 指针后 cast 为结构体 */
int32_t out_size, in_size;
uint8_t *out_ptr, *in_ptr;
dll.GetIO(master, 1, &out_size, &out_ptr, &in_size, &in_ptr);
servo_input_t* input = (servo_input_t*)in_ptr;
servo_output_t* output = (servo_output_t*)out_ptr;
/* 零拷贝读取 */
printf("位置: %d, 速度: %d\n", input->actual_position, input->actual_velocity);
/* 零拷贝写入 */
output->control_word = 0x000F;
output->target_position = 10000;
结构体大小
结构体大小必须与从站 IO 大小匹配(out_size / in_size),否则会越界访问。使用 #pragma pack(1) 确保无填充字节。
偏移映射(MDP 模块化设备)
对于 MDP 模块化设备,每个模块在 IO 中有不同偏移,可通过指针偏移实现 Slice 映射:
/* MDP 设备: 模块1 从偏移 0 开始,模块2 从偏移 8 开始 */
module1_input_t* mod1 = (module1_input_t*)(in_ptr + 0);
module2_input_t* mod2 = (module2_input_t*)(in_ptr + 8);
零拷贝指针访问
GetIO()
uint8_t GetIO(uint16_t master_index, uint16_t slave_index,
int32_t* out_byte_size, uint8_t** out_byte,
int32_t* in_byte_size, uint8_t** in_byte);
获取从站 IO 指针。返回 0 成功。
示例:
int32_t out_size, in_size;
uint8_t *out_ptr, *in_ptr;
if (dll.GetIO(master, 1, &out_size, &out_ptr, &in_size, &in_ptr) == 0) {
/* 零拷贝读取输入 */
uint16_t status_word = *(uint16_t*)in_ptr;
/* 零拷贝写入输出 */
*(uint16_t*)out_ptr = 0x000F;
}
WriteSlaveOutput() / WriteSlaveOutputByte()
void WriteSlaveOutput(uint16_t master_index, uint16_t slave_index,
const uint8_t* data, uint32_t size);
void WriteSlaveOutputByte(uint16_t master_index, uint16_t slave_index,
uint32_t offset, uint8_t value);
无锁安全输出写入(推荐多线程场景)。
类型化读写(旧接口)
/* 写入输出 PDO */
void PdoWriteInt8(uint16_t master, uint16_t slave, uint32_t offset, int8_t value);
void PdoWriteInt16(uint16_t master, uint16_t slave, uint32_t offset, int16_t value);
void PdoWriteInt32(uint16_t master, uint16_t slave, uint32_t offset, int32_t value);
void PdoWriteUint8(uint16_t master, uint16_t slave, uint32_t offset, uint8_t value);
void PdoWriteUint16(uint16_t master, uint16_t slave, uint32_t offset, uint16_t value);
void PdoWriteUint32(uint16_t master, uint16_t slave, uint32_t offset, uint32_t value);
/* 读取输入 PDO */
int8_t PdoReadInt8(uint16_t master, uint16_t slave, uint32_t offset);
int16_t PdoReadInt16(uint16_t master, uint16_t slave, uint32_t offset);
int32_t PdoReadInt32(uint16_t master, uint16_t slave, uint32_t offset);
uint8_t PdoReadUint8(uint16_t master, uint16_t slave, uint32_t offset);
uint16_t PdoReadUint16(uint16_t master, uint16_t slave, uint32_t offset);
uint32_t PdoReadUint32(uint16_t master, uint16_t slave, uint32_t offset);
类型化读写(新接口)
/* 读取输入 */
uint8_t PDOReadInputU8(uint16_t master, uint16_t slave, uint32_t offset);
int8_t PDOReadInputI8(uint16_t master, uint16_t slave, uint32_t offset);
int16_t PDOReadInputI16(uint16_t master, uint16_t slave, uint32_t offset);
uint16_t PDOReadInputU16(uint16_t master, uint16_t slave, uint32_t offset);
int32_t PDOReadInputI32(uint16_t master, uint16_t slave, uint32_t offset);
uint32_t PDOReadInputU32(uint16_t master, uint16_t slave, uint32_t offset);
int64_t PDOReadInputI64(uint16_t master, uint16_t slave, uint32_t offset);
uint64_t PDOReadInputU64(uint16_t master, uint16_t slave, uint32_t offset);
float PDOReadInputF32(uint16_t master, uint16_t slave, uint32_t offset);
double PDOReadInputF64(uint16_t master, uint16_t slave, uint32_t offset);
/* 写入输出 */
BOOL PDOWriteOutputU8(uint16_t master, uint16_t slave, uint32_t offset, uint8_t val);
BOOL PDOWriteOutputI8(uint16_t master, uint16_t slave, uint32_t offset, int8_t val);
BOOL PDOWriteOutputI16(uint16_t master, uint16_t slave, uint32_t offset, int16_t val);
BOOL PDOWriteOutputU16(uint16_t master, uint16_t slave, uint32_t offset, uint16_t val);
BOOL PDOWriteOutputI32(uint16_t master, uint16_t slave, uint32_t offset, int32_t val);
BOOL PDOWriteOutputU32(uint16_t master, uint16_t slave, uint32_t offset, uint32_t val);
BOOL PDOWriteOutputI64(uint16_t master, uint16_t slave, uint32_t offset, int64_t val);
BOOL PDOWriteOutputU64(uint16_t master, uint16_t slave, uint32_t offset, uint64_t val);
BOOL PDOWriteOutputF32(uint16_t master, uint16_t slave, uint32_t offset, float val);
BOOL PDOWriteOutputF64(uint16_t master, uint16_t slave, uint32_t offset, double val);
支持的类型
U8/I8— uint8_t / int8_t — 1 字节,无符号/有符号 8 位U16/I16— uint16_t / int16_t — 2 字节,无符号/有符号 16 位U32/I32— uint32_t / int32_t — 4 字节,无符号/有符号 32 位U64/I64— uint64_t / int64_t — 8 字节,无符号/有符号 64 位F32— float — 4 字节,单精度浮点F64— double — 8 字节,双精度浮点
示例:
uint16_t sw = dll.PDOReadInputU16(master, 1, 0);
int32_t pos = dll.PDOReadInputI32(master, 1, 2);
dll.PDOWriteOutputU16(master, 1, 0, 0x000F);
dll.PDOWriteOutputI32(master, 1, 2, 100000);
直接读写与批量操作
BOOL PDOReadDirect(uint16_t master_index, uint16_t slave_index, uint16_t pdo_index,
void* data_buffer, uint32_t buffer_size, uint32_t* bytes_read);
BOOL PDOWriteDirect(uint16_t master_index, uint16_t slave_index, uint16_t pdo_index,
const void* data_buffer, uint32_t data_size);
uint32_t PDOBatchRead(uint16_t master_index, uint16_t* slave_indices, uint32_t slave_count,
void** data_buffers, uint32_t* buffer_sizes, uint32_t* bytes_read);
uint32_t PDOBatchWrite(uint16_t master_index, uint16_t* slave_indices, uint32_t slave_count,
const void** data_buffers, uint32_t* data_sizes);
PDO 统计与监控
void* GetPDOStats(uint16_t master_index, uint16_t slave_index);
void ResetPDOStats(uint16_t master_index, uint16_t slave_index);
void EnablePDOMonitoring(BOOL enable);
BOOL IsPDOMonitoringEnabled(void);
BOOL GetPDOMapping(uint16_t master_index, uint16_t slave_index, uint16_t pdo_type,
void* mapping_buffer, uint32_t buffer_size, uint32_t* mapping_count);
PDO 辅助查询
uint64_t GetPDOAvgCycleTimeNs(uint16_t master_index); /* PDO 平均周期时间(纳秒) */
uint32_t GetPDOErrorCount(uint16_t master_index); /* PDO 错误计数 */
int IsPDOValid(uint16_t master_index); /* PDO 数据是否有效 */
int IsPDOChanged(uint16_t master_index); /* PDO 数据是否发生变化 */
uint32_t GetPDOExpectedSize(uint16_t master_index); /* PDO 期望数据大小 */
PDO 区定位
通过从站 IO 区属性可计算任意 PDO 字段在 IOmap 中的绝对偏移。所有数值见 从站 IO 区属性。
| 计算项 | 公式 |
|---|---|
| 输入 PDO 在 IOmap 起点 | GetSlaveInputOffset(mi, si) |
| 输出 PDO 在 IOmap 起点 | GetSlaveOutputOffset(mi, si) |
| 字段绝对字节偏移 | slave_offset + field_byte_offset |
| 字段位地址 | (slave_offset × 8) + GetSlaveInputStartBit(mi, si) + field_bit_offset |
| 输入字节数 | GetSlaveInputBytes(mi, si) 或 (GetSlaveInputBits + 7) / 8 |
| 输出字节数 | GetSlaveOutputBytes(mi, si) 或 (GetSlaveOutputBits + 7) / 8 |
示例: 边界检查的零拷贝读
int32_t out_size, in_size;
uint8_t *out_ptr, *in_ptr;
dll.GetIO(master, 1, &out_size, &out_ptr, &in_size, &in_ptr);
uint32_t in_bytes = dll.GetSlaveInputBytes(master, 1);
uint8_t in_sbit = dll.GetSlaveInputStartBit(master, 1);
if (in_bytes >= 6 && in_sbit == 0) {
uint16_t status_word = *(uint16_t*)(in_ptr + 0);
int32_t actual_position = *(int32_t*) (in_ptr + 2);
printf("status=0x%04X pos=%d\n", status_word, actual_position);
}
示例: 多从站 IOmap 全局偏移
/* IOmap 全局起点 + 从站 PDO 偏移 = 该字段绝对位置 */
uint32_t in_off = dll.GetSlaveInputOffset(master, 1);
uint32_t out_off = dll.GetSlaveOutputOffset(master, 1);
printf("从站1: 输入 IOmap 偏移=%u, 输出 IOmap 偏移=%u\n", in_off, out_off);
PDO 丢帧统计
void GetPDOFrameLossStats(uint16_t master_index, uint8_t group,
uint32_t* total_lost, uint32_t* consecutive_lost,
uint32_t* max_consecutive_lost);
void ResetPDOFrameLossStats(uint16_t master_index, uint8_t group);
PDO 变化监视器 (ethercat_advanced.h)
/* PDO 输入数据变化回调 */
typedef void (*pdo_change_callback_t)(uint16_t master, uint16_t slave,
const uint8_t* old_data, const uint8_t* new_data,
uint32_t size, void* user_data);
/* 创建 PDO 变化监视器 */
pdo_monitor_t* pdo_monitor_create(dll_t* dll, uint16_t master);
/* 销毁 */
void pdo_monitor_destroy(pdo_monitor_t* mon);
/* 注册从站 PDO 变化回调 */
int pdo_monitor_watch(pdo_monitor_t* mon, uint16_t slave,
pdo_change_callback_t callback, void* user_data);
/* 在 PDO 周期中调用 (检测变化并触发回调) */
void pdo_monitor_check(pdo_monitor_t* mon);
完整示例
#define DYNAMIC_LOAD
#include "ethercat.h"
#include <stdio.h>
static void on_pdo(uint16_t mi)
{
/* 在 PDO 周期回调中读写数据 */
}
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, 10000);
if (dll.RegisterPDOCyclicSync) dll.RegisterPDOCyclicSync(on_pdo);
dll.Start(master);
/* 方式1: 零拷贝指针 */
int32_t out_size, in_size;
uint8_t *out_ptr, *in_ptr;
dll.GetIO(master, 1, &out_size, &out_ptr, &in_size, &in_ptr);
/* 方式2: 类型化读写 */
int32_t pos = dll.PdoReadInt32(master, 1, 2);
dll.PdoWriteInt32(master, 1, 2, pos + 1000);
/* 方式3: 无锁安全写入 */
dll.WriteSlaveOutputByte(master, 1, 0, 0x0F);
getchar();
dll.Stop(master);
dll.Dispose(master);
UNLOAD_DLL(&dll);
return 0;
}