简述
IgH EtherCAT Master 是 Linux 平台上一款开源的 EtherCAT 主站实现,广泛应用于工业自动化领域。本项目详细记录了 IgH 主站的启动流程、配置方法、多主站部署以及时钟同步等核心技术的学习心得 。
主要内容
-
启动流程分析(systemd / SysV init 两种方式)
-
配置文件详解
-
内核模块加载原理
-
多主站配置与冗余
-
DC 时钟同步
-
故障排查与性能调试
技术特性
-
✅ 支持单主站/多主站配置
-
✅ 支持双网卡冗余备份
-
✅ 支持 DC 时钟同步
-
✅ 兼容多种网卡驱动
-
✅ 支持 EOE、COE、FOE等等
环境要求
-
Linux 内核
-
IgH EtherCAT 主站源码
-
兼容的网卡设备
资源参考
-
IgH EtherCAT Master 官方源码
-
EtherCAT 技术规范
正文
目标平台 : RK3576 ARM64 IgH 版本 : 1.5.2 内核版本: Linux 6.12
一、启动流程
1.1 启动流程概述
1.1.1 整体架构图
EtherCAT 主站的启动是一个涉及用户空间和内核空间的多层协作过程。

核心要点:
-
用户空间配置驱动内核行为 - 通过配置文件传递 MAC 地址等参数
-
双模块协作 - ec_master.ko 提供框架,ec_xxx.ko 驱动硬件
-
完全旁路 Linux 网络栈 - 实现微秒级实时响应
-
DMA 零拷贝 - 主站与驱动直接共享内存
1.2 配置文件详解
1.2.1 /etc/ethercat.conf - 主配置文件(systemd 用)
文件路径: /usr/local/etc/ethercat.conf(构建产物) → /etc/ethercat.conf(实际使用)
文件作用:
-
定义主站使用的网卡设备(通过 MAC 地址)
-
配置设备驱动模块
-
配置 EOE(EtherCAT over Ethernet)接口
-
主要供 ethercatctl 脚本使用
| 配置项 | 说明 | 示例值 | 是否必需 |
|---|---|---|---|
| MASTER0_DEVICE | 主站 0 的网卡 MAC 地址 | c4:83:4f:27:30:5b | 必需 |
| MASTER1_DEVICE | 主站 1 的网卡 MAC 地址 | (可选) | 可选 |
| MASTER0_BACKUP | 主站 0 的备份设备 MAC 地址 | (可选,用于冗余) | 可选 |
| DEVICE_MODULES | 网卡驱动模块名称 | dwmac-rk/igb/generic | 必需 |
| EOE_INTERFACES | EOE 虚拟接口 | eoe0s20 | 可选 |
| LINK_DEVICES | 启动时 UP 的网卡 | eth0 | 推荐 |
1.2.2 /etc/sysconfig/ethercat - 模块参数配置(SysV 用)
文件路径: /etc/sysconfig/ethercat
文件作用:
-
为内核模块传递参数
-
定义系统启动时加载的模块和行为
-
主要供 init.d/ethercat 脚本使用

