IgH EtherCAT主站Datagram、PDO及Mailbox模块如何详细介绍?

摘要:目录一、Datagram 模块概览Datagram 是什么?Datagram 类型一览技术详情ec_datagram 结构体字段Datagram 状态机Datagram 生命周期Datagram 在内核与用户空间的传递深入源码ec_data
目录一、Datagram 模块概览Datagram 是什么?Datagram 类型一览技术详情ec_datagram 结构体字段Datagram 状态机Datagram 生命周期Datagram 在内核与用户空间的传递深入源码ec_datagram_init()ec_datagram_prealloc()Datagram 构造函数超时与重试机制外部数据报环形缓冲ec_mbox_data_t — 邮箱响应数据二、PDO 模块概览PDO 是什么?PDO 层次结构技术详情核心数据结构ec_pdo_entry_t — PDO 条目ec_pdo_t — PDO 描述ec_pdo_list_t — PDO 列表PDO 映射表构建流程PDO 总大小计算深入源码PDO 核心函数ec_pdo_init() / ec_pdo_clear()ec_pdo_init_copy()ec_pdo_add_entry()ec_pdo_equal_entries()ec_pdo_copy_entries()PDO 列表函数ec_pdo_list_add_pdo_copy()ec_pdo_list_copy()ec_pdo_list_equal()ec_pdo_list_total_size()三、Mailbox 模块概览邮箱通信原理邮箱通信时序技术详情邮箱头格式邮箱协议类型邮箱操作流程深入源码ec_slave_mbox_prepare_send()ec_slave_mbox_prepare_check()ec_slave_mbox_check()ec_slave_mbox_prepare_fetch()ec_slave_mbox_fetch()邮箱协议复用机制 一、Datagram 模块 3.5 — datagram.c / datagram.h — 数据报管理 概览 Datagram 是什么? Datagram (数据报) 是 EtherCAT 通信的基本传输单元。每个 Datagram 包含一个命令 (Command)、地址、数据和 Working Counter。多个 Datagram 可串联在一个以太网帧中发送。 核心特性 寻址模式: 支持自动递增 (AP)、配置地址 (FP)、广播 (B)、逻辑 (L) 四种寻址 状态机: INIT → QUEUED → SENT → RECEIVED/TIMED_OUT/ERROR 大小限制: 单个 Datagram 数据最大 EC_MAX_DATA_SIZE = 1486 字节 WKC 校验: 每个 Datagram 独立的 Working Counter 验证通信完整性 Datagram 类型一览 代码 类型名 说明 0x01 APRD Auto Increment Physical Read 0x02 APWR Auto Increment Physical Write 0x03 APRW Auto Increment Physical ReadWrite 0x04 FPRD Configured Address Physical Read 0x05 FPWR Configured Address Physical Write 0x06 FPRW Configured Address Physical ReadWrite 0x07 BRD Broadcast Read 0x08 BWR Broadcast Write 0x09 BRW Broadcast ReadWrite 0x0A LRD Logical Read 0x0B LWR Logical Write 0x0C LRW Logical ReadWrite 0x0D ARMW Auto Increment Read Multiple Write 0x0E FRMW Configured Address Read Multiple Write 技术详情 ec_datagram 结构体字段 字段 类型 说明 queue struct list_head 主站发送队列节点 sent struct list_head 已发送队列节点 device_index ec_device_index_t 发送设备索引(主/备) type ec_datagram_type_t Datagram 类型 address[4] uint8_t 目标地址(4 字节) data uint8_t * 数据缓冲区指针 data_origin ec_origin_t 数据来源(内部/外部) mem_size size_t 数据缓冲区总大小 data_size size_t 有效数据大小 index uint8_t Datagram 索引(主站自动分配,用于匹配响应) working_counter uint16_t Working Counter 值 state ec_datagram_state_t Datagram 状态 jiffies_sent unsigned long 发送时间(用于超时检测) jiffies_received unsigned long 接收时间 skip_count unsigned int 未收到时重入队次数 name[20] char Datagram 描述名(调试用) Datagram 状态机 stateDiagram-v2 [*] --> INIT: ec_datagram_init() INIT --> QUEUED: ec_master_queue_datagram() QUEUED --> SENT: ec_master_send_datagrams() SENT --> RECEIVED: 匹配到响应帧 SENT --> TIMED_OUT: 超时 (EC_IO_TIMEOUT) SENT --> ERROR: 发送/接收错误 RECEIVED --> QUEUED: 重新入队 TIMED_OUT --> QUEUED: 重试 ERROR --> INIT: 重置 Datagram 生命周期 flowchart TD A[初始化 ec_datagram_init] --> B[配置: ec_datagram_lrd/lrw/...] B --> C[入队: ec_master_queue_datagram] C --> D[发送: ec_master_send_datagrams] D --> E{收到响应?} E -->|是| F[更新 data + WKC] E -->|否| G{超时?} G -->|否| H[重新入队 skip_count++] H --> D G -->|是| I[标记 TIMED_OUT] F --> J[状态 → RECEIVED] I --> K[FSM 重试或报错] Datagram 在内核与用户空间的传递 sequenceDiagram participant App as 用户应用 participant Lib as libec (用户空间) participant Cdev as 字符设备 (内核) participant Master as Master (内核) participant NIC as 网卡驱动 App->>Lib: ecrt_master_send() Lib->>Cdev: ioctl(EC_IOCTL_SEND) Cdev->>Master: app_send_cb() Master->>Master: ec_master_send_datagrams() Master->>NIC: ec_device_send() NIC->>NIC: DMA 发送帧 NIC->>Master: 中断/Poll 回调 Master->>Master: ec_master_receive_datagrams() Master-->>Cdev: 完成 Cdev-->>Lib: ioctl 返回 Lib-->>App: 返回 深入源码 ec_datagram_init() 初始化 Datagram:清零所有字段,初始化链表节点,设置 state 为 EC_DATAGRAM_INIT,data_size 为 0。 ec_datagram_prealloc() 预分配数据缓冲区。若 mem_size < size,则重新分配。对于小数据量 (≤EC_MAX_DATA_SIZE) 使用内部缓冲区,大数据量使用外部缓冲区 (data_origin = EC_ORIG_EXTERNAL)。 Datagram 构造函数 每种 Datagram 类型有对应的构造函数,设置 type 和 address 字段: 函数 地址设置 ec_datagram_aprd(ap, offset, size) address[0..1]=AP, address[2..3]=offset ec_datagram_fprd(slave_addr, offset, size) address[0..1]=slave_addr, address[2..3]=offset ec_datagram_brd(offset, size) address[0..1]=0x0000, address[2..3]=offset ec_datagram_lrd(logical_addr, size) address[0..3]=logical_addr ec_datagram_frmw(slave_addr, offset, size) address[0..1]=slave_addr, address[2..3]=offset 超时与重试机制 Datagram 发送后进入 sent 队列。每次 ec_master_send_datagrams() 调用时,检查已发送队列: 若 jiffies - jiffies_sent > EC_IO_TIMEOUT (1000μs),标记为 TIMED_OUT 若 skip_count 超过阈值,输出统计信息 未收到且未超时的 Datagram 保持 SENT 状态 外部数据报环形缓冲 Master 维护 ext_datagram_ring[EC_EXT_RING_SIZE=32],用于 FSM 状态机的 Datagram 需求。环形缓冲避免频繁分配/释放: ext_ring_idx_fsm: FSM 侧索引(分配) ext_ring_idx_rt: RT 侧索引(回收) 通过 injection_seq_fsm / injection_seq_rt 序列号协调并发 ec_mbox_data_t — 邮箱响应数据 字段 类型 说明 data uint8_t * 邮箱响应数据缓冲区 data_size size_t 缓冲区总大小 payload_size size_t 有效载荷大小 每个从站为不同邮箱协议(CoE/FoE/SoE/EoE/VoE)维护独立的 ec_mbox_data_t,用于缓存接收到的邮箱响应。 二、PDO 模块 3.6 — pdo.c / pdo_entry.c / pdo_list.c — 过程数据对象 概览 PDO 是什么? PDO (Process Data Object, 过程数据对象) 是 EtherCAT 中定义从站 I/O 数据格式的机制。每个 PDO 包含一个索引号和多个 PDO Entry (条目),每个 Entry 描述一个具体的数据字段(如位置反馈、控制字等)及其位宽。 PDO 的两种角色 TxPDO (0x1A00–0x1BFF): 从站→主站方向(输入),如编码器位置、状态字 RxPDO (0x1600–0x17FF): 主站→从站方向(输出),如控制字、目标位置 来源: PDO 映射在 EEPROM (SII) 中定义,也可通过 CoE (SDO) 动态重配置 PDO 层次结构 flowchart TD A[Sync Manager] --> B[TxPDO List / RxPDO List] B --> C1[PDO 0x1A00] B --> C2[PDO 01A01] C1 --> D1[Entry: 控制字 0x6040:00 16bit] C1 --> D2[Entry: 目标位置 0x607A:00 32bit] C1 --> D3[Entry: 操作模式 0x6060:00 8bit] C2 --> D4[Entry: 扭矩限制 0x6072:00 16bit] C2 --> D5[填充 0x0000:00 8bit] 技术详情 核心数据结构 ec_pdo_entry_t — PDO 条目 字段 类型 说明 list struct list_head 链表节点 index uint16_t PDO 条目索引 (如 0x6040) subindex uint8_t PDO 条目子索引 (如 0x00) name char * 条目名称(从 SII 字符串获取) bit_length uint8_t 位宽度(如 16、32) ec_pdo_t — PDO 描述 字段 类型 说明 list struct list_head 链表节点 index uint16_t PDO 索引 (如 0x1A00) sync_index int8_t 所属 Sync Manager 索引 name char * PDO 名称 entries struct list_head PDO 条目链表 ec_pdo_list_t — PDO 列表 字段 类型 说明 list struct list_head PDO 链表(包含多个 ec_pdo_t) PDO 映射表构建流程 flowchart TD A[从站扫描 fsm_slave_scan] --> B[读取 EEPROM SII] B --> C[ec_slave_fetch_sii_pdos] C --> D{Category Type?} D -->|0x0032 TxPDO| E[解析 TxPDO 映射] D -->|0x0033 RxPDO| F[解析 RxPDO 映射] E --> G[创建 ec_pdo_t] F --> G G --> H[遍历 PDO 条目] H --> I[创建 ec_pdo_entry_t] I --> J[设置 index/subindex/bit_length] J --> K[添加到 pdo.entries 链表] K --> L[添加到 slave.sii.pdos] M[应用程序配置] --> N[ecrt_slave_config_pdos] N --> O[设置 Sync Manager PDO 分配] O --> P[配置 PDO 映射] P --> Q[ec_pdo_list_copy 覆盖默认映射] PDO 总大小计算 ec_pdo_list_total_size() 遍历所有 PDO 的所有 Entry,累加 bit_length,向上取整到字节。这个值决定了 FMMU 的 data_size,进而影响 Domain 的逻辑地址空间分配。 ⚠ 位级映射 当 PDO 总位宽不是 8 的整数倍时,需要填充 (Padding) Entry (index=0x0000, subindex=0x00)。IgH 通过 ec_pdo_add_entry() 支持添加填充条目。 深入源码 PDO 核心函数 ec_pdo_init() / ec_pdo_clear() 初始化/清理 PDO:设置默认值,释放名称字符串和所有 Entry。 ec_pdo_init_copy() 深拷贝 PDO:复制索引、名称和所有 Entry(递归调用 ec_pdo_entry_init_copy)。 ec_pdo_add_entry() 向 PDO 添加一个条目:分配 ec_pdo_entry_t,设置 index/subindex/bit_length,添加到 entries 链表尾部。 ec_pdo_equal_entries() 比较两个 PDO 的 Entry 列表是否一致(数量相同,每个 Entry 的 index/subindex/bit_length 相同)。用于判断从站当前 PDO 映射是否与配置匹配。 ec_pdo_copy_entries() 深拷贝 Entry 列表:先清空目标 PDO 的所有 Entry,再逐个拷贝源 PDO 的 Entry。 PDO 列表函数 ec_pdo_list_add_pdo_copy() 向 PDO 列表添加一个 PDO 的深拷贝。用于应用程序配置 PDO 映射时复制默认映射。 ec_pdo_list_copy() 整体拷贝 PDO 列表:清空目标列表,逐个深拷贝源列表的 PDO。 ec_pdo_list_equal() 比较两个 PDO 列表:遍历所有 PDO,调用 ec_pdo_equal_entries() 逐一比较。用于从站配置时判断是否需要重新配置 PDO 映射。 ec_pdo_list_total_size() 计算列表中所有 PDO 的总字节数: unsigned int bit_size = 0; list_for_each_entry(pdo, &pl-;>list, list) { list_for_each_entry(entry, &pdo-;>entries, list) { bit_size += entry->bit_length; } } return (bit_size + 7) / 8; // 向上取整到字节 三、Mailbox 模块 3.7 — mailbox.c / mailbox.h — 邮箱通信 概览 邮箱通信原理 邮箱 (Mailbox) 是 EtherCAT 中主站与从站之间非周期性通信的机制,用于传输配置数据、诊断信息等。邮箱通信基于 Sync Manager 实现,使用双缓冲区和握手机制确保数据可靠传输。 邮箱特性 双缓冲区: 每个 Sync Manager 有两个缓冲区,交替使用 握手机制: 写入方设置 Mailbox Status Flag,读取方清除标志 协议复用: 同一邮箱通道传输多种协议 (CoE/EoE/FoE/SoE/VoE) 固定大小: 邮箱大小在 EEPROM 中定义 (通常 128–512 字节) 邮箱通信时序 sequenceDiagram participant Master as 主站 participant SM as Sync Manager (邮箱) participant Slave as 从站 Note over Master,Slave: 主站 → 从站 (写邮箱) Master->>SM: FPWR 写入数据 + 设置写入标志 SM->>Slave: 从站检测到新数据 Slave->>SM: 读取数据 + 清除写入标志 Note over Master,Slave: 从站 → 主站 (读邮箱) Slave->>SM: 写入响应数据 + 设置写入标志 SM-->>Master: 主站轮询检查 (BRD/FPRD) Master->>SM: FPRD 读取响应 + 清除标志 技术详情 邮箱头格式 邮箱数据前 6 字节 (EC_MBOX_HEADER_SIZE = 6) 为邮箱头: 偏移 长度 字段 说明 0 2 Length 邮箱数据长度(不含头部) 2 1 Address 站地址 3 1 Channel/Priority 通道号 + 优先级 4 1 Type 邮箱协议类型 5 1 Counter/Status 计数器 / 状态标志 邮箱协议类型 值 名称 说明 使用场景 0x01 AOE ADS over EtherCAT Beckhoff ADS 通信 0x02 EOE Ethernet over EtherCAT 虚拟网络隧道 0x03 COE CANopen over EtherCAT SDO/PDO/Emergency 0x04 FOE File Access over EtherCAT 固件升级 0x05 SOE Servo over EtherCAT 伺服驱动 IDN 访问 0x0F VOE Vendor-specific 厂商自定义协议 邮箱操作流程 flowchart TD A[ec_slave_mbox_prepare_send] --> B[构造 FPWR Datagram] B --> C[写入从站接收邮箱] C --> D[ec_slave_mbox_prepare_check] D --> E[构造 FPRD Datagram] E --> F[ec_slave_mbox_check] F --> G{邮箱有数据?} G -->|否| H[等待/重试] H --> D G -->|是| I[ec_slave_mbox_prepare_fetch] I --> J[构造 FPRD Datagram] J --> K[ec_slave_mbox_fetch] K --> L[解析邮箱头 + 返回载荷] L --> M{Type 匹配?} M -->|是| N[返回数据] M -->|否| O[检查错误码] O --> P{邮箱错误?} P -->|是| Q[返回错误] P -->|否| R[重新检查] 深入源码 ec_slave_mbox_prepare_send() 位置: master/mailbox.c:51–93 准备发送邮箱数据: 预分配 Datagram (ec_datagram_fpwr),目标为从站接收邮箱偏移 (configured_rx_mailbox_offset) 计算总大小: EC_MBOX_HEADER_SIZE + size 填充邮箱头: Length、Address(站地址)、Channel、Type(协议类型) 返回指向载荷区域的指针,供调用者填入协议数据 ec_slave_mbox_prepare_check() 位置: master/mailbox.c:103–113 准备检查邮箱状态:构造 ec_datagram_fprd 读取从站发送邮箱的 Sync Manager 状态 (偏移 +4 的第 5 字节)。此操作是轮询式的——FSM 反复调用直到从站将数据写入邮箱。 ec_slave_mbox_check() 位置: master/mailbox.c:122–125 检查邮箱是否有数据:读取 datagram->data[5] 的第 5 位 (Mailbox Status Flag)。若为 1 表示从站已写入数据到发送邮箱。 ec_slave_mbox_prepare_fetch() 位置: master/mailbox.c:134–146 准备读取邮箱数据:构造 ec_datagram_fprd,读取从站发送邮箱完整内容(大小为 configured_tx_mailbox_size)。 ec_slave_mbox_fetch() 位置: master/mailbox.c:172–229 处理接收到的邮箱数据: 读取邮箱头中的 Length 字段 验证邮箱状态 (Counter 字段) 检测邮箱错误 (bit 0 of Counter 为错误标志) 返回指向载荷数据的指针和大小 此函数由各协议 FSM 调用,FSM 负责解析具体的协议内容。 邮箱协议复用机制 同一个从站的邮箱通道被多种协议共享。IgH 的处理方式: 每个从站的 ec_slave 中为每种协议维护独立的 ec_mbox_data_t 缓冲区 ec_mbox_prot_data_prealloc() 为指定协议预分配缓冲区 当收到邮箱响应时,根据 Type 字段分发到对应协议的缓冲区 valid_mbox_data 标志指示有未处理的邮箱数据 mbox_sem 信号量保护邮箱访问的互斥