1. 项目缘起与整体设计思路
搞嵌入式网络通信的朋友大多有过这样的经历:板子焊好了,代码烧进去了,网口灯就是不亮,或者勉强能ping通但丢包严重,抓包一看全是重传。这类问题十有八九出在MAC和PHY之间的配合上。我前后用STM32F407搭配过LAN8720A和YT8512C两款PHY芯片做以太网通信,踩过的坑足够写一本小册子。这篇内容就把整个搭建过程、选型逻辑、硬件设计要点和调试经验完整梳理一遍,给正在做类似项目的朋友一个可复现的参考。
STM32F407自带以太网MAC控制器,支持RMII和MII两种接口模式。MAC负责数据链路层的帧封装、CRC校验、地址过滤,PHY负责物理层的信号编解码和线路驱动。两者之间通过RMII接口通信,RMII相比MII最大的优势是引脚数量从16根缩减到7根,对于引脚资源紧张的STM32项目来说非常友好。LAN8720A和YT8512C都是10/100M自适应的PHY芯片,都支持RMII模式,但在时钟方案、寄存器配置和硬件设计细节上有不少差异。
这套方案适合谁?如果你正在做基于STM32的物联网网关、工业数据采集终端、智能家居中控,或者毕业设计里需要网络通信功能,这篇文章里的电路设计、CubeMX配置、LWIP移植和问题排查方法都可以直接参考。我假设你有基本的STM32开发经验,会用Keil或者STM32CubeIDE,了解GPIO和中断的基本操作,但不需要你之前接触过以太网协议栈。
整个项目的核心思路是这样的:STM32F407通过RMII接口连接PHY芯片,PHY芯片通过隔离变压器连接到RJ45网口,软件层面用STM32CubeMX配置ETH外设和LWIP协议栈,最终实现TCP/UDP通信。听起来简单,但每一步都有需要注意的细节,尤其是时钟配置和PHY地址设置,这两个地方最容易出问题。
2. 硬件设计核心细节与避坑要点
2.1 RMII接口的引脚连接与时钟方案选择
RMII接口一共需要7根信号线:REF_CLK(50MHz参考时钟)、TXD0、TXD1、TX_EN、RXD0、RXD1、CRS_DV。另外还有MDC和MDIO用于PHY寄存器读写,这两根线不属于RMII数据通道但必不可少。STM32F407的ETH外设引脚是固定的,PA1对应REF_CLK,PA2对应MDIO,PA7对应CRS_DV,PC1对应MDC,PC4和PC5对应RXD0和RXD1,PB11、PB12、PB13对应TX_EN、TXD0、TXD1。这些引脚在CubeMX里配置ETH外设时会自动分配,但硬件布线时必须严格对应,接错一根线就通不了。
时钟方案是RMII接口最容易翻车的地方。RMII协议要求REF_CLK是50MHz,这个时钟可以由STM32提供,也可以由PHY芯片提供,还可以用外部晶振单独提供。三种方案各有优劣,我逐一分析。
第一种方案是PHY芯片提供50MHz时钟给STM32。LAN8720A内部有一个PLL,可以从外部25MHz晶振倍频出50MHz,通过REF_CLK引脚输出给STM32的PA1。这种方案的好处是STM32不需要额外输出时钟,节省一个引脚,而且LAN8720A的时钟输出比较稳定。但要注意LAN8720A的nINT/REFCLKO引脚功能复用问题,需要通过寄存器配置或者硬件上拉/下拉来决定这个引脚是中断输出还是时钟输出。实际使用中,LAN8720A的REFCLKO默认输出50MHz,但前提是外部晶振是25MHz且PLL配置正确。
第二种方案是STM32提供50MHz时钟给PHY。STM32F407的PA8可以配置为MCO1输出,从PLL分频得到50MHz。这种方案的好处是PHY芯片不需要外部晶振,节省一个晶振和两个负载电容。但缺点是STM32的MCO输出抖动可能比较大,尤其是PLL配置不当时,会导致PHY工作不稳定。我实测下来,STM32F407的MCO1输出50MHz给YT8512C时,丢包率明显高于PHY自供时钟的方案,所以除非引脚极度紧张,否则不推荐这种方式。
第三种方案是外部有源晶振同时给STM32和PHY提供50MHz。这种方案最稳定,但成本最高,多一个有源晶振和布线复杂度。适合对通信稳定性要求极高的工业场景。
我个人的建议是:用LAN8720A时优先选PHY自供时钟方案,用YT8512C时也优先选PHY自供时钟方案,两者都支持25MHz晶振倍频到50MHz输出。这样STM32只需要接收REF_CLK,不需要输出,减少了时钟抖动带来的不确定性。
2.2 LAN8720A与YT8512C的硬件设计差异
LAN8720A和YT8512C虽然都是RMII PHY,但硬件设计上有几个关键差异,直接决定了你能不能直接替换。
先说LAN8720A。它的PHY地址由PHYAD0引脚决定,这个引脚在芯片内部有下拉,默认地址是0。如果你想改地址,需要在PHYAD0引脚上加外部上拉电阻。LAN8720A的REF_CLK输出需要外部25MHz晶振,晶振的负载电容要根据晶振规格书选择,一般是10pF到22pF。LAN8720A的复位引脚nRST需要至少100us的低电平复位脉冲,我一般用STM32的一个GPIO控制,上电后拉低200us再拉高,确保PHY完成内部初始化。
YT8512C的PHY地址配置方式不同,它通过PHYAD[2:0]三个引脚来设置,可以支持0到7共8个地址。这意味着你可以在同一组MDC/MDIO总线上挂多个PHY芯片,通过不同地址区分。YT8512C的复位时间要求更长,数据手册上写的是至少10ms低电平,我实际用的时候给到50ms才稳定。这一点如果忽略,会出现PHY时好时坏的现象。
还有一个重要差异是LED引脚。LAN8720A的LED1和LED2引脚可以配置为连接状态和速度指示,但默认极性可能和你的LED电路不匹配。YT8512C的LED引脚配置更灵活,可以通过寄存器设置闪烁频率和极性。我在实际项目中遇到过LAN8720A的LED常亮不闪烁的问题,后来查寄存器发现是LED模式配置错了,改成正确的模式后才正常。
电源设计方面,两款PHY都需要3.3V供电,但内核电压不同。LAN8720A需要1.2V内核电压,内部有LDO可以从3.3V产生,但需要外接电容。YT8512C只需要3.3V单电源,内部集成了所有需要的稳压电路,外围更简单。如果你用LAN8720A,记得在VDDCR引脚上接1.2V去耦电容,否则PHY可能不工作。
2.3 网络变压器与RJ45接口的选型
网络变压器的作用是隔离和阻抗匹配,不能省略。我见过有人直接把PHY的TX+/TX-接到RJ45上,结果通信距离不到一米就丢包。变压器选型主要看几个参数:匝数比一般是1:1,电感量在350uH左右,插入损耗要小于1dB。常用的型号有HR911105A(带RJ45一体式)和H1102NL(分体式)。
带变压器的RJ45一体式座子最省事,HR911105A内置了变压器和LED,直接焊上去就行。但要注意它的引脚定义,TX和RX的差分对不能接反,否则通信不上。我一般会在PCB上把TX+/TX-和RX+/RX-的丝印标清楚,焊接前用万用表测一下通断。
分体式方案用H1102NL变压器加普通RJ45座子,成本更低但布线更麻烦。差分对要走等长线,阻抗控制在50欧姆,差分阻抗100欧姆。如果PCB层数不够,至少要把差分对走在同一层,下面有完整的地平面参考。我试过在两层板上走RMII差分线,通信距离只能到10米左右,换成四层板后轻松跑到100米。
中心抽头的处理也有讲究。TX侧的中心抽头通过一个电容接地,RX侧的中心抽头通过一个电容接电源。这个电容一般是0.1uF,作用是提供交流回路。如果省略这个电容,通信距离会大幅缩短。
3. 软件配置与LWIP协议栈移植实操
3.1 CubeMX中ETH外设的配置要点
用STM32CubeMX配置ETH外设时,有几个关键选项必须设置正确。在Pinout视图中选择ETH,Mode选择RMII,这样CubeMX会自动分配引脚。然后在Configuration选项卡里,Parameter Settings中需要关注几个参数。
PHY Address这个参数必须和硬件上的PHY地址一致。LAN8720A默认地址是0,YT8512C如果PHYAD[2:0]都接地也是0。如果地址设错,MDIO读写会失败,PHY初始化不通过。我遇到过有人把PHY地址设成1,结果一直读不到PHY ID,查了半天才发现是地址不对。
Auto Negotiation建议开启,这样PHY会自动和交换机协商速度和双工模式。Speed和Duplex Mode在自动协商开启时不需要手动设置,但如果你的对端设备不支持自动协商,就需要手动指定。我一般保持自动协商开启,兼容性最好。
Advanced Parameters里的Rx Buffers和Tx Buffers数量根据你的应用场景调整。如果只是做简单的数据采集,4个Rx Buffer和4个Tx Buffer够用了。如果要跑高吞吐量的TCP传输,建议增加到8个以上。PHY Watchdog建议开启,这样PHY异常时能自动复位。
中断配置也很重要。ETH外设会产生多种中断,包括接收中断、发送中断、错误中断等。在NVIC Settings中使能ETH全局中断,优先级根据你的系统需求设置。我一般把ETH中断优先级设得比串口高,比定时器低,确保网络数据能及时处理。
3.2 LWIP协议栈的移植与参数调优
LWIP的移植在CubeMX里已经简化了很多,勾选LWIP组件后大部分代码会自动生成。但有几个地方需要手动调整。
首先是lwipopts.h文件。这个文件里定义了LWIP的各种参数,直接影响性能和内存占用。MEM_SIZE决定LWIP使用的内存池大小,默认是1600字节,对于TCP通信来说太小了。我一般改成4096或者8192,具体看你的RAM余量。MEMP_NUM_PBUF是PBUF的数量,默认16个,如果丢包严重可以增加到32个。
TCP的MSS(最大分段大小)也需要注意。默认是1460字节,这是以太网标准MTU 1500减去IP头和TCP头的结果。如果你的网络环境有PPPoE或者特殊封装,可能需要调小。我一般保持默认值,除非遇到分片问题。
PBUF_POOL_SIZE决定接收缓冲池的大小,默认是16。如果同时有多个TCP连接,这个值需要增加。我试过同时开4个TCP连接,PBUF_POOL_SIZE设成16时偶尔会丢包,改成32后就稳定了。
还有一个容易忽略的参数是TCP_SND_BUF和TCP_WND。这两个参数决定发送窗口和接收窗口的大小。默认值比较保守,如果要做高速数据传输,可以适当增大。但要注意,增大窗口会消耗更多内存,需要根据你的RAM大小权衡。
3.3 PHY初始化代码的关键细节
CubeMX生成的代码里,PHY初始化部分在ethernetif.c文件中。这个文件里的low_level_init函数负责初始化PHY,但默认代码可能不适用于所有PHY芯片。
对于LAN8720A,需要检查PHY的BCR(基本控制寄存器)和BSR(基本状态寄存器)。BCR的bit15是软复位,写1后PHY会复位,需要等待复位完成才能继续配置。BSR的bit2是链路状态,bit5是自动协商完成。初始化时要轮询这两个位,确保PHY就绪。
对于YT8512C,寄存器地址和LAN8720A基本兼容,但有些扩展寄存器不同。YT8512C的特定状态寄存器可以读取更详细的链路信息,比如线序检测结果。如果要做线序自适应,需要配置YT8512C的扩展寄存器。
PHY地址的读写通过MDIO接口,时序很重要。MDC时钟频率不能超过2.5MHz,CubeMX里可以配置分频系数。我一般设成HCLK/64左右,确保MDC在1MHz到2MHz之间。MDIO是双向线,需要正确配置GPIO的复用功能和上下拉。
还有一个坑是PHY的复位时序。LAN8720A需要至少100us复位脉冲,YT8512C需要10ms以上。如果复位时间不够,PHY可能处于不确定状态。我一般用GPIO控制PHY复位,上电后拉低,延时足够时间后再拉高,然后再延时100ms等PHY内部PLL锁定。
4. 常见问题排查与实战避坑指南
4.1 网口灯不亮与链路不通的排查流程
网口灯不亮是最常见的问题,排查要按顺序来,不要东查西查浪费时间。
第一步,确认PHY的电源和地。用万用表测PHY的VDD引脚,应该是3.3V。LAN8720A还要测VDDCR,应该是1.2V。如果电压不对,先查电源电路。
第二步,测晶振是否起振。用示波器测晶振引脚,应该有25MHz正弦波。如果没有,检查晶振和负载电容。我遇到过晶振虚焊导致不起振的情况,补焊后就好了。
第三步,测REF_CLK。如果PHY自供时钟,测PHY的REFCLKO引脚应该有50MHz方波。如果没有,检查PHY的PLL配置寄存器。如果STM32供时钟,测PA1引脚应该有50MHz。
第四步,读PHY ID。在代码里加一段MDIO读PHY ID的代码,LAN8720A的ID应该是0x0007C0F1,YT8512C的ID是0x0007C0F0。如果读不到或者读出来是0xFFFF,说明MDIO通信有问题,检查MDC/MDIO的接线和上拉电阻。
第五步,检查自动协商状态。读BSR寄存器,bit5应该是1表示自动协商完成。如果一直是0,可能是网线没插好或者对端设备有问题。换一根网线试试,或者直接连电脑。
4.2 能ping通但丢包严重的解决方法
能ping通说明基本通信没问题,但丢包严重通常是时钟或者缓冲的问题。
先看时钟。用示波器测REF_CLK的抖动,如果抖动超过500ps,PHY可能采样错误。STM32供时钟的方案抖动通常比较大,换成PHY自供时钟试试。我实测STM32F407的MCO输出50MHz时,抖动在800ps左右,换成LAN8720A自供时钟后降到200ps以内,丢包率从5%降到0.1%以下。
再看缓冲。如果PBUF_POOL_SIZE太小,接收缓冲不够,就会丢包。把lwipopts.h里的PBUF_POOL_SIZE从16改成32,MEM_SIZE从1600改成4096,试试看有没有改善。
还有可能是中断优先级的问题。如果ETH中断被其他高优先级中断打断太久,接收FIFO溢出就会丢包。把ETH中断优先级调高,或者减少其他中断的处理时间。
PCB布线也有影响。RMII的REF_CLK走线要尽量短,最好包地处理。TXD和RXD的走线要等长,差分对要靠近。我遇到过REF_CLK走线太长导致丢包的情况,重新布线缩短后问题解决。
4.3 PHY地址冲突与多PHY管理
如果你的系统里挂了多个PHY芯片,地址冲突会导致通信混乱。LAN8720A的地址由PHYAD0决定,只能设0或1。YT8512C的地址由PHYAD[2:0]决定,可以设0到7。如果两个PHY地址相同,MDIO读写会同时影响两个芯片,导致不可预测的行为。
解决方法是给每个PHY分配不同的地址。YT8512C在这方面更灵活,三个地址引脚可以组合出8个地址。LAN8720A如果要用两个,一个PHYAD0接地地址0,另一个PHYAD0上拉地址1。
在软件层面,LWIP默认只管理一个PHY。如果要管理多个PHY,需要修改ethernetif.c里的PHY地址定义,或者实现一个PHY管理模块,轮流读取每个PHY的状态。我做过一个双网口的项目,两个YT8512C分别设地址0和1,软件里用两个netif结构体分别管理,跑起来很稳定。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 网口灯不亮 | 电源异常 | 测VDD和VDDCR电压 | 检查电源电路,更换LDO |
| 网口灯不亮 | 晶振不起振 | 示波器测晶振引脚 | 补焊晶振,更换负载电容 |
| 读不到PHY ID | MDIO通信失败 | 测MDC/MDIO波形 | 检查上拉电阻,确认GPIO配置 |
| 读不到PHY ID | PHY地址错误 | 核对PHYAD引脚电平 | 修改PHY地址配置 |
| 自动协商失败 | 网线或对端问题 | 换网线,直连电脑 | 更换网线,检查对端设备 |
| ping丢包严重 | 时钟抖动大 | 测REF_CLK抖动 | 改用PHY自供时钟 |
| ping丢包严重 | 缓冲不足 | 查看LWIP统计 | 增大PBUF_POOL_SIZE |
| 通信时好时坏 | 复位时间不够 | 测复位引脚波形 | 延长复位低电平时间 |
| 通信距离短 | 变压器或布线问题 | 检查变压器连接 | 更换变压器,优化差分走线 |
| LED常亮不闪 | LED模式配置错误 | 读PHY LED控制寄存器 | 修改LED模式配置 |
5. 性能优化与稳定性提升经验
5.1 中断与轮询模式的取舍
STM32的ETH外设支持中断和轮询两种模式。中断模式在数据到来时触发中断,CPU响应及时,但频繁中断会增加CPU负载。轮询模式在main循环里定期检查ETH状态,CPU负载低但响应延迟大。
我一般用中断模式接收,轮询模式发送。接收用中断可以确保数据不丢失,发送用轮询可以避免发送完成中断频繁触发。在ethernetif.c里,low_level_input函数用中断触发,low_level_output函数用轮询等待发送完成。
如果CPU负载已经很高,可以考虑降低中断频率。ETH外设支持接收中断合并,可以设置每收到N个包才触发一次中断。在ETH_InitTypeDef里配置RxInterruptMode,可以设置接收中断的触发条件。
5.2 DMA描述符的优化配置
ETH外设使用DMA搬运数据,DMA描述符的数量和排列方式影响性能。STM32F407的ETH DMA支持两种描述符模式:环形模式和链式模式。环形模式简单,描述符首尾相连,适合大多数场景。链式模式灵活,但配置复杂。
描述符数量方面,接收描述符建议至少4个,发送描述符至少2个。如果要做高速传输,接收描述符可以增加到8个。描述符太多会浪费内存,太少会导致DMA等待。
描述符的Own位是DMA和CPU之间的握手信号。DMA写完一个描述符后会把Own位还给CPU,CPU处理完后再把Own位交给DMA。如果Own位处理不当,会出现DMA停止工作的情况。我遇到过发送描述符Own位没及时释放导致发送卡死的问题,后来在发送完成中断里及时释放Own位才解决。
5.3 温度与长期运行稳定性测试
工业场景对稳定性要求高,我一般会做72小时连续运行测试。测试内容包括:持续ping测试、TCP大数据量传输、UDP广播风暴、异常拔插网线。
持续ping测试用ping -t命令,观察丢包率和延迟变化。正常情况下丢包率应该是0,延迟在1ms以内。如果丢包率超过0.1%,就需要排查。
TCP大数据量传输用iperf工具,测试吞吐量。STM32F407的ETH在100M模式下,TCP吞吐量可以跑到60Mbps以上。如果低于40Mbps,可能是LWIP参数没调好。
UDP广播风暴测试网络的抗干扰能力。用广播地址发送大量UDP包,观察是否丢包。如果丢包严重,可能是接收缓冲不够。
异常拔插网线测试PHY的链路检测和恢复能力。拔掉网线再插上,PHY应该能自动重新协商并恢复通信。如果恢复不了,可能是PHY的链路检测配置有问题。
温度测试方面,用热风枪给PHY加热到60度,观察通信是否正常。LAN8720A在高温下时钟抖动会增加,如果丢包率上升,可能需要换YT8512C。YT8512C的高温稳定性更好,我实测在70度下仍然稳定工作。
5.4 从LAN8720A切换到YT8512C的注意事项
如果你已经用LAN8720A调通了,想换成YT8512C,有几个地方需要改。
硬件上,YT8512C的复位时间要求更长,需要把复位延时从200us改成50ms。PHY地址引脚从PHYAD0一个引脚变成PHYAD[2:0]三个引脚,需要重新配置。LED引脚定义不同,可能需要调整LED电路。
软件上,PHY ID不同,如果代码里有PHY ID检查,需要更新。YT8512C的扩展寄存器地址和LAN8720A不同,如果用了扩展功能,需要改寄存器地址。自动协商的默认配置可能不同,建议显式配置BCR和BSR。
我实际切换时,硬件改了复位延时和地址引脚,软件改了PHY ID检查,其他代码基本不用动。LWIP和ETH的配置是通用的,不需要改。
5.5 实操心得与避坑清单
最后分享几条我踩坑总结出来的经验,每一条都是真金白银换来的。
第一条,PCB打样前一定要仔细核对PHY的参考电路。LAN8720A的参考电路里,VDDCR的1.2V去耦电容不能省,我见过有人省了这两个电容,结果PHY时好时坏。YT8512C的参考电路里,晶振的负载电容要根据晶振规格书选,不能随便用22pF。
第二条,MDIO的上拉电阻不能省。MDIO是开漏输出,没有上拉电阻就读不到PHY。上拉电阻一般用4.7k,太小会增加功耗,太大上升沿变缓。我试过用10k上拉,MDIO波形上升沿明显变慢,读PHY ID偶尔失败。
第三条,REF_CLK的走线要包地。50MHz的时钟信号容易辐射,包地可以减少干扰。我试过不包地,结果旁边的模拟电路受到干扰,ADC采样噪声明显增大。
第四条,PHY复位后要等足够时间再初始化。LAN8720A复位后需要等100ms等内部PLL锁定,YT8512C需要等200ms。如果复位后立刻初始化,PHY可能还没准备好,导致初始化失败。
第五条,LWIP的MEM_SIZE和PBUF_POOL_SIZE要根据实际应用调整。做TCP服务器时,这两个值要设大一些。做UDP采集时,可以设小一些节省内存。我一般先用默认值跑通,再根据实际负载调整。
第六条,调试时先用直连电脑的方式测试,排除交换机和路由器的干扰。直连能通再接交换机,这样能快速定位是STM32的问题还是网络设备的问题。
第七条,如果条件允许,尽量用YT8512C替代LAN8720A。YT8512C的集成度更高,外围更简单,高温稳定性更好,价格也差不多。LAN8720A的优势是资料多,遇到问题容易搜到答案。新手可以先从LAN8720A入手,熟悉了再换YT8512C。
这套STM32加PHY的以太网方案,我从最早的LAN8720A到后来的YT8512C,前后调了不下十个项目。每次遇到的问题都不一样,但归根结底都是时钟、复位、地址、缓冲这几个方面。把这几块吃透,以太网通信就没有通不了的。