1.2.3 /etc/init.d/ethercat - SysV init 启动脚本
文件路径: /etc/init.d/ethercat
文件作用:
-
传统的 Linux SysV init 启动脚本
-
负责加载/卸载内核模块
-
管理系统启动 (stop/start/restart/status) 流程
启动流程分析:
以下是基于提供的Mermaid语法生成的流程图文本描述及解释:
流程图解析
graph TB
START[开始] --> R1[读取配置]
R1 --> R2[解析 MAC 地址]
R2 --> R3[设置网口 UP]
R3 --> R4[加载 ec_master 模块]
R4 --> R5[卸载原网卡驱动]
R5 --> R6[加载 EtherCAT 驱动]
R6 --> R7[创建设备节点]
R7 --> END[完成]
流程说明
该流程图描述了一个典型的EtherCAT主站初始化过程。从开始到完成的7个关键步骤按顺序执行,每个步骤依赖前一个步骤的输出。
关键步骤说明
读取配置:从配置文件中获取必要的参数,包括网络接口、MAC地址等。
解析MAC地址:将配置中的MAC地址字符串转换为驱动可识别的格式。
设置网口UP:通过系统命令激活指定的网络接口。
加载ec_master模块:加载EtherCAT主站核心模块,提供基础通信能力。
卸载原网卡驱动:移除系统原有的网络驱动以避免冲突。
加载EtherCAT驱动:加载专用的EtherCAT协议栈驱动。
创建设备节点:在/dev目录下生成EtherCAT设备文件供应用程序访问。
关键代码片段:
# 读取配置
ETHERCAT_CONFIG=/etc/sysconfig/ethercat
. ${ETHERCAT_CONFIG}
# 解析 MAC 地址
while true; do
DEVICE=$(eval echo "\${MASTER${MASTER_INDEX}_DEVICE}")
if [ -z "${DEVICE}" ]; then break; fi
parse_mac_address ${DEVICE}
DEVICES=${DEVICES}${MAC}
done
# 加载主站模块
modprobe ec_master main_devices=${DEVICES} backup_devices=${BACKUPS}
# 替换网卡驱动
for MODULE in ${DEVICE_MODULES}; do
rmmod ${MODULE}
modprobe ec_${MODULE}
done
1.2.4 /usr/local/sbin/ethercatctl - systemd 启动脚本
文件路径: /usr/local/sbin/ethercatctl
文件作用:
-
systemd 环境下被调用的启动脚本
-
读取 /etc/ethercat.conf 配置文件
-
与 init.d 脚本功能类似
与 init.d/ethercat 的对比:
| 差异点 | ethercatctl | init.d/ethercat |
|---|---|---|
| 配置文件 | /etc/ethercat.conf | /etc/sysconfig/ethercat |
| 模块参数传递 | main_devices= | main_devices= |
| 驱动替换逻辑 | 相同 | 相同 |
1.2.5 ethercat.service - systemd 服务单元
文件路径: /usr/local/lib/systemd/system/ethercat.service
文件作用:
-
定义 systemd 服务单元
-
指定启动/停止命令
-
设置依赖关系
文件内容:
[Unit]
Description=EtherCAT Master Kernel Modules
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/ethercatctl start
ExecStop=/usr/local/sbin/ethercatctl stop
[Install]
WantedBy=multi-user.target
关键参数说明:
| 参数 | 值 | 说明 |
|---|---|---|
| Type | oneshot | 服务只执行一次就退出 |
| RemainAfterExit | yes | 退出后仍保持活跃状态 |
| ExecStart | ethercatctl start | 启动时执行的命令 |
| WantedBy | multi-user.target | 开机自启目标 |
1.3 两种启动方式对比
IgH EtherCAT Master 支持两种启动方式:systemd 和 SysV init。
1.3.1 方式一:systemd 启动
EtherCAT 系统启动流程解析
以下是基于 Mermaid 语法生成的 EtherCAT 系统启动流程图解析:
graph TB
A[系统启动] --> B[systemd 初始化]
B --> C[读取 ethercat.service]
C --> D[systemctl start ethercat]
D --> E[读取/etc/ethercat.conf]
E --> F[解析配置变量]
F --> G[modprobe ec_master]
G --> H[注册/dev/EtherCAT*]
H --> I[modprobe ec_dwmac-rk]
I --> J[设备就绪 Phase:Idle]
流程步骤说明
systemd 初始化阶段
Linux 系统启动后由 systemd 接管初始化过程,读取服务单元文件(如 ethercat.service)配置。
服务启动阶段
通过 systemctl 命令触发 ethercat 服务启动,服务管理器会按照单元文件定义的顺序加载依赖项和执行启动命令。
配置文件处理
系统读取 /etc/ethercat.conf 主配置文件,解析其中定义的网络接口、主站参数等关键变量。
内核模块加载
动态加载 ec_master 主站模块,该模块负责创建字符设备文件(如 /dev/EtherCAT0)并提供用户空间接口。
从站驱动加载
加载特定网卡驱动模块(如示例中的 ec_dwmac-rk),该步骤可能因硬件差异而不同,需匹配实际使用的以太网控制器。
状态转换
主站完成初始化后进入空闲(Idle)状态,等待后续的拓扑扫描和状态机控制指令。此时可通过 ethercat 命令行工具查询状态。
关键文件说明
- ethercat.service:定义服务启动顺序、依赖关系和 ExecStart 命令
- ethercat.conf:包含 MASTER0_DEVICE、DEVICE_MODULES 等硬件相关配置
- ec_master.ko:主站核心模块,实现 EtherCAT 协议栈和主站功能
常见调试方法
检查服务状态:
systemctl status ethercat
查看内核模块加载:
lsmod | grep ec_
验证设备节点:
ls -l /dev/EtherCAT*
查看启动日志:
journalctl -u ethercat -b
启动命令:
bash
systemctl start ethercat # 启动
systemctl stop ethercat # 停止
systemctl restart ethercat # 重启
systemctl status ethercat # 状态
systemctl enable ethercat # 开机自启
1.3.2 方式二:SysV init 启动
以下是针对 EtherCAT 启动流程的流程图代码(Graph TB 语法),可直接用于 Mermaid 工具渲染:
流程节点说明
- 系统启动:Linux 内核完成初始化后进入用户空间
- 运行级别 3:多用户命令行模式(无图形界面)
- /etc/rc3.d/:该运行级别对应的服务脚本目录
- S99ethercat:EtherCAT 服务的软链接(数字99表示启动顺序)
- init.d/ethercat:实际的 EtherCAT 初始化脚本
- /etc/sysconfig/ethercat:主配置文件(路径可能因发行版而异)
- ec_master:EtherCAT 主站内核模块
- ec_dwmac-rk:特定网卡驱动模块(示例为 Rockchip DW MAC 驱动)
关键配置建议
-
确保
/etc/sysconfig/ethercat包含正确的 MAC 地址配置:MASTER0_DEVICE="00:0a:35:00:01:02" -
模块加载顺序可通过
ETHERCAT_MODULE_ORDER变量调整:ETHERCAT_MODULE_ORDER="ec_master ec_dwmac-rk"
调试方法
-
检查模块加载状态:
bashlsmod | grep ec_ -
查看系统日志:
bashjournalctl -u ethercat
启动命令:
bash
/etc/init.d/ethercat start # 启动
/etc/init.d/ethercat stop # 停止
/etc/init.d/ethercat restart # 重启
/etc/init.d/ethercat status # 状态
chkconfig --add ethercat # 开机自启
1.3.3 启动方式判别机制
系统如何知道使用哪种启动方式?
系统不会自动判别 ,而是由用户选择的安装和配置方式决定:

