系列导读
本篇是代码接口系列的最后一篇,覆盖三类小但关键的接口:
- 辅助类:
MacAddress / T1SPlcaSettings / T1SMacSettings
- lwIP 集成层:
lwipopts.h 配置、sys_now() 桥接
- 全系列索引速查
前序文章:
1. MacAddress
文件位置:src/MacAddress.h / src/MacAddress.cpp
1.1 类签名
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MacAddress : public arduino::Printable { public: MacAddress() : MacAddress(nullptr) { } MacAddress(uint8_t const * mac); static MacAddress create_from_uid();
uint8_t * data() { return _data; } uint8_t const * data() const { return _data; }
private: uint8_t _data[MAC_ADDRESS_NUM_BYTES]; virtual size_t printTo(Print & p) const override; };
|
1.2 三种构造方式
MacAddress() — 默认(全 0)
- 内部委托给
MacAddress(nullptr)
memset(_data, 0, 6)
MacAddress(const uint8_t *mac) — 从字节数组
1 2
| uint8_t bytes[6] = {0xA8, 0x61, 0x0A, 0x12, 0x34, 0x56}; MacAddress mac(bytes);
|
- 如果
mac == nullptr,填充 0
- 否则
memcpy(_data, mac, 6)
static MacAddress create_from_uid() — 从 MCU 唯一 ID
src/MacAddress.cpp:39-52
1 2 3 4 5 6
| MacAddress MacAddress::create_from_uid() { uint8_t mac_addr[6] = {0xA8, 0x61, 0x0A, 0, 0, 0}; static uint8_t const MAC_NIC_SPECIFIC_OFFSET = 3; get_unique_chip_id_3(mac_addr + MAC_NIC_SPECIFIC_OFFSET); return MacAddress(mac_addr); }
|
- OUI 固定为
A8:61:0A — Arduino 在 IEEE 注册的组织唯一标识符
- 后 3 字节取自 MCU 唯一 ID(不同平台实现不同)
各平台 UID 实现(get_unique_chip_id_3)
src/MacAddress.cpp:78-102
| 平台宏 |
来源 |
字节序 |
ARDUINO_ARCH_SAMD |
*(volatile uint32_t*)(0x0080A048) — SAMD21 唯一 ID 寄存器 |
小端 |
ARDUINO_MINIMA / ARDUINO_UNOWIFIR4 / ARDUINO_PORTENTA_C33 |
R_BSP_UniqueIdGet() — Renesas FSP BSP API |
由 BSP 决定 |
ARDUINO_GIGA / ARDUINO_PORTENTA_H7_* |
HAL_GetUIDw2() — STM32 HAL |
小端 |
| 其他 |
# warning 编译警告,UID 全 0(多块板子 MAC 冲突) |
|
已知代码问题(MacAddress.cpp:90-98):
1 2 3 4 5 6 7 8 9
| #elif defined(ARDUINO_GIGA) || defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) { uint32_t const stm32_uid = HAL_GetUIDw2(); memcpy(uid, &stm32_uid, 3); } { auto stm32_uid = HAL_GetUIDw2(); memcpy(uid, &stm32_uid, 3); }
|
两个相同的 { ... } 块在 #elif 中都被编译——stm32_uid 写两次,memcpy 调两次,UID 被覆盖(但因为两次内容相同,结果仍然正确)。Bug 但不影响功能。
1.3 序列化:printTo(Print&)
1 2 3 4 5 6 7 8
| size_t MacAddress::printTo(Print & p) const { char msg[32] = {0}; uint8_t const * const ptr_mac = (this->data()); snprintf(msg, sizeof(msg), "MAC\t%02X:%02X:%02X:%02X:%02X:%02X", ptr_mac[0], ptr_mac[1], ptr_mac[2], ptr_mac[3], ptr_mac[4], ptr_mac[5]); return p.write(msg); }
|
| 项 |
说明 |
| 输出格式 |
MAC\tAA:BB:CC:DD:EE:FF |
| 大写十六进制 |
%02X |
| 分隔符 |
: |
| 前缀 |
MAC\t(含 tab) |
| 用途 |
Serial.println(mac_addr) 直接打印 |
1.4 与 TC6_Arduino_10BASE_T1S::begin() 的对接
1 2
| memcpy(_lw.ip.mac, mac_addr.data(), sizeof(_lw.ip.mac));
|
data() 返回内部 _data[6] 指针,6 字节直接拷贝。
随后传递给:
1 2 3
| regVal = ((uint32_t)pReg->mac[3] << 24) | ((uint32_t)pReg->mac[2] << 16) | ... TC6_WriteRegister(pReg->pTC6, 0x00010024 , regVal, ...);
|
字节序转换在 tc6-regs 内部完成,调用方不需要关心。
1.5 完整用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| MacAddress const mac = MacAddress::create_from_uid();
uint8_t bytes[6] = {0xA8, 0x61, 0x0A, 0x12, 0x34, 0x56}; MacAddress const mac(bytes);
MacAddress const mac;
Serial.println(mac);
t1s_phy.begin(ip, mask, gw, mac, plca, mac_settings);
|
2. T1SPlcaSettings
文件位置:src/T1SPlcaSettings.h / src/T1SPlcaSettings.cpp
2.1 类签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class T1SPlcaSettings : public arduino::Printable { private: uint8_t _node_id; uint8_t _node_count; uint8_t _burst_count; uint8_t _burst_timer; public: static uint8_t const DEFAULT_NODE_ID = 0; static uint8_t const DEFAULT_NODE_COUNT = 8; static uint8_t const DEFAULT_BURST_COUNT = 0; static uint8_t const DEFAULT_BURST_TIMER = 128;
T1SPlcaSettings(); T1SPlcaSettings(uint8_t const node_id); T1SPlcaSettings(uint8_t const node_id, uint8_t const node_count, uint8_t const burst_count, uint8_t const burst_timer);
virtual size_t printTo(Print & p) const override;
uint8_t nodeId() const { return _node_id; } uint8_t nodeCount() const { return _node_count; } uint8_t burstCount() const { return _burst_count; } uint8_t burstTimer() const { return _burst_timer; } };
|
2.2 四个字段语义
| 字段 |
范围 |
含义 |
OA TC6 寄存器 |
_node_id |
0..255 |
本节点 PLCA ID
0 = Coordinator(必须唯一一个)
1..N = Follower |
PLCA_CONTROL_1[7:0] |
_node_count |
1..255 |
总节点数上限 Coordinator 知道的最大 ID + 1 |
PLCA_CONTROL_1[15:8] |
_burst_count |
0..N |
单次 PLCA 周期内允许连续发送的最大 chunk 数 |
PLCA_BURST_MODE[15:8] |
_burst_timer |
0..255 |
突发模式计时器(单位由 chip 决定) |
PLCA_BURST_MODE[7:0] |
默认值与典型配置
| 角色 |
_node_id |
_node_count |
_burst_count |
_burst_timer |
| Coordinator |
0 |
8 (MAX_NODES) |
0 |
128 |
| Follower 1 |
1 |
8 |
0 |
128 |
| Follower 7 |
7 |
8 |
0 |
128 |
Burst 模式何时启用
_burst_count > 0:允许该节点在 PLCA turn 中连续发 burst_count 个 chunk(提升吞吐)
_burst_count = 0(默认):标准 PLCA,每 turn 一个 chunk
_burst_timer:两次 burst 之间的间隔(防止独占总线)
2.3 构造函数重载
1 2 3
| T1SPlcaSettings(); T1SPlcaSettings(uint8_t node_id); T1SPlcaSettings(node_id, node_count, burst_count, burst_timer);
|
注意:默认构造 T1SPlcaSettings() 等价于 T1SPlcaSettings(0) = Coordinator。这是反直觉的——默认是 Coordinator 而不是普通 Follower。代码中典型用法:
1 2
| static T1SPlcaSettings const t1s_plca_settings{T1S_PLCA_NODE_ID};
|
2.4 序列化输出
1 2 3 4 5 6 7 8 9 10 11 12 13
| size_t T1SPlcaSettings::printTo(Print & p) const { char msg[128] = {0}; snprintf(msg, sizeof(msg), "PLCA\n" "\tnode id : %d%s\n" "\tnode count : %d\n" "\tburst count : %d\n" "\tburst timer : %d", _node_id, (_node_id == 0) ? " (PLCA Coordinator)" : "", _node_count, _burst_count, _burst_timer); return p.write(msg); }
|
输出示例(Node 1):
1 2 3 4 5
| PLCA node id : 1 node count : 8 burst count : 0 burst timer : 128
|
Node 0 会带 (PLCA Coordinator) 后缀。
2.5 运行时修改
T1SPlcaSettings 对象一旦传入 begin() 就被 TC6_Arduino_10BASE_T1S 拷贝保存到 _t1s_plca_settings 成员。修改原对象不会影响 PHY 行为。
要运行时修改 PLCA,必须调用:
或通过 TC6Regs_SetPlca()(间接,被 enablePlca() 内部调用):
1 2 3 4 5 6
| bool TC6_Arduino_10BASE_T1S::enablePlca() { return TC6Regs_SetPlca(_lw.tc.tc6, true, _t1s_plca_settings.nodeId(), _t1s_plca_settings.nodeCount()); }
|
注意:enablePlca() 只更新 node_id/count,不更新 burst_count/burst_timer(那些只在 init 时写入)。
2.6 完整用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| T1SPlcaSettings coordinator_plca;
T1SPlcaSettings follower_plca(MY_NODE_NUMBER);
T1SPlcaSettings custom_plca(2, 8, 3, 64);
Serial.println(follower_plca);
t1s_phy.begin(ip, mask, gw, mac, follower_plca, mac_settings);
|
3. T1SMacSettings
文件位置:src/T1SMacSettings.h / src/T1SMacSettings.cpp
3.1 类签名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class T1SMacSettings : public arduino::Printable { private: bool const _mac_promiscuous_mode; bool const _mac_tx_cut_through; bool const _mac_rx_cut_through; public: static bool const DEFAULT_MAC_PROMISCUOUS_MODE = false; static bool const DEFAULT_MAC_TX_CUT_THROUGH = false; static bool const DEFAULT_MAC_RX_CUT_THROUGH = false;
T1SMacSettings(); T1SMacSettings(bool const mac_promiscuous_mode, bool const mac_tx_cut_through, bool const mac_rx_cut_through);
virtual size_t printTo(Print & p) const override;
bool isMacPromiscuousModeEnabled() const { return _mac_promiscuous_mode; } bool isMacTxCutThroughEnabled() const { return _mac_tx_cut_through; } bool isMacRxCutThroughEnabled() const { return _mac_rx_cut_through; } };
|
3.2 三个字段语义
| 字段 |
默认 |
含义 |
LAN8651 寄存器 |
_mac_promiscuous_mode |
false |
混杂模式:接受所有帧(包括非本机 MAC 的) |
NETWORK_CONFIG bit[4] = 0x10 |
_mac_tx_cut_through |
false |
TX 切通:帧未完全进入 MAC FIFO 就开始发送(降低延迟,需要 PHY 支持) |
CONFIG0 bit[9] = 0x200 |
_mac_rx_cut_through |
false |
RX 切通:帧未完全接收就开始向上传递(降低延迟,增加 CRC 错误风险) |
CONFIG0 bit[8] = 0x100 |
const 设计:所有字段都是 bool const——对象一旦构造就不可修改。要运行时切换模式必须重新构造对象 + 重新 begin()。
3.3 寄存器写入逻辑
tc6-regs.cpp:386-407
1 2 3 4 5 6 7 8 9
| regVal = pReg->promiscuous ? 0x10 : 0x0; TC6_WriteRegister(pReg->pTC6, 0x00010001 , regVal, true);
regVal = 0x9026; if (pReg->txCutThrough) regVal |= 0x200u; if (pReg->rxCutThrough) regVal |= 0x100u; TC6_WriteRegister(pReg->pTC6, 0x00000004 , regVal, true);
|
注意 0x9026 是 LAN8651 的基础 CONFIG0 值(不是 0),含:
- SPI chunk 大小
- 其他 LAN8651 特定设置
移植时:基础值 0x9026 需要替换为竞品的 CONFIG0 默认值。
3.4 序列化输出
1 2 3 4 5 6 7 8 9 10
| size_t T1SMacSettings::printTo(Print & p) const { char msg[128] = {0}; snprintf(msg, sizeof(msg), "MAC\n" "\tpromisc. mode : %d\n" "\ttx cut through: %d\n" "\trx cut through: %d", _mac_promiscuous_mode, _mac_tx_cut_through, _mac_rx_cut_through); return p.write(msg); }
|
输出:
1 2 3 4
| MAC promisc. mode : 0 tx cut through: 0 rx cut through: 0
|
3.5 完整用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| T1SMacSettings mac_settings;
T1SMacSettings low_latency_mac(false, true, true);
T1SMacSettings sniffer_mac(true, false, false);
Serial.println(low_latency_mac);
t1s_phy.begin(ip, mask, gw, mac, plca_settings, low_latency_mac);
|
4. lwipopts.h 详解
文件位置:src/lib/liblwip/cfg/lwipopts.h
4.1 平台/内核选项
| 宏 |
值 |
含义 |
NO_SYS |
1 |
裸机模式——lwIP 不启动内部线程,由用户主循环驱动 |
SYS_LIGHTWEIGHT_PROT |
0 |
不需要并发保护(单线程 + 中断) |
MEM_ALIGNMENT |
4 |
内存对齐(ARM Cortex-M 自然对齐) |
MEM_SIZE |
0 |
私有 heap 0(MEM_LIBC_MALLOC=1 时忽略) |
MEM_LIBC_MALLOC |
1 |
使用 libc malloc/free |
4.2 模块开关(核心)
| 宏 |
值 |
备注 |
LWIP_ARP |
1 |
ARP 必需 |
LWIP_IPV4 |
1 |
仅 IPv4 |
LWIP_IPV6 |
0 |
禁用 |
LWIP_ICMP |
1 |
让 ping 工作 |
LWIP_RAW |
1 |
原始 PCB(库内部未使用,保留默认) |
LWIP_UDP |
1 |
必需——本库只支持 UDP |
LWIP_TCP |
0 |
禁用 TCP(减小 RAM) |
LWIP_DHCP |
0 |
静态 IP(可手动改 1 + 配 DHCP 调用) |
LWIP_AUTOIP |
0 |
禁用 AutoIP |
LWIP_IGMP |
0 |
禁用多播 |
LWIP_DNS |
0 |
解释 beginPacket(host, port) 返回 0 |
LWIP_SNMP |
0 |
禁用 SNMP |
LWIP_NETCONN |
0 |
禁用 Netconn API |
LWIP_SOCKET |
0 |
禁用 BSD Socket API |
LWIP_HAVE_LOOPIF |
0 |
无 loopback |
4.3 内存池大小
| 宏 |
值 |
单池 RAM |
说明 |
MEMP_NUM_PBUF |
20 |
~640B |
通用 pbuf 池 |
MEMP_NUM_UDP_PCB |
4 |
~80B |
UDP 控制块(4 个并发 socket) |
MEMP_NUM_TCP_PCB |
0 |
0 |
TCP 禁用 |
MEMP_NUM_TCP_PCB_LISTEN |
0 |
0 |
TCP 禁用 |
MEMP_NUM_TCP_SEG |
0 |
0 |
TCP 禁用 |
MEMP_NUM_ARP_QUEUE |
6 |
~144B |
ARP 请求排队 |
MEMP_NUM_RAW_PCB |
4 |
~80B |
原始 PCB |
MEMP_NUM_NETBUF/NETCONN |
0 |
0 |
Netconn API 禁用 |
PBUF_POOL_SIZE |
1 |
~1520B |
PBUF_POOL 池(1 个 1500 字节) |
4.4 IP 选项
| 宏 |
值 |
备注 |
IP_FORWARD |
0 |
单网口不转发 |
IP_OPTIONS_ALLOWED |
1 |
允许但不解 IP options |
IP_REASSEMBLY |
0 |
禁用 IP 分片重组(10Mbps 通常不分片) |
IP_FRAG |
0 |
禁用 IP 分片发送(MTU 1500 通常不需要) |
IP_REASS_MAXAGE |
3 |
重组超时 |
IP_DEFAULT_TTL |
255 |
标准 TTL |
ICMP_TTL |
IP_DEFAULT_TTL |
|
4.5 UDP 选项
| 宏 |
值 |
备注 |
LWIP_UDP |
1 |
|
LWIP_UDPLITE |
0 |
禁用 UDP-Lite |
UDP_TTL |
IP_DEFAULT_TTL |
|
4.6 缓冲区 / MTU
| 宏 |
值 |
备注 |
PBUF_LINK_HLEN |
14 |
标准以太网头 |
ETHERNET_MTU |
1500 |
注意:TC6_Arduino_10BASE_T1S::lwIpInit 把 netif->mtu = 1536(多 36 字节给 VLAN 等) |
4.7 校验和
| 宏 |
值 |
含义 |
CHECKSUM_CHECK_IP |
0 |
不校验 IP 头——LAN8651 PHY 已做 |
CHECKSUM_CHECK_UDP |
0 |
不校验 UDP——同上 |
CHECKSUM_CHECK_TCP |
0 |
TCP 禁用,但即使启用也不校验 |
注意:lwIP 在发送时仍会生成校验和(由 MAC 硬件或软件添加),仅是 RX 时不验证。pbuf_take() 后 lwIP 内部用 inet_chksum 计算。
4.8 调试
1 2 3 4
| #define LWIP_DEBUG 0
extern unsigned char debug_flags; #define LWIP_DBG_TYPES_ON debug_flags
|
debug_flags 是用户自定义的全局变量,可以运行时切换调试级别(默认未定义)。
4.9 符号重命名(避免冲突)
1 2 3
| #define sys_now t1s_sys_now #define etharp_output t1s_etharp_output
|
为什么需要:
- Arduino 核心库有自己的
sys_now 等
- 不同模块 include lwIP 时可能出现符号冲突
- 重命名保证这个 lwIP 实例的符号与其他模块共存
src/lib/lwip_sys_now.cpp 实现的 sys_now 因为宏重定义,实际编译为 t1s_sys_now。
4.10 调优建议
减小 RAM(UNO R4 紧张时)
1 2 3 4
| #define MEMP_NUM_PBUF 10 #define PBUF_POOL_SIZE 1 #define MEMP_NUM_ARP_QUEUE 3 #define MEMP_NUM_UDP_PCB 2
|
启用 TCP(如需要)
1 2 3 4 5 6 7
| #define LWIP_TCP 1 #define MEMP_NUM_TCP_PCB 4 #define MEMP_NUM_TCP_PCB_LISTEN 1 #define MEMP_NUM_TCP_SEG 8 #define TCP_MSS (1500 - 40) #define TCP_WND (2 * TCP_MSS) #define TCP_SND_BUF (2 * TCP_MSS)
|
注意:在 UNO R4 Minima(32KB RAM)上启用 TCP 会立即超出 RAM。
启用 DHCP
然后在 setup() 中:
1 2
| dhcp_start(&t1s_phy.getNetif());
|
当前库没有 expose netif——启用 DHCP 需要修改 TC6_Arduino_10BASE_T1S 添加 getter。
5. sys_now() 时基桥接
文件位置:src/lib/lwip_sys_now.cpp
1 2 3 4 5 6 7
| #include <Arduino.h> #include "liblwip/include/lwip/opt.h" #include "liblwip/arch/cc.h"
extern "C" u32_t sys_now(void) { return millis(); }
|
5.1 实际编译符号
因为 lwipopts.h 定义了 #define sys_now t1s_sys_now,实际编译时 sys_now 标识符被替换为 t1s_sys_now。所以链接器看到的是:
1
| extern "C" u32_t t1s_sys_now(void) { return millis(); }
|
lwIP 内部调用 sys_now() 时,预处理器先替换为 t1s_sys_now()。
5.2 调用频率
sys_check_timeouts() 在 TC6_Arduino_10BASE_T1S::service() 中被调用,依赖主循环频率。sys_now() 的精度直接决定 lwIP 内部定时器精度:
- ARP 表老化(默认 60s 更新、20min 失效):毫秒级足够
- DHCP 租约更新:秒级
- TCP 重传:毫秒级(TCP 禁用,无关)
如果主循环很慢(如 100ms 一次 service()),lwIP 计时会偏移——但本库没有 TCP,偏移影响有限。
6. 全系列速查:API 索引
6.1 应用层
| 类型 |
头文件 |
主要 API |
Arduino_10BASE_T1S_PHY_TC6(宏) |
Arduino_10BASE_T1S.h |
实例化 PHY + HAL |
Arduino_10BASE_T1S_PHY_Interface(抽象) |
Arduino_10BASE_T1S_PHY_Interface.h |
begin() / service() |
Arduino_10BASE_T1S_UDP |
Arduino_10BASE_T1S_UDP.h |
begin/beginPacket/write/endPacket/parsePacket/read/peek/flush/remoteIP/remotePort/bufferSize/stop |
MacAddress |
MacAddress.h |
构造 / data() / create_from_uid() / printTo() |
T1SPlcaSettings |
T1SPlcaSettings.h |
构造 / nodeId/nodeCount/burstCount/burstTimer() / printTo() |
T1SMacSettings |
T1SMacSettings.h |
构造 / isMac*Enabled() / printTo() |
6.2 PHY 实现层
| 类型 |
头文件 |
主要 API |
TC6::TC6_Arduino_10BASE_T1S |
microchip/TC6_Arduino_10BASE_T1S.h |
begin() / service() / getPlcaStatus() / enablePlca() / sendWouldBlock() / digitalWrite() |
TC6::TC6_Io |
microchip/TC6_Io.h |
begin() / onInterrupt() / isInterruptActive() / releaseInterrupt() / spiTransaction() |
TC6::DIO |
microchip/TC6_Arduino_10BASE_T1S.h |
枚举 A0 / A1 |
6.3 libtc6 协议层(C API)
| 函数 |
头文件 |
用途 |
TC6_Init / TC6_Destroy / TC6_Reset |
tc6.h |
生命周期 |
TC6_Service |
tc6.h |
主循环驱动 |
TC6_EnableData |
tc6.h |
数据开关 |
TC6_SendRawEthernetPacket / TC6_SendRawEthernetSegments |
tc6.h |
发送 |
TC6_GetRawSegments |
tc6.h |
获取发送 segment |
TC6_ReadRegister / TC6_WriteRegister / TC6_ReadModifyWriteRegister / TC6_MultipleRegisterAccess |
tc6.h |
寄存器 |
TC6_GetState / TC6_GetInstance |
tc6.h |
状态 |
TC6_SpiBufferDone |
tc6.h |
SPI 异步完成 |
TC6_UnlockExtendedStatus |
tc6.h |
扩展状态解锁 |
6.4 libtc6 必实现回调
| 函数 |
用途 |
TC6_CB_OnSpiTransaction |
平台 SPI |
TC6_CB_OnRxEthernetSlice |
RX 切片累积 |
TC6_CB_OnRxEthernetPacket |
RX 帧完成 |
TC6_CB_OnNeedService |
通知主循环 |
TC6_CB_OnError |
错误处理 |
TC6_CB_OnExtendedStatus |
扩展状态(由 tc6-regs 实现) |
6.5 tc6-regs 寄存器层
| 函数 |
用途 |
TC6Regs_Init / TC6Regs_GetInitDone / TC6Regs_Reinit |
初始化 |
TC6Regs_SetPlca |
运行时 PLCA 切换 |
TC6Regs_GetChipRevision |
读 chip revision |
TC6Regs_EnableDio_A0/A1 / TC6Regs_ToggleDio_A0/A1 |
GPIO 控制 |
TC6Regs_CheckTimers |
超时检查(必须周期调用) |
TC6Regs_CB_GetTicksMs |
必实现回调 |
TC6Regs_CB_OnEvent |
必实现回调 |
TC6_CB_OnExtendedStatus |
必实现回调 |
6.6 错误码全集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| typedef enum { TC6Error_Succeeded, TC6Error_NoHardware, TC6Error_UnexpectedSv, TC6Error_UnexpectedDvEv, TC6Error_BadChecksum, TC6Error_UnexpectedCtrl, TC6Error_BadTxData, TC6Error_SyncLost, TC6Error_SpiError, TC6Error_ControlTxFail, } TC6_Error_t;
typedef enum { TC6Regs_Event_UnknownError, TC6Regs_Event_Transmit_Protocol_Error, TC6Regs_Event_Transmit_Buffer_Overflow_Error, TC6Regs_Event_Transmit_Buffer_Underflow_Error, TC6Regs_Event_Receive_Buffer_Overflow_Error, TC6Regs_Event_Loss_of_Framing_Error, TC6Regs_Event_Header_Error, TC6Regs_Event_Reset_Complete, TC6Regs_Event_PHY_Interrupt, TC6Regs_Event_Transmit_Timestamp_Capture_Available_A/B/C, TC6Regs_Event_Transmit_Frame_Check_Sequence_Error, TC6Regs_Event_Control_Data_Protection_Error, TC6Regs_Event_RX_Non_Recoverable_Error, TC6Regs_Event_TX_Non_Recoverable_Error, TC6Regs_Event_FSM_State_Error, TC6Regs_Event_SRAM_ECC_Error, TC6Regs_Event_Undervoltage, TC6Regs_Event_Internal_Bus_Error, TC6Regs_Event_TX_Timestamp_Capture_Overflow_A/B/C, TC6Regs_Event_TX_Timestamp_Capture_Missed_A/B/C, TC6Regs_Event_MCLK_GEN_Status, TC6Regs_Event_gPTP_PA_TS_EG_Status, TC6Regs_Event_Extended_Block_Status, TC6Regs_Event_SPI_Err_Int, TC6Regs_Event_MAC_BMGR_Int, TC6Regs_Event_MAC_Int, TC6Regs_Event_HMX_Int, TC6Regs_Event_GINT_Mask, TC6Regs_Event_Chip_Error, TC6Regs_Event_Unsupported_Hardware, } TC6Regs_Event_t;
|
7. 配置矩阵速查
7.1 典型应用配置
| 场景 |
T1SPlcaSettings |
T1SMacSettings |
| Coordinator(默认) |
{0} |
默认 |
| Follower(普通) |
{MY_NODE_ID} |
默认 |
| 低延迟通信 |
{MY_NODE_ID, 8, 1, 64} |
(false, true, true) |
| 网络嗅探 |
任意 |
(true, false, false) |
| 高吞吐 Burst |
{MY_NODE_ID, 8, 5, 32} |
默认 |
7.2 板卡引脚
| 板卡 |
CS |
RESET |
IRQ |
SPI |
| Arduino Zero / UNO R4 Minima / WiFi |
9 |
6 |
2 |
SPI |
| Portenta H7 (MID carrier) |
PH_6 |
PH_15 |
PC_7 |
SPI |
| Portenta C33 (MID carrier) |
25 |
6 |
2 |
SPI1 |
| GIGA |
9 |
6 |
2 |
SPI1 |
8. 调试速查清单
| 现象 |
排查方向 |
begin() 失败 |
1) SPI 连线 2) 芯片 ID 寄存器读 3) 中断脚配置 |
service() 看不到 RX |
1) IRQ 是否触发 2) tc6NeedService 是否置位 3) lwIP PBUF_POOL_SIZE |
| UDP 收不到包 |
1) FilterRxEthernetPacket 是否过滤 2) pbuf_take 失败 3) pbuf->next 分段丢失 |
| UDP 发不出去 |
1) ARP 未解析(ping 一下) 2) TX segment 队列满 3) _t1s_plca_settings 配置 |
| PLCA 频繁回退 CSMA/CD |
1) Coordinator 是否在线 2) 线缆质量 3) Node ID 冲突 |
9. 跨系列引用图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 应用 Sketch (.ino) │ ├─→ Arduino_10BASE_T1S_UDP [文档 1:UDP Socket API] │ │ │ └─→ lwIP UDP/ARP/IP │ │ │ └─→ netif.linkoutput = TC6_Arduino_10BASE_T1S::lwIpOut │ │ │ ▼ │ libtc6 (TC6_t instance) [文档 3:libtc6 C API] │ │ │ ├─→ TC6_SendRawEthernetSegments │ ├─→ TC6_ReadRegister / WriteRegister │ └─→ TC6_CB_OnSpiTransaction ──→ TC6_Io [文档 2:HAL] │ └─→ TC6_Arduino_10BASE_T1S [文档 2:PHY Interface] │ ├─→ TC6Regs_Init / Reinit / SetPlca ├─→ TC6_Io(SPI + 中断) └─→ MacAddress / T1SPlcaSettings / T1SMacSettings [本篇]
|
10. 小结
辅助类(MacAddress / T1SPlcaSettings / T1SMacSettings)虽然小,但承载了几个关键设计决策:
MacAddress::create_from_uid() 利用 MCU 唯一 ID 自动生成 MAC,省去产线烧录
T1SPlcaSettings 默认 node_id=0(Coordinator),与典型使用(Follower)相反——需要在 sketch 显式指定
T1SMacSettings 字段 const,无法运行时切换模式
lwIP 配置(lwipopts.h)和 sys_now 桥接是连接”PC 上的 lwIP 概念”与”嵌入式平台”的最后一公里。本库的设计是最小可用 lwIP——禁用 TCP、DHCP、DNS、IGMP,仅保留 IPv4 + ICMP + ARP + UDP,配合 32KB RAM 的 UNO R4。
至此,本系列四篇结束。读者现在应能:
- 调用任一公开 API 并理解其语义
- 排查接口调用失败的常见原因
- 在保持应用层不变的前提下替换 HAL 或 PHY 实现
- 移植整个栈到非 Arduino 平台或竞品 MAC-PHY