系列导读
移植目标
将 Arduino 10BASE-T1S 库的测试验证能力移植到竞品 T1S 芯片环境,实现:
- 在 Arduino 平台上驱动竞品 MAC-PHY 芯片
- 复用 lwIP 协议栈和 UDP 测试框架
- 用相同的 UDP echo 测试方法验证竞品芯片功能
架构抽象边界
移植的核心问题是:哪些模块与 Microchip LAN8651 芯片绑定,哪些是通用的?
graph TB
subgraph "可直接复用 (Chip-Independent)"
A1["Arduino_10BASE_T1S_UDP"]
A2["lwIP TCP/IP Stack"]
A3["T1SPlcaSettings"]
A4["T1SMacSettings"]
A5["MacAddress"]
end
subgraph "条件复用 (取决于竞品是否兼容 OA TC6)"
B1["libtc6 协议核心 (tc6.cpp)"]
B2["Credit 管理 / Chunk 分帧"]
B3["Header/Footer 解析"]
end
subgraph "必须替换 (Chip-Specific)"
C1["TC6_Io (SPI 配置)"]
C2["tc6-regs.cpp (寄存器初始化)"]
C3["芯片 ID 校验逻辑"]
C4["PHY 校准参数"]
C5["PLCA 寄存器地址"]
end
A1 --> A2
A2 --> B1
B1 --> C1
B1 --> C2
场景一:竞品兼容 OA TC6 SPI 协议
如果竞品芯片也实现了 Open Alliance TC6 SPI 接口(DNC/SEQ/DV/SV/EV 帧结构一致),移植工作量最小:
需要修改的文件
| 文件 | 修改内容 | 工作量 |
|---|---|---|
src/microchip/TC6_Io.cpp |
SPI 时钟/模式(如果不同)、引脚定义 | 小 |
src/microchip/lib/libtc6/src/tc6-regs.cpp |
整个寄存器初始化表、芯片 ID 校验、PLCA 寄存器地址 | 大 |
src/Arduino_10BASE_T1S.h |
板卡引脚宏定义 | 小 |
TC6_Io 适配
SPI HAL 层极其简洁,只有 4 个需要确认的参数:
1 | // 需要根据竞品 datasheet 确认这些值 |
复位时序也需要确认:
1 | bool TC6_Io::begin() |
寄存器初始化表替换
这是工作量最大的部分。需要根据竞品 datasheet 重写 TC6_MEMMAP[] 数组:
1 | // 原始 LAN8651 初始化表(32 条) |
需要从竞品 datasheet 中提取的信息:
- CONFIG0 等 SPI 配置寄存器 — chunk size、保护模式设置
- MAC 寄存器 — MAC 地址写入地址、网络控制寄存器
- PHY 寄存器 — 校准参数、电气特性配置
- PLCA 寄存器 — Node ID、Node Count、Burst 参数的地址和位域
- 中断掩码 — 状态上报配置
芯片 ID 校验修改
1 | // 原始校验逻辑(tc6-regs.cpp:346-356) |
PLCA 寄存器地址映射
如果竞品芯片支持 PLCA 但寄存器地址不同,需要修改 HandlePlca():
1 | // LAN8651 PLCA 寄存器地址 |
场景二:竞品使用私有 SPI 协议
如果竞品芯片不兼容 OA TC6(使用自定义寄存器协议或不同的帧封装),需要替换整个协议层:
需要替换的模块
graph TB
subgraph "保留"
A["Arduino_10BASE_T1S_UDP + lwIP"]
end
subgraph "重新实现的接口层"
B["新的 PHY Driver Class"]
end
subgraph "全部替换"
C["竞品 SPI 协议实现"]
D["竞品寄存器驱动"]
E["竞品 chunk/frame 处理"]
end
A --> B
B --> C
B --> D
C --> E
需要实现的接口
Arduino_10BASE_T1S_PHY_Interface 定义了上层需要的抽象接口:
1 | // src/Arduino_10BASE_T1S_PHY_Interface.h |
新的驱动类需要实现这些接口,并完成以下内部工作:
- 初始化 SPI 和竞品芯片
- 集成 lwIP — 注册
netif,提供lwIpOut()回调 - 实现 service() — 驱动 SPI 收发,将接收帧送入 lwIP
- PLCA 管理 — 如果竞品支持 PLCA
lwIP 集成的关键回调
无论竞品用什么 SPI 协议,与 lwIP 的集成模式不变:
1 | // 发送回调:lwIP 要发帧时调用 |
回调接口清单
libtc6 通过回调函数与外部交互。如果复用 libtc6,需要实现这些回调:
| 回调函数 | 用途 | 实现要点 |
|---|---|---|
TC6_CB_OnSpiTransaction() |
执行 SPI 全双工传输 | 调用 TC6_Io::spiTransaction() |
TC6_CB_OnRxEthernetSlice() |
收到帧片段 | 拼接到 lwIP pbuf |
TC6_CB_OnRxEthernetPacket() |
收到完整帧 | 调用 netif->input() |
TC6_CB_OnNeedService() |
TC6 内部需要服务 | 设置 tc6NeedService 标志 |
TC6_CB_OnError() |
错误上报 | 日志输出或重新初始化 |
TC6_CB_OnExtendedStatus() |
扩展状态(中断事件) | 处理 TX/RX timestamp、错误标志 |
如果不复用 libtc6(场景二),这些回调不需要实现,但等效功能需要在新驱动中完成。
移植步骤 Checklist
Phase 1: 环境准备
- 获取竞品 T1S 芯片 datasheet
- 确认竞品是否兼容 OA TC6 SPI 协议
- 准备硬件:竞品评估板 + Arduino 开发板 + SPI 连接
- Fork Arduino_10BASE_T1S 仓库,创建移植分支
Phase 2: SPI 通信验证
- 确认 SPI 物理连接(MOSI/MISO/CLK/CS/IRQ/RESET 引脚)
- 修改
TC6_Io.cpp中的 SPI 时钟/模式/时序参数 - 修改
Arduino_10BASE_T1S.h中的引脚定义 - 验证基本 SPI 通信:读取芯片 ID 寄存器
Phase 3: 寄存器初始化
- 根据 datasheet 编写新的
TC6_MEMMAP[]初始化表 - 实现芯片 ID 校验逻辑
- 配置 MAC 地址寄存器
- 配置 PHY 基础参数
- 验证:寄存器回读确认写入正确
Phase 4: PLCA 配置
- 确认竞品 PLCA 寄存器地址和位域
- 修改
HandlePlca()函数 - 测试 Node 0(协调器)模式
- 测试 Node 1+ 模式
- 验证:PLCA status 寄存器读取正确
Phase 5: 数据通路
- 如果兼容 OA TC6:验证 chunk 发送/接收
- 如果私有协议:实现新的帧收发驱动
- 集成 lwIP netif
- 验证:ping 测试(ICMP echo)
Phase 6: UDP 功能验证
- 运行 UDP_Server example(竞品芯片侧)
- 从 EVB-LAN8670-USB 或另一个节点发 UDP 包
- 验证双向通信
- 验证 PLCA 多节点场景(2+ 节点)
Phase 7: 性能与稳定性
- 长时间运行测试(24h+)
- 吞吐量测量
- 异常恢复:拔线重插、节点加入/退出
- 温度/电压边界条件(如果需要)
验证方案
测试拓扑
graph LR
A["Arduino + 竞品 T1S\n(Node 1, UDP Client)"] -->|"单对双绞线"| B["Multi-drop Bus"]
C["Arduino + LAN8651\n(Node 0, UDP Server)"] -->|"单对双绞线"| B
D["EVB-LAN8670-USB\n(Linux, tcpdump)"] -->|"单对双绞线"| B
验证项
| 测试 | 方法 | 通过标准 |
|---|---|---|
| SPI 通信 | 读芯片 ID 寄存器 | 返回正确的 OUI/Model |
| 链路建立 | PLCA status 读取 | bit[15] = 1 (PLCA active) |
| IP 连通性 | ping from Linux host | ICMP reply 正常 |
| UDP 收发 | UDP echo test | 双向数据正确、无丢包 |
| 多节点 | 3 节点同时通信 | 所有节点均能收发 |
| 稳定性 | 持续发包 1000+ 次 | 无累积错误、无 PLCA 回退 |
tcpdump 抓包验证
1 | # Linux EVB 侧 |
期望输出:
1 | 192.168.42.101.8889 > 192.168.42.100.8888: UDP, length 48 |
常见移植陷阱
- SPI 时序差异 — 竞品可能要求 CS 到第一个 CLK 有最小间隔,Arduino SPI 库不保证这一点。可能需要插入
delayMicroseconds() - 中断极性 — LAN8651 IRQ 是 active-low falling-edge,竞品可能是 level-triggered 或 active-high
- Chunk size 不同 — OA TC6 标准 chunk 是 64 字节,但有些芯片支持 32 字节 chunk,需要修改
TC6_CHUNK_SIZE - MAC 地址字节序 — LAN8651 按特定顺序写入 SPEC_ADD1/ADD2,竞品可能不同
- 初始化时序 — 有些芯片需要等待 PLL lock 或者 PHY ready 信号,不能立即开始寄存器写入
- lwIP 重入 —
service()调用频率影响网络定时器精度,太慢会导致 ARP timeout
代码结构建议
建议的移植后目录结构:
1 | src/ |
小结
移植策略取决于一个关键判断:竞品是否兼容 OA TC6 SPI 协议。
- 兼容:改寄存器表和引脚配置,工作量约 1-2 周
- 不兼容:需要实现新的帧收发驱动并对接 lwIP,工作量约 3-4 周
无论哪种场景,lwIP 协议栈、UDP 封装层、测试框架(UDP echo + tcpdump)都可以完整复用,这是这个开源库最大的移植价值。