判别流程:
| 步骤 | 用户操作 | 系统行为 | 结果 |
|---|---|---|---|
| 1. 安装 | 用户选择安装 systemd 服务或 SysV 脚本 | 安装对应的服务文件 | 决定使用哪种启动方式 |
| 2. 配置 | 用户创建对应的配置文件 | 读取相应配置文件 | systemd 读 /etc/ethercat.conf SysV 读 /etc/sysconfig/ethercat |
| 3. 启动 | 用户执行对应的启动命令 | 执行相应的启动脚本 | 加载内核模块 |
| 4. 开机自启 | 用户设置对应的自启服务 | 在对应运行级别创建链接 | systemd 或 SysV 管理 |
用户配置选择:
选择 systemd 启动(推荐现代系统)
适用系统:
-
Ubuntu 16.04+
-
Debian 8+
-
CentOS 7+
-
openSUSE 12+
配置步骤:
bash
# 1. 安装 systemd 服务文件
sudo cp /usr/local/lib/systemd/system/ethercat.service /etc/systemd/system/
# 2. 创建配置文件 /etc/ethercat.conf
sudo nano /etc/ethercat.conf
# 3. 配置内容
MASTER0_DEVICE="c4:83:4f:27:30:5b"
DEVICE_MODULES="dwmac-rk"
LINK_DEVICES="eth0"
# 4. 重新加载 systemd 配置
sudo systemctl daemon-reload
# 5. 设置开机自启
sudo systemctl enable ethercat
# 6. 启动服务
sudo systemctl start ethercat
# 7. 查看状态
systemctl status ethercat
判别标志:
-
✅ 存在文件:
/etc/systemd/system/ethercat.service -
✅ 存在文件:
/etc/ethercat.conf -
✅ 使用命令:
systemctl管理
选择 SysV init 启动(旧系统或特殊需求)
适用系统:
-
Ubuntu 14.04 及更早版本
-
Debian 7 及更早版本
-
CentOS 6 及更早版本
-
其他不支持 systemd 的系统
配置步骤:
bash
# 1. 安装 init.d 脚本
sudo cp /usr/local/etc/init.d/ethercat /etc/init.d/
sudo chmod +x /etc/init.d/ethercat
# 2. 创建配置文件 /etc/sysconfig/ethercat
sudo nano /etc/sysconfig/ethercat
# 3. 配置内容
MAIN_DEVICES="c4:83:4f:27:30:5b"
DEVICE_MODULES="dwmac-rk"
LINK_DEVICES="eth0"
# 4. 设置开机自启(根据系统选择)
# CentOS/RedHat:
sudo chkconfig --add ethercat
sudo chkconfig ethercat on
# Debian/Ubuntu:
sudo update-rc.d ethercat defaults
# 5. 启动服务
sudo /etc/init.d/ethercat start
# 6. 查看状态
sudo /etc/init.d/ethercat status
判别标志:
-
✅ 存在文件:
/etc/init.d/ethercat -
✅ 存在文件:
/etc/sysconfig/ethercat -
✅ 使用命令:
/etc/init.d/ethercat管理
1.4 内核模块加载原理
1.4.1 模块参数传递机制
源码位置 : master/module.c
参数定义:
bash
// 最大主站数量
#define MAX_MASTERS 32
// 参数变量定义
static char *main_devices[MAX_MASTERS]; // 主设备 MAC 地址数组
static unsigned int master_count; // 主站数量
static char *backup_devices[MAX_MASTERS]; // 备份设备 MAC 地址数组
static unsigned int backup_count; // 备份设备数量
#ifdef EC_EOE
char *eoe_interfaces[MAX_EOE]; // EOE 接口数组
unsigned int eoe_count; // EOE 接口数量
bool eoe_autocreate = 1; // EOE 自动创建
#endif
static unsigned int debug_level; // 调试级别
unsigned long pcap_size; // Pcap 缓冲区大小
参数传递流程:
bash
用户空间执行:modprobe ec_master main_devices=00:11:22:33:44:55
↓
内核模块加载:ec_init_module()
↓
解析参数:ec_mac_parse() 将 MAC 地址字符串转换为 uint8_t 数组
↓
存储到全局变量:macs[MAX_MASTERS][2][ETH_ALEN]
↓
初始化主站:ec_master_init(&masters[i], ...)
MAC 地址解析函数 (ec_mac_parse):
bash
static int ec_mac_parse(uint8_t *mac, const char *mac_str, int is_backup)
{
// 解析格式:XX:XX:XX:XX:XX:XX
// 转换为 6 字节数组
// 验证格式正确性
// 存储到 macs[i][is_backup ? 1 : 0]
}
1.4.2 设备节点创建过程
源码位置 : master/module.c, master/cdev.c, master/master.c
设备节点创建流程:
第一步:分配设备号 (ec_init_module)
bash
/ 根据 master_count 分配字符设备号范围
if (master_count) {
if (alloc_chrdev_region(&device_number, 0, master_count, "EtherCAT")) {
EC_ERR("Failed to obtain device number(s)!\n");
ret = -EBUSY;
goto out_return;
}
}
第二步:创建设备类 (ec_init_module)
bash
/ 创建 /sys/class/EtherCAT 设备类
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
class = class_create(THIS_MODULE, "EtherCAT");
#else
class = class_create("EtherCAT");
#endif
第三步:初始化主站 (ec_master_init 循环调用)
bash
for (i = 0; i < master_count; i++) {
ret = ec_master_init(&masters[i], i, macs[i][0], macs[i][1],
device_number, class, debug_level);
if (ret)
goto out_free_masters;
}
第四步:创建字符设备和设备节点 (ec_master_init)
bash
// 初始化字符设备
ret = ec_cdev_init(&master->cdev, master,
MKDEV(MAJOR(dev_num), master->index), 1);
// 创建 /dev/EtherCAT0, /dev/EtherCAT1, ...
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
master->class_device = class_device_create(class, NULL,
MKDEV(MAJOR(dev_num), master->index), NULL, "EtherCAT%u", index);
#else
master->class_device = device_create(class, NULL,
MKDEV(MAJOR(dev_num), master->index), NULL, "EtherCAT%u", index);
#endif
字符设备操作 (master/cdev.c):
bash
// 字符设备文件操作结构
static const struct file_operations eccdev_fops = {
.owner = THIS_MODULE,
.open = eccdev_open,
.release = eccdev_release,
.read = eccdev_read,
.write = eccdev_write,
.unlocked_ioctl = eccdev_ioctl,
.mmap = eccdev_mmap,
.fasync = eccdev_fasync,
};
// 字符设备初始化
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
{
cdev->master = master;
cdev_init(&cdev->cdev, &eccdev_fops);
cdev->cdev.owner = THIS_MODULE;
// 添加字符设备到内核
ret = cdev_add(&cdev->cdev, dev_num, 1);
return ret;
}
完整创建流程:
设备号计算:
bash
// 主设备号从 alloc_chrdev_region 获取
// 次设备号 = master index (0, 1, 2...)
dev_t dev_num = MKDEV(MAJOR(device_number), master->index);
// 示例:
// master 0 → /dev/EtherCAT0 (主设备号:250, 次设备号:0)
// master 1 → /dev/EtherCAT1 (主设备号:250, 次设备号:1)
// master 2 → /dev/EtherCAT2 (主设备号:250, 次设备号:2)
1.4.3 网卡驱动替换原理
这是 EtherCAT 能够实现实时通信的关键机制。
常规驱动 vs EtherCAT 驱动 :

