跳到主要内容

MDP -- 模块化设备

Modular Device Profile (MDP, ETG.5001) 是 EtherCAT 中描述模块化设备的标准协议。

自动处理

内部会自动处理 MDP 设备的模块检测和 PDO 配置。 绝大多数场景下,用户无需直接操作 MDP。

关于热插拔

SDK 支持 MDP 模块热插拔再配置自修复。 但生产环境中几乎不会出现 MDP 模块热插拔的使用场景。 EtherCAT 从站的模块配置在设备上电后即固定,运行期间不会动态变更。 部署 MDP 模块化设备推荐使用 DENI; 如需自动配置,请先用 esi_dll_load_directory() 导入对应的 ESI 文件,再让主站正常走 INIT → PreOp → OP 状态链,自动编排会在 PreOp 阶段完成(见下文 MDP 自动编排)。

模块索引间距

MDP 设备中各模块的对象字典地址按固定间距排列:间距由从站 OD 0xF000:01 (ModuleIndexDistance) 提供,每个槽位的对象字典地址 = 间距 × 槽位号。SDK 自动处理地址偏移计算。

MDP 检测​

BOOL GetSlaveHasMDP(uint16_t master, uint16_t slave);

检查从站是否为 MDP 模块化设备。返回 TRUE 表示从站声明了 MDP 模块。

模块结构​

/* ethercat_advanced.h */
typedef struct {
uint16_t slot_index; /* 槽位号 */
uint16_t module_profile; /* 模块配置文件 (190/195/290/790/6900) */
uint16_t module_ident; /* 模块标识 */
uint16_t pdo_input_offset; /* PDO 输入偏移 */
uint16_t pdo_input_size; /* PDO 输入大小 */
uint16_t pdo_output_offset; /* PDO 输出偏移 */
uint16_t pdo_output_size; /* PDO 输出大小 */
} mdp_module_t;

typedef struct {
int count;
mdp_module_t* modules;
} mdp_list_t;

模块发现 (ethercat_advanced.h)​

/* ethercat_advanced.h */
mdp_list_t* mdp_discover(uint16_t master, uint16_t slave);
void mdp_free(mdp_list_t* list);
  • mdp_discover() 通过 CoE 读取从站已检测模块列表(对象字典 0xF050,反映物理上实际插入的模块)
  • 返回值需调用 mdp_free() 释放,失败返回 NULL
PDO 偏移/大小字段不在 mdp_discover 填充

mdp_discover() 只填充 slot_index / module_profile / module_ident,四个 pdo_* 字段置 0(PDO 偏移需结合 PDO 分配表才能确定)。尺寸权威 = GetPdoAssignmentSizes() / GetPdoMappingSize()(slave/startup.h),偏移权威 = GetPdoOffsets()。

示例:

mdp_list_t* modules = mdp_discover(master, 1);
if (modules) {
for (int i = 0; i < modules->count; i++) {
mdp_module_t* m = &modules->modules[i];
printf("Slot %d: Profile=%d Ident=0x%04X\n",
m->slot_index, m->module_profile, m->module_ident);
}
mdp_free(modules);
}

模块标识列表直读 (slave/mdp.h)​

slave/mdp.h 提供轻量 static __inline 包装,直读 ETG.5001 标准模块标识数组(uint32_t 模块标识码),不走 SDO 缓存路径。底层由运行库实现。

/* slave/mdp.h — 以 master/slave 索引直接调用 */
int mdp_get_config_module_list(uint16_t master, uint16_t slave,
uint32_t* out_idents, int max);
int mdp_get_detected_module_list(uint16_t master, uint16_t slave,
uint32_t* out_idents, int max);
int mdp_check_module_match(uint16_t master, uint16_t slave,
int* out_first_mismatch);
  • mdp_get_config_module_list() — 读 0xF030 ConfigModuleIdent[](已配置模块标识)。返回写入数量,<=0 失败。
  • mdp_get_detected_module_list() — 读 0xF050 DetectedModuleIdent[](已检测模块标识)。返回写入数量,<=0 失败。
  • mdp_check_module_match() — 比对 0xF030 与 0xF050。返回 1=完全匹配,0=不匹配(out_first_mismatch 输出 0-based 槽位),<0=未支持/失败。
无句柄参数

这组包装是普通 C 函数,直接以 master/slave 索引调用,没有模块句柄参数。薄壳未就绪时列表类返回 0,校验类返回 <0。

MDP 自动编排 (ETG.5001)​

MDP 模块化设备的自动配置 = 读 0xF050(已检测)→ 匹配 ESI <Modules> → 生成 0xF030 写序列 + 主站 PDO Assignment + 模块 SdoInit。该编排在主站 INIT → PreOp 后由底层运行时自动执行,SDK 通常无需任何编排代码。

DarraEcat_MDPAutoEnumerate()​

int DarraEcat_MDPAutoEnumerate(uint16_t master_index);

对主站下所有 MDP 从站执行模块枚举(读取 0xF050 检测模块列表并刷新内部模块表)。返回处理的从站数量,<0 表示失败。普通 C 函数,直接调用 DarraEcat_MDPAutoEnumerate(master)。

部署 MDP 模块化设备

推荐使用 DENI 文件部署(一行加载 ESI/PDO/模块配置全套)。如需运行时自动配置,先用 esi_dll_load_directory() 导入对应 ESI 文件,再让主站正常走 INIT → PreOp → OP 状态链,自动编排会在 PreOp 阶段完成。

完整示例​

#include "ethercat.h"
#include "ethercat_advanced.h"
#include "slave/mdp.h"
#include <stdio.h>

int main(void) {
uint16_t master = Initialize();
SetNetwork(master, "\\Device\\NPF_{GUID}", "");
SetStateSequence(master, EC_STATE_PRE_OP, 5000);

uint16_t slave = 1;
if (!GetSlaveHasMDP(master, slave)) {
printf("从站不是 MDP 设备\n");
Dispose(master);
return 1;
}

/* 已检测模块 (slot/身份; PDO 偏移与大小用 GetPdoOffsets/GetPdoAssignmentSizes 另取) */
mdp_list_t* modules = mdp_discover(master, slave);
if (modules) {
for (int i = 0; i < modules->count; i++) {
mdp_module_t* m = &modules->modules[i];
printf("Slot %d: Profile=%d Ident=0x%04X\n",
m->slot_index, m->module_profile, m->module_ident);
}
mdp_free(modules);
}

/* 模块标识列表校验: 已配置 (0xF030) vs 已检测 (0xF050) */
int first_mismatch = -1;
int match = mdp_check_module_match(master, slave, &first_mismatch);
if (match == 1) {
printf("模块列表匹配\n");
} else if (match == 0) {
printf("模块不匹配, 首个差异槽位: %d\n", first_mismatch);
}

Dispose(master);
return 0;
}