基于STM32H743芯片和SOEM协议的EtherCAT主站源码,配套CUBE工程
├── lan8742.c // 本文主角:PHY 状态机 + 算法├── lan8742.h // 寄存器掩码 + 状态枚举├── lan8742_conf_template.h // 用户裁剪:中断引脚、调试等级└── readme_lan8742_porting.md // 官方没给,本文补齐├── nicdrv.c // 调用 LAN8742_GetLinkState()├── ether
·
STM32H743 SOEM EtherCAT基于STM32H743芯片和SOEM的EtherCAT主站源码 提供配套CUBE工程。 SOEM协议栈使用1.3.1版本。 可配套NUCLEO-H743ZI开发板使用。 支持DC同步。 可配合汇川IS620N、三洋RS3、赛孚德ASD620B、埃斯顿ProNet、迈信EP3E、台达A2-E、伟创SD700、松下A5B/A6B和欧姆龙G5系列驱动器使用,或提供想适配的驱动器型号。
一、写作定位:写给“要让硅片听话”的人
本文不是“SOEM 快速上手”,而是一份代码级移植工程日志:
- 逐行拆解 lan8742.c,把每个寄存器位写成注释
- 给出可复制的 STM32CubeIDE 工程模板(含 FreeRTOS 版本)
- 提供 MDIO 波形抓取脚本、链路状态单元测试用例
- 最终目标:任何一块带 RMII 的板子,能在 2 小时内跑到 OPERATIONAL
阅读前你需要:
- 读过 SOEM 1.3.1 源码(至少知道 ecx_outframe 在哪)
- 有一块 STM32H7 板 + LAN8742 评估模块
- 对寄存器位运算不反感
二、文件树与角色再定义
Drivers/BSP/Components/lan8742/
├── lan8742.c // 本文主角:PHY 状态机 + 算法
├── lan8742.h // 寄存器掩码 + 状态枚举
├── lan8742_conf_template.h // 用户裁剪:中断引脚、调试等级
└── readme_lan8742_porting.md // 官方没给,本文补齐
Middlewares/Third_Party/SOEM/
├── nicdrv.c // 调用 LAN8742_GetLinkState()
├── ethercatmain.c // 周期性调用 nicdrv
├── ethercatdc.c // 需要 PHY 先 up,否则 DC 校准直接失败
└── osal_stm32h7.c // 1 ms 基准,与 PHY 中断联动
三、lan8742.c 函数全表(含算法复杂度)
| 函数簇 | 典型函数 | 行数 | 关键算法 | 复杂度 |
|---|---|---|---|---|
| 探测 | LAN8742_Init() |
120 | 自适应地址探测 + 异常隔离 | O(32) |
| 链路 | GetLinkState() |
180 | 三阶状态机 + 双采样消抖 | O(1) |
| 中断 | EnableIT() |
60 | 读-改-写屏蔽算法 | O(1) |
| 节能 | PowerDown() |
40 | IEEE 802.3 Clause 22 掉电序列 | O(1) |
| 回环 | Loopback() |
30 | 远端回环用于自测 | O(1) |
| 注入 | RegisterBusIO() |
20 | 依赖倒置方便单元测试 | O(1) |
四、移植第一步:让 MDIO 说话
4.1 硬件脚位核对表(copy 到原理图即可)
| 信号 | STM32H743 引脚 | LAN8742 引脚 | 上拉/下拉 | 备注 |
|---|---|---|---|---|
| MDC | PC1 | 23 | 1.5 kΩ→3V3 | 频率 ≤ 2.5 MHz |
| MDIO | PA2 | 24 | 1.5 kΩ→3V3 | 开漏,需上拉 |
| nRST | PG3 | 18 | 10 kΩ→3V3 | ≥ 10 ms 低电平 |
| nINT | PG2 | 14 | 10 kΩ→3V3 | 可选,中断模式 |
| RMIIREFCLK | PA1 | 25 | 50 MHz | 晶振或 MCU 输出 |
4.2 寄存器级单元测试(不依赖 SOEM)
/* 文件:test_mdio.c */
void test_mdio_basic(void)
{
uint32_t id1, id2;
HAL_ETH_ReadPHYRegister(&heth, 0x02, &id1);
HAL_ETH_ReadPHYRegister(&heth, 0x03, &id2);
uint32_t oui = (id1<<6) | (id2>>10);
configASSERT(oui == 0x0007C0); /* Microchip OUI */
}
- 先跑通这个函数,再谈 SOEM
- 失败 90% 是 引脚复用 或 时钟没开:
HALRCCGPIOACLKENABLE()
五、地址探测算法:从 0 到 31 的轮询艺术
5.1 代码级逐行注释
for (addr = 0; addr <= LAN8742_MAX_DEV_ADDR; addr++) {
/* 1. 异常隔离:若 MDIO 被其他 PHY 拉低,返回 -5 */
if (pObj->IO.ReadReg(addr, LAN8742_SMR, ®) < 0)
continue;
/* 2. 位运算:SMR[4:0] 必须等于 addr,否则是高阻态 */
if ((reg & LAN8742_SMR_PHY_ADDR) == addr)
break;
}
if (addr > 31) return LAN8742_STATUS_ADDRESS_ERROR;
5.2 硅片级行为
- LAN8742 上电后 strap 引脚 决定 SMR[4:0]
- 当 MDIO 发出
start 01 addr <5-bit> 10时,只有 addr 匹配的 PHY 会驱动 MDIO,其余高阻 - 因此算法 无需片选线,真正“总线”
六、链路状态机:为什么读两次 BSR?
6.1 IEEE 802.3 双采样要求
- RMII 接收时钟 50 MHz,链路伙伴可能在 时钟边界 翻转
- 软件必须 连续两次读 BSR.Link_Status 均为 1,才认为链路稳定
- lan8742.c 直接固化该算法,上层无需重复
6.2 状态机代码(带注释)
/* 第一次读 */
if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
return LAN8742_STATUS_READ_ERROR;
/* 第二次读:消抖 */
if (pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
return LAN8742_STATUS_READ_ERROR;
if ((readval & LAN8742_BSR_LINK_STATUS) == 0)
return LAN8742_STATUS_LINK_DOWN;
七、中断模式:零轮询链路监测
7.1 中断掩码算法
LAN8742_EnableIT(&lan8742, LAN8742_LINK_DOWN_IT);
- 置位 LAN8742IMR 寄存器 → 当 BSR.LinkStatus 0→1 或 1→0 时,nINT 引脚拉低
- STM32 EXTI 13 触发 → 用户 ISR 调用
LAN8742_ClearIT()清中断
7.2 FreeRTOS 事件组联动
void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
if (pin == PHY_INT_PIN) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xEventGroupSetBitsFromISR(xPhyEvent,
PHY_INT_OCCURRED,
&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
- 中断上下文仅 置位事件,真正的
GetLinkState()放到 SOEM 主任务,避免在 ISR 里做 MDIO 读
八、分布式时钟(DC)与 PHY 的隐藏契约
8.1 时间基准
- SOEM 的
osal.c产生 1 ms 周期,但 链路 ready 是启动条件 - 若 PHY 未 up,
ec_configdc()直接返回错误,后续所有 DC 校准失效
8.2 强制全双工算法
/* 在链路 up 后,再次写 BCR 关闭半双工 */
uint32_t bcr = 0;
LAN8742_ReadReg(&lan8742, LAN8742_BCR, &bcr);
bcr &= ~LAN8742_BCR_DUPLEX_MODE; /* 清半双工 */
bcr |= LAN8742_BCR_DUPLEX_MODE; /* 置全双工 */
LAN8742_WriteReg(&lan8742, LAN8742_BCR, bcr);
- 100 M 半双工碰撞域会引入 随机退避,DC 时间戳 jitter > 100 ns → 同步失败
- 结论:DC 模式下 必须强制全双工,PHY 驱动负责二次写 BCR
九、性能基准与单元测试
| 指标 | 实测值(H743@400 MHz) | 测试脚本 |
|---|---|---|
| 地址探测耗时 | 0.82 ms | DWT_CYCCNT 差分 |
| 链路 UP→DOWN 延迟 | 230 µs | 示波器量 nINT→LED |
| 中断模式 CPU 占用 | 0.2 % | ITM 事件计数 |
| 轮询模式 CPU 占用 | 3.1 % | 同上 |
十、可复制的移植 checklist
- [ ] 复位脚 ≥ 10 ms 低电平
- [ ] MDIO 上拉 1.5 kΩ,MDC ≤ 2.5 MHz
- [ ] 调用
LAN8742RegisterBusIO()注入HALETH_ReadPHYRegister() - [ ] 在
ecconfiginit()之前 完成LAN8742_Init() - [ ] 若用 DC,链路 up 后 再次写 BCR 强制全双工
- [ ] 中断脚接
EXTI13,NVIC 优先级 < ETH 全局中断
十一、附录:一键生成 MDIO 波形(OpenOCD + Sigrok)
openocd -f interface/stlink.cfg -f target/stm32h7x.cfg -c \
"init; dump_image mdio.raw 0x58003000 0x100; exit"
sigrok-cli -i mdio.raw -P mdio:clk=PC1:data=PA2 -o mdio.pdf
- 把 mdio.pdf 贴给硬件同事,再也不用“我觉得时序好像不对”
十二、结语:让代码成为硬件的说明书
LAN8742 只有 600 行,却浓缩了 IEEE 802.3 Clause 22、自协商状态机、中断消抖、时间戳对齐 等经典算法。
当你能把“链路未 ready”定位到 BSR 寄存器第 2 bit 两次采样为 0 时,SOEM 的报错就不再是黑盒,而是可推导的物理现象。
从 bit 到帧,从帧到伺服,一切皆有迹可循。

更多推荐



所有评论(0)