今年做一款工业数据采集网关,老板给的硬指标是“以太网PHY必须用国产”。一开始我还想着沿用成熟方案LAN8720A,结果采购直接回话:缺货,交期三周起步。翻了一圈选型手册,SR8201F进入视野。这颗国产PHY芯片从管脚定义到寄存器映射都对标RTL8201F,兼容性做得相当到位,而且价格友好、现货充足。于是从硬件改版到LWIP协议栈跑通,前后折腾了近两周,中间踩了不少坑。这篇文章我就以SR8201F为主线,把硬件设计、驱动编写、LWIP移植和调试排障的完整流程一次讲透,给准备在这颗PHY上做以太网产品开发的朋友当个参考。
1. 项目概述与方案选型
1.1 这颗芯片能干什么,适合什么场景
SR8201F是一颗10/100M自适应以太网物理层收发芯片,也就是常说的PHY。它负责把MAC发出的数字信号调制成模拟信号送上网线,同时把网线上的模拟信号解调成MAC能识别的数字信号。对应到以太网OSI模型里,PHY属于物理层,再往上才是数据链路层的MAC。
这颗芯片最典型的应用场景就是嵌入式设备接入以太网。比如工业数据采集网关、DTU、串口服务器、电力集中器、边缘计算盒子、各种带网口的开发板,凡是需要联网的MCU或MPU产品,基本都能看到PHY芯片的身影。SR8201F提供标准的RMII和MII两种MAC接口,既能对接STM32、GD32这类内置MAC的MCU,也能通过外部MAC芯片扩展网络功能。我这次的设计用的是STM32H723内置的MAC,通过RMII接口连接SR8201F,跑FreeRTOS + LWIP协议栈,整体方案成本低、占用PCB面积小,非常适合中小型联网设备。
相比颗颗很多进口PHY,SR8201F最大的优势是供应稳定。当前形势下进口物料交期不可控,国产PHY在供货、价格和原厂技术支持上都有明显优势。实际用下来,这颗芯片的电气性能并不差,在温度范围、ESD能力等指标上甚至比某些进口料更舍得堆料。对于产品经理天天逼着“降本增效”的项目,SR8201F是一个值得考虑的正向选项。
1.2 为什么选它:兼容性、成本与供货
很多工程师一听到“国产替代”就头疼,担心原厂文档不全、寄存器定义不兼容、时序对不上。SR8201F在这个问题上处理得很聪明——它直接对标Realtek RTL8201F的设计,管脚排列、寄存器地址、控制逻辑几乎一样。也就是说,原来为RTL8201F写的驱动,改改PHY地址、微调一下初始化时序就能跑在SR8201F上。对于从台湾或者海外方案切换过来的产品,硬件改动成本能压到最低。
成本方面,SR8201F散片价格在同类PHY里属于较低档位,大批量采购还能谈。不过更关键的是“现货”两个字。我以前吃过亏,一款产品设计用的PHY停产,被迫换料,重新画板、重新测试、重新过认证,前前后后多花了一个月。这次选SR8201F之前,我特意找原厂确认了产能和长期供货计划,确认没问题才正式用于项目。
选型时还有一点务必注意:SR8201F的PHY地址不是固定的,由外围引脚状态决定,典型值是0x01,但有些开板设计会把地址拉到0x00。调试时最好在代码里做一个PHY地址扫描,从0到31依次尝试读寄存器2和3,能读到有效PHY ID的那个地址就是芯片当前的工作地址。这个扫描逻辑在后续调试中会省很多事,强烈建议直接写进初始化代码里。
2. 硬件电路设计要点
2.1 电源与时钟:先搞好电,再谈通信
PHY芯片是典型的模拟数字混合器件,电源设计直接影响链路质量。SR8201F的供电架构是:数字IO部分用3.3V,内部核心电压由芯片内部LDO产生,一般不需要外部额外提供1.8V或1.2V,这对硬件设计来说省了不少事。
不过省事不等于可以随便画。3.3V电源引脚旁边必须放去耦电容,而且电容量要搭配合理。我的做法是每个电源引脚就近放一个0.1uF高频陶瓷电容,再在芯片附近的电源入口放一个10uF钽电容或大体积陶瓷电容,形成高低频搭配的退耦结构。PCB布局上,电容要尽量靠近PHY的电源引脚,走线要短,否则高频噪声滤不干净,很容易出现丢包、链路断连等莫名其妙的问题。
时钟是另一个关键。RMII接口要求50MHz参考时钟,可以由MAC提供,也可以由板载50MHz晶振或者有源振荡器提供,两者之间选一个即可。但要注意,PHY内部收发器对这个参考时钟的质量比较敏感,如果时钟抖动大、精度差,链路协商和数据传输会出各种诡异故障。实测用ST-Link那种万用板自带的廉价晶振,出现过10Mbps模式下不通、百兆模式下偶发丢包的情况。后来换成温漂在50ppm以内的有源晶振,问题就消失了。
强烈建议:如果系统里没有现成的50MHz时钟源,优先用无源晶体配合PHY内部振荡器,或者选用有源振荡器,不要图省事直接从MCU的PLL里分频,除非你仔细核对过时钟树的精度和抖动。以太网是硬件实时性要求很高的通信,时钟的账必须先算清楚再画板。
2.2 RMII接口信号与引脚分配
RMII是“简化媒体独立接口”,相比MII,它的数据线宽度从8位降到了2位,控制信号也精简了,大大减少了对MCU引脚资源的占用。SR8201F的RMII接口信号包括:TXD[1:0]发送数据、RXD[1:0]接收数据、TX_EN发送使能、CRS_DV载波侦听/数据有效、MDC管理时钟、MDIO管理数据,外加一个REF_CLK参考时钟。总共不到10根线,主流MCU都能轻松接出来。
引脚分配上有一个容易踩的坑:RMII的RXD[1:0]和CRS_DV这组信号在STM32等MCU上是固定映射到某个复用功能上的,不能随意改引脚。以STM32H723为例,RMII功能通常绑定在PA1/PA2/PA7等特定引脚上,如果你把PHY的数据线接到其他引脚,无论软件怎么配AF,信号都上不了MAC。所以原理图阶段就要仔细翻MCU的Datasheet,确认RMII的引脚映射,不要等PCB打完再改,那就是纯纯的返工。
信号线之间不要穿电阻、不要加磁珠、不要串联过长的走线,这些“想当然”的处理反而会破坏信号完整性。RMII这类接口在低速短距离传输场景下,直接点对点连接就是最优解。电路上唯一建议加的是在MDIO线上串联一个1kΩ左右的电阻,用于减少信号振铃,MDC时钟线可以加一个上拉电阻确保默认电平稳定。
2.3 复位电路、PHY地址与LED配置
SR8201F的复位时序比很多PHY要温和,但依然不能忽略。芯片上电后需要等待电源稳定,再释放复位,典型时间是几十毫秒,具体数值以手册为准。实际项目中,我推荐用MCU的GPIO来控制PHY的复位引脚,这样软件可以在初始化前灵活地控制复位时序。比如上电后先拉低复位引脚,等电源稳定后再拉高,然后再延时至少1ms,确保PHY内部完成初始化。
用GPIO复位还有一个好处:调试时如果PHY状态乱了,可以在不重启整个系统的情况下,只复位PHY重新初始化,这比反复按复位键效率高很多。注意复位引脚一般内部有上拉,外部可以不加上拉电阻,但为了防止复位信号被干扰,建议在靠近PHY侧加一个10kΩ下拉到地,确保上电瞬间是确定低电平状态。
PHY地址配置是硬件上容易忽略的细节。SR8201F的PHY地址由引脚高低电平决定,通常出厂默认地址为0x01,但不同封装、不同批次可能有差异。最稳妥的方式还是用MDIO扫描法,在驱动初始化时读PHY ID,确认实际地址是多少。如果硬件已经固定地址,可以在原理图里把PHYAD引脚通过电阻拉高或拉低,保证地址唯一。多片PHY并联在一条MDIO总线上时,地址必须各不相同,这个务必在设计阶段规划清楚。
LED配置相对简单。SR8201F一般有2到3个LED驱动引脚,可以指示Link状态、活动状态和速率。硬件上LED串一个限流电阻(一般1kΩ到2kΩ)到地即可,正极接3.3V。注意LED的驱动能力有限,不能直接驱动大电流LED,如果要做高亮LED,建议用三极管驱动。
2.4 网络变压器与Layout注意事项
不少第一次做以太网硬件的朋友会问:PHY和RJ45之间为什么非要放网络变压器?直接连不行吗?答案是:不行。网络变压器有三个作用:隔离PHY和外部网线的直流电位、抑制共模干扰、匹配线路阻抗。没有变压器,静电和雷击浪涌很容易顺着网线打进PHY,芯片当场报废也不是稀奇事。
选择变压器时,注意选带中心抽头且抽头引出的型号,以便按手册要求接电源或电容到地。SR8201F手册一般会给出推荐电路,有的方案中心抽头接3.3V,有的接0.1uF电容到地,这取决于PHY内部驱动电路的结构。这里不可以照搬其他PHY的接法,一定要按照SR8201F手册推荐的网络来设计,否则信号衰减会非常严重,甚至完全不通。
PCB走线方面,PHY出来到变压器的差分信号线(TD+、TD-、RD+、RD-)要成对等长走线,尽量在完整的地平面上走线,控制差分阻抗在100Ω附近。变压器下面要挖空参考平面,防止寄生电容影响高频特性。RJ45外壳的金属屏蔽层要通过一个高压电容和电阻连接到地,这个“泄放地”和处理器的数字地要分开,最后单点连接,不然共模电流会干扰整个系统。
Layout阶段最容易犯的错是把PHY芯片、变压器、RJ45之间的距离拉得太远。理想布局是PHY靠近变压器、变压器靠近RJ45,三者尽量在一条直线上,间距控制在1到2厘米以内。如果空间受限,也要保证差分线不过孔、不跨分割,否则高频眼图会很难看。
3. PHY驱动与寄存器配置
3.1 读懂PHY核心寄存器
写PHY驱动前,必须先搞懂寄存器。SR8201F兼容RTL8201F的寄存器定义,管理接口是标准的MDIO,通过MDC时钟和MDIO数据线访问32个左右的核心寄存器。真正常用的其实就前几个,我整理了一个速查表,调试时对照着看效率极高。
| 寄存器 | 名称 | 关键位 | 作用 |
|---|---|---|---|
| 0x00 | 控制 | bit15软复位、bit14回环、bit13速率选择、bit12自动协商使能、bit8全双工 | 配置PHY工作模式、触发复位 |
| 0x01 | 状态 | bit5自动协商完成、bit2链路已建立、bit6... | 读取协商结果和链路状态 |
| 0x02 | PHY ID高16位 | bit15:0 | 识别芯片型号 |
| 0x03 | PHY ID低16位 | bit15:0 | 识别芯片版本 |
| 0x04 | 自动协商通告 | bit12:5技术能力 | 配置本端支持的速率和双工模式 |
最常用的是0x00和0x01。0x00寄存器往bit15写1会触发软复位,这个过程需要等待自清,复位完成后才能继续配置其他寄存器。0x01的bit2是链路状态,1表示物理链接已经建立,网线插上了、对端设备也协商成功了。调试时第一步就查这个位,基本能判断问题是出在物理层还是上层协议。
PHY ID寄存器(0x02和0x03)是调试时定位问题的重要线索。如果MDIO能读到这两位的值且值有规律,说明PHY供电和MDIO通信正常;如果读到0xFFFF或者0x0000,大概率是MDIO引脚接错、地址不对或者PHY没工作。SR8201F的PHY ID具体数值以手册为准,我这边读到的ID和RTL8201F非常接近,这也侧面验证了兼容性设计。
3.2 初始化流程与代码实现
PHY初始化的标准流程是:上电复位(或GPIO复位)→ 等待PHY稳定 → 读取PHY ID确认通信正常 → 配置自动协商或强制模式 → 等待协商完成 → 读取状态寄存器确认最终速率和双工模式。下面是一段基于MDIO底层函数的初始化示例,适用于类似STM32 HAL的环境。
uint16_t phy_read(uint8_t phy_addr, uint8_t reg) { uint32_t tmp = 0; tmp |= (0x02 << 28); // 读操作 tmp |= (phy_addr << 23); // PHY地址 tmp |= (reg << 18); // 寄存器地址 // 通过MDC/MDIO时序发送并接收16位数据 // 具体时序由MCU硬件或GPIO模拟实现 return (uint16_t)mdio_receive_data(tmp); } void phy_write(uint8_t phy_addr, uint8_t reg, uint16_t val) { uint32_t tmp = 0; tmp |= (0x01 << 28); // 写操作 tmp |= (phy_addr << 23); tmp |= (reg << 18); tmp |= val; mdio_send_command(tmp); } uint8_t phy_init(uint8_t phy_addr) { uint16_t id_hi, id_lo; uint16_t status = 0; // 1. 确认PHY在总线上 id_hi = phy_read(phy_addr, 2); id_lo = phy_read(phy_addr, 3); if (id_hi == 0xFFFF || id_lo == 0xFFFF) { return 1; // MDIO通信异常 } // 2. 软复位并等待完成 phy_write(phy_addr, 0, 0x8000); do { status = phy_read(phy_addr, 0); } while ((status & 0x8000) != 0); // 3. 开启自动协商,通告全速能力 phy_write(phy_addr, 4, 0x01E1); // 100M-Full、100M-Half、10M-Full、10M-Half phy_write(phy_addr, 0, 0x1200); // 设置自动协商使能并重启协商 // 4. 等待协商完成,超时保护 for (int i = 0; i < 100; i++) { status = phy_read(phy_addr, 1); if (status & 0x0020) break; delay_ms(50); } return 0; }重点说一下自动协商通信寄存器0x04的配置。0x01E1这个值对应的是:bit12:11百兆全双工和百兆半双工、bit10:9十兆全双工和十兆半双工,也就是把芯片支持的所有速率和双工模式都通告给对端,让对方选择双方都能支持的最高性能组合。如果产品需要固定在百兆全双工模式,可以在0x00寄存器里关闭自动协商,直接强制写入0x0140,锁定百兆全双工。
初始化完成后,从状态寄存器0x01读取bit6和bit5可以判断最终协商结果:bit6是协商完成标志,bit5是协商伙伴能力确认。具体速率和双工信息分散在0x00的bit13和bit8、以及0x01的某些位里,建议读完原始值后先打印成二进制日志,再配合调试助手人工分析,这样能快速发现PHY回读的值是否跟预期一致。
3.3 链路状态检测方案
PHY工作起来之后,软件还需要实时监控链路状态。以太网是物理层事件驱动的,网线插拔、对端关机、交换机重启都可能导致链路断开。MAC和LWIP层必须感知到这些状态变化,才能正确处理ARP缓存、路由表等动态数据。
轮询和中断是两种主流的检测方案。轮询最简单:每隔几百毫秒读一次PHY状态寄存器的bit2,如果发现从1变成0,说明链路断开,通知上层;从0变成1,说明链路恢复,重新初始化MAC的相关配置。轮询的缺点是实时性不够,断开到上层感知之间有最多几百毫秒的延迟,但一般设备完全够用。
中断方案更高效:SR8201F支持通过中断引脚上报链路状态变化,初始化时配置好中断屏蔽寄存器,当链路状态改变时,PHY拉低/拉高中断引脚,MCU外部中断触发回调,在回调里读状态、清中断、更新链路标志。这个方案实时性好,但需要注意中断引脚的电平和触发方式,有的PHY是低有效,配置错了会导致中断风暴。
我的建议:对大多数MCU应用,轮询已经足够了。把轮询放到一个低优先级的任务里,每500ms执行一次,既不阻塞主流程,又能及时感知链路变化。只有对实时性要求极高的工业现场设备,才值得去折腾中断方案。本次项目用的STM32H723跑FreeRTOS,我直接建立了一个独立的“网络管理任务”,专门负责链路轮询和状态上报,实测CPU占用可以忽略不计,链路切换响应也完全满足需求。
4. LWIP移植与对接细节
4.1 移植前需要搞清楚的几个概念
LWIP是嵌入式领域应用最广的轻量级TCP/IP协议栈,全称Lightweight IP。它负责IP、TCP、UDP、ARP等协议的处理,但本身不关注底层以太网硬件怎么操作。MCU里的以太网MAC和PHY才是最底层的物理通道。要让LWIP跑起来,必须把MAC和PHY的操作封装成LWIP规定的几个接口函数,再把网卡挂到协议栈的netif链表里。
很多新手第一次移植LWIP时容易陷入误区,一上来就改协议栈代码。其实LWIP协议栈核心部分基本不用动,真正要写的是底层驱动:初始化函数、发送函数、接收函数,以及连接它们的中断处理或轮询逻辑。PHY这部分则更底层,它属于MAC外部的一个器件,主要工作是告诉MAC“我的链路状态是啥、协商速率是多少”,MAC拿到这个信息后再配置自己的速率和双工模式。
简单打个比方:LWIP相当于公司里管业务的部门,MAC相当于负责把快递送到门口的配送员,PHY就是门口那条路的路况管理员。路况管理员告诉配送员“路通了,可以跑百兆”,配送员才知道用多快的速度去派件。所以PHY调试不通过,上层协议栈再强也白搭,这也是我把硬件和PHY驱动放在前面重点讲的原因。
4.2 底层接口实现
以STM32H723的HAL库为例,移植LWIP时需要实现下面几个核心函数:
low_level_init(struct netif *netif):初始化MAC和PHY,给MAC分配MAC地址,配置RMII引脚时钟,调用PHY初始化函数,设置netif的链路状态。low_level_output(struct netif *netif, struct pbuf *p):发送一个PBUF数据包。通常把PBUF里的数据拷到MAC发送描述符指定的内存区域,然后触发发送DMA。low_level_input(struct netif *netif):从MAC接收描述符里取走已经收到的数据包,封装成PBUF,交给协议栈处理。
其中,low_level_init里有一段代码特别容易踩坑:MAC地址的配置。很多开板默认MAC地址是00:00:00:00:00:00,导致DHCP和ARP通信异常。我建议在产品EEPROM或Flash里保存一个出厂唯一MAC地址,如果没烧录过,则使用MCU的UID生成一个随机MAC,再写入netif的hwaddr字段。比如,把UID的后几个字节和固定前缀02:00:00拼接,既保证了唯一性,又避免了组播地址冲突。
low_level_output相对简单,但有一个优化点值得提:发送时优先使用PBUF的连续内存区域,避免跨描述符分片。如果PBUF的payload特别长,而MAC描述符是分段的,就需要多次搬运,性能和代码复杂度都会上升。LWIP提供了一个PBUF_LINK_ENCAPSULATION机制,可以在分配PBUF时就预留好头部空间,从源头规避这个问题。
接收路径是性能关键。MAC接收到以太网帧后,DMA会把数据写到接收描述符指向的内存缓冲区,然后触发中断。中断里要做的事情是:读状态判断帧是否正确→调用low_level_input提取数据→调用netif->input把数据交给LWIP协议栈→重新加载接收描述符。整个流程要求精简高效,不能在中断里做耗时操作。
一些性能要求不高的场景可以简化:把接收做得稍微轮询化,由主循环定时调用接收函数,省去DMA中断的复杂度。但STM32H723性能足够,还是建议用DMA中断方式,这样在高速收发时不容易丢包。
4.3 LWIP与RTOS的联动
当LWIP和FreeRTOS一起使用时,通常会开启NO_SYS为0的配置,LWIP内部创建tcpip_thread线程,统一处理来自各个网络接口的数据包和协议栈消息。这个线程通过mailbox接收输入数据,你只需要在MAC接收中断里把数据netif->input塞给协议栈,协议栈内部会通过信号量通知tcpip_thread处理。
发送路径则有两条:如果你使用RAW API,那么应用层直接调用netif的linkoutput发送,数据会马上走底层驱动;如果使用Socket API或者Netconn API,发送请求会先投递给tcpip_thread,由它统一调用底层发送函数。两种模式各有取舍,但底层驱动只需要实现netif->linkoutput和netif->output两个回调即可。
移植时一个非常容易出错的地方是没给LWIP分配足量的内存。LWIP需要内存池和PBUF池,默认配置适合PC仿真环境,在高性能MCU上跑业务会明显不够。比如TCP收发窗口很大的场景,PBUF池太小会导致数据接收时出现“out of memory”错误,具体表现就是网络卡顿、传输速率上不去。建议直接在lwipopts.h里调大MEM_SIZE和PBUF_POOL_SIZE,比如STM32H723有564KB RAM,配到几百KB协议栈内存一点也不奢侈。
还要注意FreeRTOS任务栈大小和优先级。tcpip_thread的栈不要低于1024字,默认给的512在复杂业务场景容易爆栈,造成系统随机崩溃。我的习惯是tcpip_thread栈设成1536字,优先级设为2到3级(共5级的话选中间值),既保证响应速度,又不会把其他任务饿死。
4.4 移植完成后的快速验证
驱动写完、系统跑起来之后,第一步验证不是急着写业务代码,而是把网络通路打通。我的验证顺序是:先查物理层,再查链路层,最后查网络层,每一层都确认通过再推进,避免问题层层叠加最后无从排查。
物理层验证:代码里加一个测试函数,周期打印PHY状态寄存器0x01的值,确认bit2为1。用网线把设备和电脑直连,观察PHY的Link LED是否亮起。如果LED不亮,先检查网线、网口、变压器,再检查PHY初始化时序和电源。
链路层验证:设备启动后,发送ARP请求,用Wireshark抓包确认网线上有ARP广播帧发出。对端电脑也会回ARP响应,如果电脑的ARP缓存里能看到设备的IP和MAC映射,说明链路层双向都通了。注意关掉电脑上的防火墙,不然ARP包可能被拦截。
网络层验证:ping 设备IP。如果通则大功告成,不通则用下面章节提到的排查流程逐步往下找。这里还有个经验:第一次ping用大包而不是默认的小包,大包能一次测试出MTU配置和数据处理能力的问题,小包通了不代表大包没问题。
5. 调试实战与问题排查
5.1 调试工具与抓包方法
做嵌入式网络调试,工具用对了能省一半时间。我的标配是:一台带USB转以太网的电脑、一个网络调试助手(UDP/TCP收发小工具)、Wireshark、一个逻辑分析仪(至少10MHz采样率)和一块能测Link状态的万用表。这几样加起来成本不高,但能覆盖从物理层到应用层的全部问题定位。
硬件级调试优先用逻辑分析仪抓RMII接口的波形。RMII的时钟是50MHz,普通逻辑分析仪可能吃力,但抓TX_EN、TX_DATA这类信号足够了。重点看:PHY输出到MAC的CRS_DV是否在收到数据时拉高、TX_EN和数据是否对齐、有没有毛刺。这些波形能直接显示MAC和PHY之间有没有“聋子对话”的情况。
软件级调试优先用MDIO寄存器dump工具。很多MCU调试器或串口调试助手支持自定义命令,我把PHY寄存器读取做成了一个调试命令,可以一次性读出0x00到0x0F的所有寄存器并打印。通过对比寄存器值,能很快发现自动协商没完成、速率配置错误、MDIO位序异常等问题。这比盲目改代码高效得多。
应用层抓包用Wireshark。抓到ARP请求、ICMP请求、TCP握手,能直观看到数据是不是按预期组织。我遇到过一个很隐蔽的问题:设备ping通但对端访问设备TCP端口失败,Wireshark一看,设备发出的TCP SYN包没有回ACK,最后定位到是MAC地址冲突导致交换机把帧丢到了错误端口。
5.2 常见问题Top 5详解
调试过程中我遇到的典型问题远不止5个,但下面5个出现频率最高、也最让人抓狂,逐个拆解一下。
第一个问题是MDIO读不到PHY ID。现象是读取寄存器2和3得到的值全是0xFFFF或者0x0000。这个问题的排查顺序是:先量PHY电源引脚电压是否3.3V,再看复位引脚是不是处理好,再用示波器确认MDC时钟和MDIO数据有没有正常发出。如果波形正常但读不到ID,八成是PHY地址不对,用扫描法挨个试。还有一个容易忽视的地方:MDIO的上拉电阻不能太大,调试时我用了10kΩ的上拉,结果是信号驱动能力不足,改成1.5kΩ后就能正常读到ID了。
第二个问题是链路协商一直不成功。现象是PHY状态寄存器的bit2始终为0,Link LED不亮。排查思路:换网线、换对端设备、检查网络变压器连接。我之前遇到过一次奇葩问题:变压器中心抽头接法照搬了另一颗PHY的方案,结果10M/100M都不协商成功,翻手册发现SR8201F的中心抽头建议是通过电容接地,不能直接接电源。改完电路后一次通过。所以务必以当前PHY的手册为唯一依据,不要跨芯片照搬参考设计。
第三个问题是链路亮但ping不通。这种情况最让人疑惑,物理层显示正常,协议层却上不去。先确认设备IP地址和电脑IP在同一网段、电脑防火墙关了、设备有没有配置默认网关。然后Wireshark看设备是否发出ARP请求,如果没发,检查LWIP初始化后是否调用了netif_set_up,ARP功能是否打开。如果ARP发出去了但对端不回,可能是MAC地址冲突,看看设备MAC是不是和电脑一样,或者全是0。
第四个大包ping不通、小包通。这个现象典型的MTU配置问题。LWIP里网卡的MTU默认是1500,如果MAC层配置了VLAN标记或者其他封装,实际能承载的MTU会变小。排查时先用ping -l 1472测试,如果超过某个值就不通,基本可以确定是MTU设置或DMA描述符数据长度限制问题。也可以在MAC驱动里检查单帧最大接收长度是否至少1536字节,不够的话把DMA描述符的缓冲区开大一点。
第五个问题是偶发丢包。设备平常跑得好好的,偶尔ping丢一两个包。这个问题的根因往往不在协议栈,而在硬件信号质量或者中断处理不及时。用示波器看RMII差分线波形,如果上升沿比较缓或者有振铃,丢包就很容易发生。另外,接收中断里如果耗时太长,或者RTOS里中断优先级设得比tcpip_thread高,也会导致接收描述符来不及回收,缓冲被占满后新数据直接丢弃。解决办法是优化中断处理代码,把主要工作放到协议栈线程里做。
5.3 独家排查技巧:从寄存器看链路健康
PHY寄存器不仅能判断“通不通”,还能看出“为什么不通”。举个例子,状态寄存器0x01的bit5是“远端协商能力”字段,如果读到的是0,说明对端设备没有正确发出能力通告,这种问题往往是网线质量问题或者对端网卡老化,不是本端能修的。再比如,控制寄存器0x00的bit14回环自测,当你在没有插网线的情况下把这个位置1,PHY会把发送数据从内部直接回环到接收端,如果本端能收到自己发的数据,说明PHY的数据通路是正常的,问题一定出在外部。
调试期间,我还习惯把PHY的中断状态寄存器一并读出来,排查异常事件。有的PHY寄存器里,除了链路状态之外,还能看到“发送错误计数”、“接收错误计数”等状态。这些计数器能反映线缆质量、连接可靠性和是否存在电磁干扰。比如接收错误计数一直增长,多半是变压器抽头没接好或者差分走线阻抗不匹配;发送错误计数增长,则可能是PHY到MAC的TX信号时序有问题。
这里再分享一个实用的小技巧:很多PHY支持软件强制速率和双工模式,调试时不一定要依赖自动协商。比如怀疑自动协商有bug,可以直接把PHY配置成100M全双工、MAC也配置成100M全双工,两边手工对齐后再测,若问题消失,说明自动协商流程有兼容性问题;若问题依旧,就可以排除协商问题,往信号完整性方向排查。这个二分法能快速缩小问题范围,比漫无目的地抓包强太多。
6. 关于国产芯片调试的一些个人体会
国产PHY芯片这几年进步很大,SR8201F算是一个比较典型的正面案例。它的兼容性做得足够好,数据手册也很规矩,遇到问题时原厂技术支持响应速度也快。但国产芯片的生态确实还在建设期,网上能搜到的资料、博客、论坛求助相对少,遇到问题主要靠自己啃手册、推时序。这倒逼我把PHY寄存器、MAC接口、LWIP底层这些概念彻底吃透。
调试下来,我的体会最深的一点是:以太网问题不能只盯着上层协议栈看。网络是分层协作的系统,物理层有一点小瑕疵,到上层表现出来往往是千奇百怪的故障。比如时钟抖动偏大,表现出来是偶发丢包;退耦电容位置不对,表现出来是交换机重启后设备拒联网。每次遇到这种“玄学问题”,都要耐下心来从最底层的电源、时钟、波形一个个查起。手里有一台好用的示波器,永远比在网上找现成答案靠谱。
最后再分享一个小经验:从头设计一款带以太网的产品,第一次打板时不要把所有功能都做进去,先做一块最小系统板,包含MCU、PHY、变压器、RJ45、调试接口,把网络通路跑通后再往主板上集成。这样即使遇到问题,排障范围很小,改版成本也低。我这次就是从一块小核心板开始的,整个调试过程大概比以前直接上整板至少省出三天时间,强烈推荐给所有准备做以太网产品的朋友。