- 一、Device 模块
- 概览
- Device 模块是什么?
- 技术详情
- ec_device 结构体字段
- 设备抽象层架构
- 设备绑定/解绑流程
- 深入源码
- ec_device_init()
- ec_device_attach()
- ec_device_poll()
- ec_device_send()
- ec_device_tx_data()
- SKB 环形缓冲
- 概览
- 二、Ethernet (EoE) 模块
- 概览
- EoE 是什么?
- 技术详情
- ec_eoe 结构体关键字段
- EoE 状态机
- EoE 帧分片与重组
- EoE 帧类型
- 深入源码
- ec_eoe_init()
- ec_eoe_run()
- EoE 状态函数详解
- ec_eoe_state_rx_start (Line 744)
- ec_eoe_state_rx_fetch_data (Line 839)
- ec_eoe_state_tx_start (Line 1021)
- ec_eoe_state_tx_sent (Line 1101)
- ec_eoe_request_t — EoE IP 参数请求
- 概览
- 三、EEPROM / SII 处理
- 概览
- EEPROM (SII) 是什么?
- EEPROM 内存映射
- 技术详情
- 固定信息区 (0x0000–0x003F)
- Category 类型表
- EEPROM 读取流程
- 深入源码
- SII FSM 状态机 (fsm_sii.c)
- SII FSM 状态函数详解
- ec_fsm_sii_state_start_reading (Line 266)
- ec_fsm_sii_state_read_check (Line 290)
- ec_fsm_sii_state_read_fetch (Line 329)
- ec_fsm_sii_state_start_writing (Line 439)
- ec_fsm_sii_state_write_check (Line 461) / write_check2 (Line 499)
- sii_firmware.c — SII 固件覆盖
- ec_sii_image_t — SII 完整镜像
- ESC EEPROM 控制寄存器 (N32H7x5EC)
- 概览
一、Device 模块
3.8 — device.c / device.h — 设备抽象层
概览
Device 模块是什么?
Device 模块是 IgH 主站与物理网卡驱动之间的抽象层。每个 ec_device 封装了一个 Linux net_device,提供 EtherCAT 帧的发送和接收功能。主站最多支持两个设备(主设备 + 备设备),实现冗余拓扑。
核心功能
设备绑定: 将网卡驱动注册到主站(attach/detach)
帧发送: 构造 SKB 并通过网卡 DMA 发送
帧接收: 通过 Poll 函数获取接收到的帧数据
SKB 环形缓冲: 预分配 16 个 SKB 用于发送,避免运行时分配
统计: 帧收发计数、字节数、错误数
技术详情
ec_device 结构体字段
| 字段 | 类型 | 说明 |
|---|---|---|
master |
ec_master_t * |
所属主站 |
dev |
struct net_device * |
Linux 网络设备指针 |
poll |
ec_pollfunc_t |
网卡 Poll 函数指针(中断替代) |
module |
struct module * |
网卡驱动模块指针 |
open |
uint8_t |
设备是否已打开 |
link_state |
uint8_t |
链路状态 |
tx_skb[EC_TX_RING_SIZE] |
struct sk_buff ** |
发送 SKB 环形缓冲(16 个) |
tx_ring_index |
unsigned int |
当前发送缓冲索引 |
jiffies_poll |
unsigned long |
最后一次 Poll 的时间 |
tx_count / rx_count |
u64 |
收发帧计数 |
tx_bytes / rx_bytes |
u64 |
收发字节计数 |
tx_errors |
u64 |
发送错误计数 |
tx/rx_frame_rates[3] |
s32 |
不同统计周期的帧速率 |
tx/rx_byte_rates[3] |
s32 |
不同统计周期的字节速率 |
设备抽象层架构
设备绑定/解绑流程
深入源码
ec_device_init()
位置: master/device.c:74–167
初始化设备:清零结构体,预分配 16 个 (EC_TX_RING_SIZE) 发送 SKB。每个 SKB 预留 ETH_HLEN + EC_MAX_DATA_SIZE 空间,确保发送时无需动态分配。
ec_device_attach()
位置: master/device.c:223–248
绑定网卡设备:保存 net_device 指针、poll 函数和 module 指针。这是网卡驱动通过 ecdev_offer() 将自身注册到 IgH 的入口。
ec_device_poll()
位置: master/device.c:563–578
调用网卡驱动的 poll 函数,模拟中断处理。在 EtherCAT 模式下,网卡中断被禁用,由主站线程主动调用 poll 获取接收数据。这是 IgH 实现确定性的关键——避免中断延迟的不确定性。
ec_device_send()
位置: master/device.c:415–453
发送以太网帧:
- 获取当前发送 SKB:
tx_skb[tx_ring_index] - 设置 SKB 长度:
ETH_HLEN + size - 通过
netdev_ops->ndo_start_xmit或hard_start_xmit提交到网卡 - 递增
tx_ring_index(环形) - 更新发送统计计数
ec_device_tx_data()
位置: master/device.c:396–406
返回当前发送 SKB 的数据指针 (跳过 ETH_HLEN = 14 字节以太网头)。主站使用此指针直接写入 EtherCAT 帧数据,避免额外拷贝。
SKB 环形缓冲
| 参数 | 值 | 说明 |
|---|---|---|
EC_TX_RING_SIZE |
16 (0x10) | 环形缓冲大小 |
| 每个 SKB 大小 | ETH_HLEN + EC_MAX_DATA_SIZE | 14 + 1486 = 1500 字节 |
| 分配时机 | ec_device_init() |
初始化时一次性分配 |
二、Ethernet (EoE) 模块
3.9 — ethernet.c / ethernet.h + eoe_request.h — Ethernet over EtherCAT
概览
EoE 是什么?
EoE (Ethernet over EtherCAT) 允许通过 EtherCAT 网络传输标准以太网帧,实现从站的网络隧道功能。IgH 为每个支持 EoE 的从站创建一个虚拟网络接口 (如 eoe0s0),应用程序可以像操作普通网卡一样配置 IP 地址、路由等。
典型应用场景
网络桥接: 将从站的 EoE 接口桥接到物理网络,实现远程管理
IP 配置: 通过 EoE 为从站设置 IP 地址、子网掩码、网关
Web 管理: 访问从站内置 Web 服务器进行参数配置
数据采集: 从站通过 EoE 上传诊断数据到上位机
技术详情
ec_eoe 结构体关键字段
| 分类 | 字段 | 类型 | 说明 |
|---|---|---|---|
| 基础 | master |
ec_master_t * |
所属主站 |
slave |
ec_slave_t * |
关联从站 | |
state |
void (*)(ec_eoe_t *) |
当前状态函数 | |
| 虚拟网卡 | dev |
struct net_device * |
虚拟网络设备 |
| 接收 | rx_skb |
struct sk_buff * |
当前接收 SKB |
rx_expected_fragment |
uint8_t |
期望的下一个分片号 | |
rx_counter / rx_rate |
uint32_t |
接收字节计数/速率 | |
rx_idle |
unsigned int |
接收空闲标志 | |
| 发送 | tx_ring |
struct sk_buff ** |
发送帧环形缓冲 |
tx_ring_size |
unsigned int |
发送环形缓冲大小 | |
tx_frame_number |
uint8_t |
当前发送帧号 | |
tx_fragment_number |
uint8_t |
当前发送分片号 | |
tx_offset |
size_t |
当前发送偏移 | |
tx_counter / tx_rate |
uint32_t |
发送字节计数/速率 |
EoE 状态机
EoE 帧分片与重组
EoE 将标准以太网帧分片传输,每个分片通过邮箱发送:
| 参数 | 值 | 说明 |
|---|---|---|
| 分片载荷 | 邮箱大小 - 邮箱头 - EoE 头 | 取决于从站邮箱配置 |
| 帧号 | tx_frame_number |
标识一帧的所有分片 |
| 分片号 | tx_fragment_number |
从 0 递增 |
| 最后分片标志 | EoE 头中设置 | 接收方用于判断帧是否完整 |
| 发送环形缓冲 | tx_ring |
默认 100 个 SKB 槽位 |
EoE 帧类型
| 类型 | 值 | 说明 |
|---|---|---|
EC_EOE_TYPE_FRAME_FRAG |
0x00 | EoE 帧分片(收发) |
EC_EOE_TYPE_TIMESTAMP_RES |
0x01 | 时间戳响应 |
EC_EOE_TYPE_INIT_REQ/RES |
0x02/0x03 | IP 参数设置请求/响应 |
EC_EOE_TYPE_MACFILTER_REQ/RES |
0x04/0x05 | MAC 地址过滤设置 |
深入源码
ec_eoe_init()
位置: master/ethernet.c:207
EoE 处理器初始化:
- 通过
alloc_netdev()创建虚拟网络设备,命名格式eoe<MASTER>[as]<SLAVE> - 设置 MAC 地址(基于主站 NIC 地址或生成唯一地址)
- 分配发送环形缓冲 (默认 100 个 SKB 槽位)
- 初始化状态机为
ec_eoe_state_rx_start - 调用
register_netdev()注册网络设备到内核
ec_eoe_run()
由 EoE 线程周期性调用,执行 EoE 状态机的当前状态函数。每次调用处理一个状态转移(接收检查/发送分片)。
EoE 状态函数详解
ec_eoe_state_rx_start (Line 744)
初始化接收序列:准备检查从站发送邮箱状态。
ec_eoe_state_rx_fetch_data (Line 839)
处理接收到的 EoE 数据分片:
- 解析 EoE 帧号和分片号
- 如果是第一分片 (
fragment_number == 0),分配新的 SKB - 将分片数据拷贝到 SKB
- 如果是最后分片,将完整帧通过
netif_rx()传递给内核网络栈
ec_eoe_state_tx_start (Line 1021)
开始发送流程:检查是否有待发送的以太网帧,若有则从 tx_ring 取出并开始分片发送。
ec_eoe_state_tx_sent (Line 1101)
检查上一分片是否发送成功,若成功则继续发送下一分片,否则重试。
ec_eoe_request_t — EoE IP 参数请求
用于通过 EoE 协议设置从站 IP 参数:
| 字段 | 说明 |
|---|---|
mac_address[6] |
MAC 地址 |
ip_address |
IP 地址 |
subnet_mask |
子网掩码 |
gateway |
默认网关 |
dns |
DNS 服务器 |
name[32] |
主机名 |
result |
操作结果码 |
请求通过 slave->eoe_requests 队列传递到从站 FSM,由 fsm_eoe 处理发送。
三、EEPROM / SII 处理
3.10 — fsm_sii.c / sii_firmware.c — EEPROM 读写与解析
概览
EEPROM (SII) 是什么?
EEPROM (电可擦可编程只读存储器) 是 EtherCAT 从站中的非易失性存储器,存储从站的 SII (Slave Information Interface, 从站信息接口) 数据。SII 数据定义了从站的身份、通信参数、PDO 映射等关键信息,主站通过 ESC 的 EEPROM 访问寄存器 (0x0500–0x05FF) 读写这些数据。
SII 数据内容
固定信息: 厂商 ID、产品码、序列号、邮箱配置
Category 区: 字符串、通用信息、Sync Manager、PDO 映射、DC 参数等
用户数据: 厂商自定义数据区域
大小: 典型 4KB–16KB(IgH 限制最大EC_MAX_SII_SIZE = 4096words)
EEPROM 内存映射
图:EEPROM 内存布局 — 固定信息区 (0x0000–0x003F) + Category 变长区
技术详情
固定信息区 (0x0000–0x003F)
| 字偏移 | 大小 (words) | 内容 | 源码常量 |
|---|---|---|---|
| 0x00–0x01 | 2 | 配置站地址/别名 | EC_ALIAS_SII_OFFSET = 0x04 |
| 0x04–0x05 | 2 | 厂商 ID (Vendor ID) | EC_VENDOR_SII_OFFSET = 0x08 |
| 0x05–0x06 | 2 | 产品码 (Product Code) | EC_PRODUCT_SII_OFFSET = 0x0A |
| 0x06–0x07 | 2 | 版本号 (Revision Number) | EC_REVISION_SII_OFFSET = 0x0C |
| 0x07–0x08 | 2 | 序列号 (Serial Number) | EC_SERIAL_SII_OFFSET = 0x0E |
| 0x0A–0x0B | 2 | Bootstrap 邮箱偏移/大小 | — |
| 0x0C–0x0D | 2 | Bootstrap 邮箱发送偏移/大小 | — |
| 0x0E–0x0F | 2 | 标准邮箱接收偏移/大小 | — |
| 0x10–0x11 | 2 | 标准邮箱发送偏移/大小 | — |
| 0x12 | 1 | 邮箱协议位域 | — |
| 0x1F–0x20 | 2 | EEPROM 大小 | — |
字节寻址 vs 字寻址
EEPROM 使用字 (word, 2字节) 寻址。表中偏移为字偏移,实际 ESC 寄存器访问使用字节地址(字偏移 × 2)。源码常量如
EC_VENDOR_SII_OFFSET = 0x08为字节偏移。
Category 类型表
| Type | 名称 | 内容 | 解析函数 |
|---|---|---|---|
| 0x001A | Strings | 文本字符串表(设备名称等) | ec_slave_fetch_sii_strings() |
| 0x001B | General | 设备通用信息 | — |
| 0x001C | FMMU 描述 | FMMU 使用方式 | — |
| 0x001D | Sync Manager 描述 | SM 配置参数 | — |
| 0x001E | General 类别 | CoE 标志、物理层、功耗 | ec_slave_fetch_sii_general() |
| 0x0020 | TxPDO | 输入 PDO 映射 | ec_slave_fetch_sii_pdos() |
| 0x0021 | RxPDO | 输出 PDO 映射 | ec_slave_fetch_sii_pdos() |
| 0x0028 | FMMU 使用 | 每个 FMMU 的使用描述 | — |
| 0x0029 | Sync Manager | 每个 SM 的配置 | ec_slave_fetch_sii_syncs() |
| 0x0032 | TxPDO (扩展) | 扩展输入 PDO | — |
| 0x0033 | RxPDO (扩展) | 扩展输出 PDO | — |
| 0x003C | DC 同步 | 分布式时钟同步参数 | — |
| 0xFFFF | 结束标记 | Category 链表终止 | — |
EEPROM 读取流程
深入源码
SII FSM 状态机 (fsm_sii.c)
SII FSM 状态函数详解
ec_fsm_sii_state_start_reading (Line 266)
发起 EEPROM 读取请求:通过 FPWR 向 ESC 寄存器 0x0502 写入读取命令(设置地址 + 读使能)。
ec_fsm_sii_state_read_check (Line 290)
检查 EEPROM 读取状态:通过 FPRD 读取 0x0502 寄存器。
- 检查 busy 位 (bit 0):EEPROM 仍在读取中,等待下次执行
- 检查 loaded 位 (bit 4):数据已加载到寄存器
- 若超时 (
EC_IO_TIMEOUT),重试
ec_fsm_sii_state_read_fetch (Line 329)
读取 EEPROM 数据:通过 FPRD 从 ESC 寄存器 0x0508 和 0x050C 读取 4 字节数据(每次读取 2 个 word)。将数据存储到 SII 缓冲区。
ec_fsm_sii_state_start_writing (Line 439)
发起 EEPROM 写入请求:通过 FPWR 向 ESC 寄存器 0x0508/0x50C 写入数据,然后向 0x0502 写入写入命令。
ec_fsm_sii_state_write_check (Line 461) / write_check2 (Line 499)
检查 EEPROM 写入完成状态:读取 0x0502 寄存器,等待 busy 位清除。
sii_firmware.c — SII 固件覆盖
该模块允许从文件系统加载自定义 SII 镜像,替代 EEPROM 内容:
- 查找路径: 从
EC_SII_DIR目录查找 SII 文件 - 文件命名: 基于 vendor_id/product_code/revision_number 定位文件
- 加载机制: 通过 Linux 固件请求 API (
request_firmware()) - 用途: 无需物理修改 EEPROM 即可更新从站配置,支持 SII 数据热更新
ec_sii_image_t — SII 完整镜像
| 字段 | 类型 | 说明 |
|---|---|---|
list |
struct list_head |
链表节点(master->sii_images) |
words |
uint16_t * |
原始 SII 字数据 |
nwords |
size_t |
SII 内容大小(words) |
sii |
ec_sii_t |
解析后的 SII 数据 |
ESC EEPROM 控制寄存器 (N32H7x5EC)
| 地址 | 名称 | 说明 |
|---|---|---|
| 0x0500 | EEPROM 配置 | EEPROM 访问配置寄存器 |
| 0x0502 | EEPROM 控制/状态 | 读/写命令、busy/loaded 状态位 |
| 0x0504 | EEPROM 地址 | 读/写起始地址 |
| 0x0508 | EEPROM 数据 (低) | 读/写数据低 16 位 |
| 0x050A | EEPROM 数据 (高) | 读/写数据高 16 位 |
