☰
从零开发 EtherCAT 主站(七):SOEM PDO 映射源码解析,深入理解 SyncManager、FMMU 与 IOMap
2026/10/25 11:47:35 网站建设 项目流程

从零开发 EtherCAT 主站(七):SOEM PDO 映射源码解析,深入理解 SyncManager、FMMU 与 IOMap

文章目录

  • 从零开发 EtherCAT 主站(七):SOEM PDO 映射源码解析,深入理解 SyncManager、FMMU 与 IOMap
    • 一、从 `simple_ng.c` 看 PDO 映射入口
    • 二、为什么需要 PDO Mapping?
    • 三、SOEM PDO 映射整体流程
    • 四、EtherCAT 从站内部结构:ESC、SyncManager 与 FMMU
    • 五、Logical Address:EtherCAT 为什么不用设备地址通信?
    • 六、FMMU:从 Logical Address 到 ESC RAM
    • 七、`ecx_config_map_group()` 源码分析
    • 八、`outputs` 和 `inputs` 指针到底是什么?
    • 九、完整流程总结
    • 十、本章总结

上一篇文章中,我们分析了 SOEM 主站初始化流程,了解了 EtherCAT 主站如何完成网卡初始化、从站扫描、设备信息读取等步骤,并最终进入 PRE-OP 状态。

但是,主站虽然知道了网络中有哪些从站,却还不知道如何与它们交换实时数据。

以伺服驱动器为例,我们希望周期性地发送控制字、目标位置、目标速度,同时读取状态字、实际位置等信息。这些数据如何进入 EtherCAT 通信帧?从站又如何知道应该读取哪些数据?

这就是PDO(Process Data Object)映射需要解决的问题。

本章将结合 SOEM 源码(ec_config.c、simple_ng.c),深入分析从 PDO 映射到最终形成IOMap的完整过程。

一、从simple_ng.c看 PDO 映射入口

在simple_ng.c的初始化流程中:

ecx_init()↓ecx_config_init()// 扫描从站,读取 EEPROM↓ecx_config_map_group()// ⬅️ PDO 映射的入口↓ecx_configdc()// 配置分布式时钟

ecx_config_map_group()是 PDO 映射的入口函数。它的原型如下:

intecx_config_map_group(ecx_contextt*context,void*pIOmap,uint8 group);

这个函数执行完成后,会输出类似下面的日志:

mapped 8O+12I bytes from 1 segments

Obytes和Ibytes分别表示输出过程数据和输入过程数据的总字节数。这些数据已经被映射到主站的IOMap中。

之后,周期通信函数:

ecx_send_processdata(context);ecx_receive_processdata(context,EC_TIMEOUTRET);

直接操作的就是这块IOMap内存。

二、为什么需要 PDO Mapping?

很多开发者会问:为什么不能像 CANopen 的 SDO 那样,每次通过索引和子索引去读写对象字典?

实际上 EtherCAT确实提供这种方式——就是 SDO。但 SDO 的问题在于实时性不足。

假设控制周期是 1kHz,每 1ms 主站都需要发送目标位置、读取实际位置和状态。如果每次都通过 SDO,通信过程是:

主站请求 → 等待 → 从站响应 → 解析

每个参数都需要一次完整的请求-响应往返,效率极低。

PDO 的核心思想是:提前规定好哪些数据需要周期交换,以及它们在通信帧中的固定位置。

例如一个伺服驱动器:

方向对象大小
RxPDO0x6040 Control Word16 bit
RxPDO0x607A Target Position32 bit
RxPDO0x60FF Target Velocity32 bit
TxPDO0x6041 Status Word16 bit
TxPDO0x6064 Position Actual32 bit
TxPDO0x606C Velocity Actual32 bit

RxPDO 总长 80 bit = 10 Byte,TxPDO 同样 10 Byte。主站只需要周期性地发送 10 字节、接收 10 字节,无需关心对象索引解析和数据转换。

这就是 PDO 高效率的根本原因。

三、SOEM PDO 映射整体流程

打开ec_config.c,SOEM 的 PDO 配置流程如下:

ecx_config_map_group() | v ecx_config_find_mappings() // ① 遍历从站,统计 PDO 大小 | v 遍历每个从站,配置 FMMU // ② 为每个从站建立逻辑地址映射 | v 生成 IOMap // ③ 形成主站可直接操作的连续内存

最终形成的数据通路是:

Master IOMap (主站内存) | | EtherCAT Frame (逻辑地址寻址) v Slave FMMU (逻辑地址 → 物理地址) | v Slave ESC RAM (SyncManager 管理) | v Slave Application Data

四、EtherCAT 从站内部结构:ESC、SyncManager 与 FMMU

为了理解源码,需要先了解 EtherCAT 从站控制器(ESC)的内部结构:

EtherCAT Frame | v +----------+ | ESC | +----------+ | +---------+---------+ | | v v SyncManager FMMU | | v v ESC RAM ←─── 逻辑地址映射

SyncManager(同步管理器):管理 ESC 内部的数据缓冲区,保证数据一致性,防止读写冲突。典型配置如下:

SyncManager方向用途
SM0主站 → 从站Mailbox 输出
SM1从站 → 主站Mailbox 输入
SM2主站 → 从站RxPDO(过程数据输出)
SM3从站 → 主站TxPDO(过程数据输入)

SM2 和 SM3 就是 PDO 数据的物理存储区域。

