【原创】IgH EtherCAT主站详解(六)--Datagram、PDO及Mailbox模块介绍
一、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 状态机
Datagram 生命周期
Datagram 在内核与用户空间的传递
深入源码
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 层次结构
技术详情
核心数据结构
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 映射表构建流程
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 字节)
邮箱通信时序
技术详情
邮箱头格式
邮箱数据前 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 | 厂商自定义协议 |
邮箱操作流程
深入源码
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信号量保护邮箱访问的互斥

浙公网安备 33010602011771号