驱动替换流程:
卸载原驱动并加载 EtherCAT 驱动
卸载原有的网络驱动模块(如 igb 或 dwmac-rk),替换为 EtherCAT 专用驱动模块(如 ec_igb 或 ec_dwmac-rk)。通过 rmmod 命令移除原驱动,再使用 modprobe 加载 EtherCAT 驱动模块。
初始化硬件配置
加载驱动后,需初始化硬件相关参数,包括 DMA 控制器、中断处理机制和寄存器配置。确保硬件处于兼容 EtherCAT 协议的状态,例如调整时钟同步、PHY 模式等底层设置。
配置 DMA 与缓冲区
为高效数据传输分配 DMA 缓冲区,并配置描述符链表。描述符需明确指向物理内存地址,以支持主站与从站间的实时数据交换。通常需要设置环形缓冲区和双缓冲机制以降低延迟。
注册设备到 EtherCAT 主站
调用 ecdev_offer 函数将驱动注册到 EtherCAT 主站。此步骤会建立主站与从站的通信链路,并传递设备能力信息(如支持的同步模式、缓冲区大小等)。
主站状态切换
主站检测到从站后,状态从 IDLE 过渡到 OPERATION。此时开始周期性的数据交换,驱动需正确处理同步信号(如 DC 同步)和过程数据帧的收发。状态切换可能触发中断,需在驱动中实现相应的状态机处理逻辑。
代码示例(关键片段):
c
// 注册 EtherCAT 设备
ec_device_t *ecdev = ecdev_offer(netdev, ec_poll, THIS_MODULE);
if (!ecdev) {
printk("Failed to offer device\n");
return -ENODEV;
}
// 状态变化回调
static void ec_state_change(void *priv, ec_device_state_t state) {
if (state == EC_DEVICE_OPERATION) {
printk("Enter OPERATION state\n");
}
}
源码分析 (devices/generic.c):
bash
// Generic 驱动设备注册
int __init ec_gen_init_module(void)
{
struct list_head descs;
struct net_device *netdev;
ec_gen_interface_desc_t *desc, *next;
INIT_LIST_HEAD(&generic_devices);
INIT_LIST_HEAD(&descs);
// 遍历所有网络设备
rcu_read_lock();
for_each_netdev_rcu(&init_net, netdev) {
if (netdev->type != ARPHRD_ETHER)
continue;
// 创建接口描述
desc = kmalloc(sizeof(ec_gen_interface_desc_t), GFP_ATOMIC);
strncpy(desc->name, netdev->name, IFNAMSIZ);
desc->netdev = netdev;
desc->ifindex = netdev->ifindex;
memcpy(desc->dev_addr, netdev->dev_addr, ETH_ALEN);
list_add_tail(&desc->list, &descs);
}
rcu_read_unlock();
// 向主站注册设备
list_for_each_entry_safe(desc, next, &descs) {
ret = offer_device(desc); // 关键:注册到 EtherCAT 主站
if (ret)
goto out_err;
kfree(desc);
}
return ret;
}
设备注册到主站 (master/module.c):
bash
// 用户空间请求主站
ec_master_t *ecrt_request_master_err(unsigned int master_index)
{
ec_master_t *master = &masters[master_index];
// 检查主站是否空闲
if (master->phase != EC_IDLE) {
EC_MASTER_ERR(master, "Master still waiting for devices!\n");
return ERR_PTR(-ENODEV);
}
// 获取所有设备的模块引用
for (dev_idx = 0; dev_idx < ec_master_num_devices(master); dev_idx++) {
ec_device_t *device = &master->devices[dev_idx];
if (!try_module_get(device->module)) {
EC_MASTER_ERR(master, "Device module is unloading!\n");
return ERR_PTR(-ENODEV);
}
}
// 进入操作阶段
if (ec_master_enter_operation_phase(master)) {
EC_MASTER_ERR(master, "Failed to enter OPERATION phase!\n");
return ERR_PTR(-EIO);
}
return master; // 成功返回
}
关键要点:
-
驱动替换必要性:
-
原网卡驱动使用标准 Linux 网络栈,延迟不可控
-
EtherCAT 驱动直接操作硬件,实现确定性延迟
-
-
设备注册机制:
-
驱动通过
ecdev_offer()向主站注册 -
主站收集所有配置的设备
-
用户空间调用
ecrt_request_master()完成绑定
-
-
DMA 零拷贝:
-
用户空间内存通过 mmap 映射到内核
-
DMA 直接在用户空间缓冲区读写
-
避免内核态到用户空间的数据拷贝
-
-
实时性保证:
-
旁路网络协议栈
-
优先级继承锁 (rtmutex)
-
硬件时间戳支持
-
1.4.4 完整启动时序图
1.4.4.1 系统启动流程

1.4.4.2 关键函数调用栈


1.4.4.3 主站状态机

1.4.4.4 设备注册时序
未完待续