FMMU(现场总线内存管理单元):负责将 EtherCAT 的逻辑地址转换为从站 ESC 的物理地址。

FMMU 实现逻辑地址到物理地址的映射,使主站可以用统一的逻辑地址空间访问所有从站的数据,无需关心每个从站的物理内存地址。

五、Logical Address:EtherCAT 为什么不用设备地址通信?

传统总线(如 CAN)使用节点 ID 寻址:主站知道要访问哪个节点,然后发送数据。

EtherCAT 则采用逻辑地址寻址。主站将所有从站的物理内存映射到一个4GB 的虚拟逻辑空间中。主站只需发送一个报文,如同操作本地内存一样,一次性读写所有从站的过程数据。

例如,整个网络的逻辑地址布局可能是:

Logical Address Space (4GB) | +--- 0x0000 ← Slave 1 Output (SM2) | +--- 0x0010 ← Slave 2 Output (SM2) | +--- 0x0020 ← Slave 1 Input (SM3) | +--- 0x0030 ← Slave 2 Input (SM3)

主站只需要维护这一整块连续的逻辑地址空间,不需要逐个访问每个从站。这是 EtherCAT 能够实现微秒级实时通信的关键原因之一。

六、FMMU:从 Logical Address 到 ESC RAM

那么,逻辑地址0x1000如何知道对应哪个从站的哪块内存?

答案就是FMMU。

每个 FMMU 通道包含以下配置参数:

参数说明
逻辑起始地址在 4GB 逻辑空间中的起始位置
数据长度映射的数据大小(字节)
物理起始地址ESC 内部的物理内存地址
方向读(输入)或写(输出)

FMMU 和 SM 的协作关系如下:

主站发送帧 (逻辑地址 LRW) ↓ FMMU: 逻辑地址 → 物理地址(ESC 内部) ↓ SM: 物理地址 → 缓冲区管理(保证数据一致性) ↓ 从站应用层读写数据

简单来说:FMMU 负责“地址翻译”,SM 负责“数据缓冲”。

七、ecx_config_map_group()源码分析

回到 SOEM 源码。ecx_config_map_group()的核心逻辑如下:

intecx_config_map_group(ecx_contextt*context,void*pIOmap,uint8 group){uint32 LogAddr=0;// 逻辑起始地址// 1. 查找映射并设置 SyncManagerecx_config_find_mappings(context,group);// 2. 遍历所有从站,配置 FMMUfor(slave=1;slave<=*(context->slavecount);slave++){// 为每个从站分配逻辑地址// 配置 FMMU:逻辑起始地址 → ESC 物理地址// 更新 LogAddr 指针}// 3. 返回 IOMap 总大小return(int)LogAddr;}

第一步:ecx_config_find_mappings()

这个函数遍历所有从站,根据 EEPROM 中的 PDO 配置信息,统计每个从站的输入/输出数据大小,并计算出整个 Group 需要的总内存大小。

第二步:配置 FMMU

遍历从站,为每个从站的输出(SM2)和输入(SM3)分别创建 FMMU 映射:

  • 分配一段逻辑地址空间
  • 将逻辑地址映射到从站 ESC 的物理地址
  • 更新LogAddr指针,为下一个从站预留空间

八、outputs和inputs指针到底是什么?

这是 SOEM 中容易误解的地方。

在simple_ng.c中,grp->outputs和grp->inputs本质是指针,它们指向IOMap中的特定位置。

例如IOMap布局如下:

uint8 map[4096] | +--- 0x0000 ← Slave 1 Output (grp->outputs 指向这里) | +--- 0x0010 ← Slave 2 Output | +--- 0x0020 ← Slave 1 Input (grp->inputs 指向这里)

因此,修改*slave.outputs = value实际上就是修改map[]数组。下一次调用ecx_send_processdata()时,这块内存就会被封装成 EtherCAT 帧发送出去。

九、完整流程总结

ecx_config_init() // 扫描从站,读取对象字典 | v ecx_config_map_group() | +-------------------+ | | v v ecx_config_find_mappings() 遍历从站,配置 FMMU | | +---------+---------+ | v 生成 IOMap // 主站可直接操作的连续内存 | v 进入 OP 状态 | v ecx_send_processdata() // 周期发送 IOMap ecx_receive_processdata() // 周期接收 IOMap

十、本章总结

通过分析 SOEM 的ec_config.c,我们理解了 EtherCAT 主站最核心的一步:如何把设备对象转换成实时过程数据。

概念作用
PDO定义周期交换的数据(控制字、目标位置、状态字等)
SyncManager管理 ESC 内存区域(SM2=RxPDO,SM3=TxPDO)
FMMU逻辑地址 → 从站物理内存的映射
IOMapSOEM 主站最终操作的连续内存

配置完成后,周期通信函数ecx_send_processdata()和ecx_receive_processdata()本质上就是交换IOMap中的数据。

到这里,我们已经完成了从EtherCAT 网络初始化 → 扫描 Slave → 配置 PDO → 生成 IOMap → 周期通信的完整链路。

下一篇将继续深入 EtherCAT 最核心的实时环节:

《从零开发 EtherCAT 主站(八):SOEM 周期通信源码解析,ecx_send_processdata() 到底发送了什么?》

届时将分析 EtherCAT Frame 结构、LRW/LRD/LWR 命令、SOEM 如何组织过程数据,以及 Working Counter(WKC)的完整工作机制。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询