MDP — 模块化设备
Modular Device Profile (MDP, ETG.5001) 是 EtherCAT 中描述模块化设备的标准协议。
通过 slave.GetMDP() 获取 MDP* 指针。从站不支持 MDP 时返回 nullptr,使用前必须判空。
进入 OP 时,master.SetState(EcState::OP) 会自动触发 MDP 模块化设备编排:
检测物理模块、写入已配置模块列表 (0xF030)、配置对应 SM/PDO。
GCAN-8200 等 MDP 设备无需手动写 0xF030,避免 SM3 length=0 导致进 SafeOp 报 AL=0x001E。
绝大多数场景下, 用户无需直接操作 MDP 类。
SDK 支持 MDP 模块热插拔再配置自修复。 但生产环境中几乎不会出现 MDP 模块热插拔的使用场景。 EtherCAT 从站的模块配置在设备上电后即固定, 运行期间不会动态变更。 部署 MDP 模块化设备推荐使用 DENI。
获取 MDP 实例
auto& slave = master.GetSlave(1);
MDP* mdp = slave.GetMDP(); // 从站不支持 MDP 时返回 nullptr
if (!mdp) {
printf("从站 1 不是 MDP 模块化设备\n");
return;
}
GetMDP() 仅在 slave.HasMDP() 为真时返回非空指针,且实例延迟创建、后续访问返回同一缓存对象。
属性
ConfiguredModules()
std::vector<MdpSlotInfo> ConfiguredModules() const;
已配置模块列表 (对象索引 0xF030)。通过 native MDPGetConfigModuleList 直读各槽位模块标识码。
返回值:
std::vector<MdpSlotInfo>— 已配置模块列表
相关结构:
struct MdpSlotInfo {
int SlotIndex; // 槽位索引 (从 1 开始)
uint32_t ModuleIdent; // 模块标识码 (32 位)
std::string ModuleName; // 模块名称
uint32_t ModuleType; // 模块设备类型码
std::string Status; // 状态 ("已配置" / "已检测")
uint16_t ObjectDictionaryIndex; // 对象字典基地址
std::string ModuleIOType; // I/O 类型 (默认 "Unknown")
std::string ToString() const; // 格式化为 "Slot N: 名称 (0xIDENT)"
};
示例:
MDP* mdp = slave.GetMDP();
if (!mdp) return;
for (auto& slot : mdp->ConfiguredModules()) {
printf("%s\n", slot.ToString().c_str());
}
DetectedModules()
std::vector<MdpSlotInfo> DetectedModules() const;
已检测模块列表 (对象索引 0xF050), 反映物理上实际插入的模块。通过 native MDPGetDetectedModuleList 直读。
返回值:
std::vector<MdpSlotInfo>— 已检测模块列表 (同ConfiguredModules结构)
示例:
MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& slot : mdp->DetectedModules())
printf("Slot %d: %s\n", slot.SlotIndex, slot.ModuleName.c_str());
}
GetAvailableModules()
std::vector<MdpAvailableModule> GetAvailableModules() const;
可用模块列表,由 DetectedModules() 派生 (每个已检测模块映射为一条可用模块记录)。
返回值:
std::vector<MdpAvailableModule>— 可用模块列表
相关结构:
struct MdpAvailableModule {
uint32_t ModuleIdent; // 模块标识码
uint32_t ModuleType; // 模块设备类型码
std::string ModuleName; // 模块名称
std::string Description; // 模块描述
};
示例:
MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& mod : mdp->GetAvailableModules())
printf("%s (0x%08X)\n", mod.ModuleName.c_str(), mod.ModuleIdent);
}
ModuleIndexDistance() / MaxModuleCount()
uint16_t ModuleIndexDistance() const; // 0xF000:01, 模块索引间距, 默认 0x0008
uint16_t MaxModuleCount() const; // 0xF000:02, 最大模块数量
读取 ETG.5001 Modular Device Profile 对象 0xF000 的核心属性,结果带缓存。
方法
GetModulePdoLayout()
std::vector<MdpModulePdoInfo> GetModulePdoLayout() const;
获取各模块在从站 IOmap 中的 PDO 布局。通过 CoE SDORead 读取 PDO Assignment (0x1C12/0x1C13) 和 PDO Mapping 条目, 累积计算各模块的字节偏移。
需要从站已完成 DENI 配置 (ConfigMap 后可用), 不依赖 ESI 文件。CoE 不可用或未检测到模块时返回空向量。
返回值:
std::vector<MdpModulePdoInfo>— 各模块的 PDO 布局信息, 无法获取时返回空向量
相关结构:
struct MdpModulePdoInfo {
int SlotIndex; // 槽位索引
uint32_t InputOffset; // 输入 PDO 偏移
uint16_t InputSize; // 输入 PDO 字节数
uint32_t OutputOffset; // 输出 PDO 偏移
uint16_t OutputSize; // 输出 PDO 字节数
};
示例:
MDP* mdp = slave.GetMDP();
if (mdp) {
for (auto& mod : mdp->GetModulePdoLayout())
printf("Slot %d: In=%uB @%u, Out=%uB @%u\n",
mod.SlotIndex, mod.InputSize, mod.InputOffset,
mod.OutputSize, mod.OutputOffset);
}
Discover()
std::vector<MdpDiscoveredModule> Discover() const;
通过 DLL C 高级 API 快速发现 MDP 模块,返回各槽位的模块 Profile / 标识码 / PDO 偏移信息。
相关结构:
struct MdpDiscoveredModule {
uint16_t SlotIndex; // 槽位索引
uint16_t ModuleProfile; // 模块 Profile
uint16_t ModuleIdent; // 模块标识码
uint16_t PdoInputOffset; // 输入 PDO 偏移
uint16_t PdoInputSize; // 输入 PDO 字节数
uint16_t PdoOutputOffset; // 输出 PDO 偏移
uint16_t PdoOutputSize; // 输出 PDO 字节数
};
DetectModules() / IsModuleConfigConsistent()
bool DetectModules() const; // 触发从站重新扫描已安装模块 (写 0xF002:00)
bool IsModuleConfigConsistent() const; // 已配置模块与已检测模块是否一致
AutoConfigureFromDetectedModules()
int AutoConfigureFromDetectedModules() const;
将已检测模块 (0xF050) 写入已配置模块列表 (0xF030) 完成自动配置。
返回值:
int— 成功配置的模块数量,-1表示失败
进入 OP 时 master.SetState() 已自动执行 MDP 编排,通常无需手动调用 AutoConfigureFromDetectedModules()。
自动配置
MDP 设备进入 OP 时由 master.SetState(EcState::OP) 自动完成模块编排 (检测 → 写 0xF030 → 配置 SM/PDO)。
推荐部署方式:
- 使用 DENI — Darra 配置工具导出,
master.SetENI("xxx.deni")加载即包含完整 MDP 配置
C/C++ SDK 不提供运行期 ESI 文件加载接口 (EtherCATMaster 没有 SetEsiFile() /
SetEsiFiles() 成员)。C++ SDK 中从站信息通过扫描自动从 EEPROM 读取, 或通过
SetENI() 加载的 ENI/DENI 配置文件获取。ESI 文件管理是 C# 层 EsiManager 的功能。