资讯详情

STM32F407+LAN8720A+RT-Thread以太网驱动移植与排坑实战

📅 2026/9/17 3:37:53 | 华诺云谱 👁 阅读
STM32F407+LAN8720A+RT-Thread以太网驱动移植与排坑实战
接到一个实际项目需求在某块自研板卡上基于STM32F407和LAN8720A这颗PHY芯片把RT-Thread的以太网功能跑通。以前用裸机做过以太网无非是ST官方的Ethernet例程改改但这回要用RT-Thread等于整个驱动链路要从lwIP、netdev、MAC控制器再到PHY芯片全部打通绕不开一堆配置和适配工作。网上关于RT-Thread以太网的文章不少但大多是拿官方板卡演示一换到自己设计的板子问题立刻冒出来PHY地址不对、时钟配置冲突、RMII引脚复用没开、驱动里读不到PHY的ID...每一个都能卡住半天。这次把这套全流程整理出来从硬件设计要点、RT-Thread环境搭建、驱动适配、编译烧录到最后的ping通与常见问题排查完整记录从零到一的踩坑过程。不管是做物联网网关、工业采集设备还是想在自己设计的板卡上跑通网络功能这篇文章应该都能帮上忙。1. 整体设计与方案选型思路1.1 为什么选RT-Thread LAN8720A这套组合STM32做以太网业内常见的方案无非两种一种是直接用STM32内部自带的MAC控制器外挂一颗PHY芯片另一种是选用集成MACPHY的芯片比如W5500这类。两种方案各有优劣但在需要高性价比、灵活性和可扩展性的场景下STM32内置MAC外挂PHY仍然是主流选择。STM32F407系列内置了完整的10/100M以太网MAC控制器支持MII和RMII两种接口模式。但MAC层只能处理数据链路层的活儿物理层的信号编解码、时钟恢复、线路驱动还得交给PHY芯片来完成。LAN8720A就是一颗非常常见的10/100M以太网物理层收发器体积小、功耗低、价格便宜在工业级和消费级产品中大量使用和STM32搭配属于经典组合。选择RT-Thread作为操作系统主要原因有三点。第一RT-Thread对lwIP协议栈的移植已经做得非常成熟netdev框架、sal套接字抽象层都已经集成好了用户只需要把底层驱动适配好上层就可以直接使用BSD socket或者AT命令方式进行网络通信。第二RT-Thread的驱动框架做得规范ETH驱动有一套标准接口以后如果想换PHY芯片只需要改PHY驱动部分上层代码完全不用动。第三调试方便RT-Thread的FinSH控制台可以实时查看网络状态、ping测试、查看路由表等比裸机调试效率高太多了。1.2 LAN8720A与STM32的硬件连接方案LAN8720A支持RMII接口RMII相比MII最大的优势是引脚数量少了一半。MII需要16根数据线而RMII只用7根TXD0、TXD1、TX_EN、RXD0、RXD1、CRS_DV、REF_CLK。对STM32F407来说这几个信号可以直接映射到特定的GPIO引脚上。关键的信号连接如下TXD0和TXD1发送数据线连接到STM32的PA14、PB12或PC1等引脚TX_EN发送使能用于指示TXD上的数据是否有效RXD0和RXD1接收数据线CRS_DV载波侦听和数据有效指示在RMII模式下这个信号同时承担了CRS和RX_DV的功能REF_CLK50MHz参考时钟RMII模式下这个时钟由MAC侧提供可以来自STM32的MCO输出或外部有源晶振这里有一个非常重要的设计决策50MHz参考时钟从哪里来。两种常见方案第一种方案用STM32的MCO1引脚输出PA8复用为MCO1通过配置PLL将50MHz时钟输出给LAN8720A的REF_CLK引脚。这种方案省掉一颗有源晶振成本更低但需要注意MCO输出的时钟质量如果PCB布线不合理高速信号容易受干扰。第二种方案外接一颗50MHz有源晶振直接送给LAN8720A的REF_CLK。这种方案时钟质量更好稳定性更高但成本稍高。我在这块板卡上选择的是MCO输出方案实测下来很稳但后续项目如果对EMC要求较高建议直接上有源晶振。1.3 网络变压器与RJ45的配套选择LAN8720A是PHY芯片它的差分信号线不能直接接到RJ45接口上中间必须经过网络变压器隔离。网络变压器的作用有三个一是电气隔离保护PHY芯片免受外部浪涌和共模干扰二是信号耦合将差分信号耦合到网线上三是阻抗匹配保证100欧姆差分阻抗的一致性。最常见的做法是直接用带网络变压器的RJ45连接器比如HR911105A这类器件。这种方式集成度高PCB布局方便布板时只需要注意差分信号线的阻抗控制和等长处理即可。如果选的是分离式网络变压器需要特别注意中心抽头的连接方式。以LAN8720A为例TX和RX差分对经过变压器后中心抽头一般通过0.1uF电容接地这个电容不能省否则共模噪声会很大。2. 硬件准备与原理图关键点解析2.1 LAN8720A核心引脚功能梳理在开始写代码之前先把LAN8720A的关键引脚功能理清楚。这颗芯片的引脚不算多但好几个引脚是复用功能的原理图设计时一个小失误就可能导致PHY无法工作。XI和XO时钟引脚如果使用外部晶振就在这里连接。但前面提到采用MCO输出方案时这个引脚接到外部50MHz信号源。PHYAD0PHY地址配置引脚LAN8720A的SMI接口地址默认为0x00。如果这个引脚接上拉电阻到3.3V地址变为0x01。实际项目中板卡上如果有多颗PHY需要分别设定不同地址。MODESEL接口模式选择引脚接上拉选择RMII模式接下拉选择MII模式。这个引脚内部有下拉如果不接上拉芯片默认为MII模式会导致RMII接口不工作。REGOFF稳压器输出控制引脚接高电平时关闭内部1.2V稳压器接低电平时启动稳压器。大多数设计直接接地即可。nINT/RMII中断输出引脚默认配置为中断输出也可以复用作RMII的CRS_DV信号这个要仔细看数据手册。LED0和LED1链接状态和活动状态指示通过配置寄存器可以调整显示模式。TXP/TXN、RXP/RXN差分信号对连接到网络变压器。读硬件工程师画的原理图时一定要逐个核对这几个引脚的连接方式尤其是PHYAD0和MODESEL这两个引脚直接决定了驱动代码里PHY地址和接口模式的配置。2.2 电源与复位设计注意事项LAN8720A需要三组电源VDD_3.3V3.3V I/O电源、VDD_1.2V内部数字逻辑电源、VDD_2.5V模拟电源。其中VDD_1.2V可以由芯片内部稳压器产生也可以外部提供。使用内部稳压器时只需要给芯片供3.3V然后在VDDCR引脚上接一个滤波电容即可。实际设计时VDDCR引脚的电容需要特别注意LAN8720A数据手册要求使用2.2uF的陶瓷电容而且必须靠近引脚放置否则可能导致稳压器工作不稳定。这属于踩过坑才知道的细节第一次画板子时我用了1uF电容结果PHY芯片上电后经常无法正常启动换了大电容立马就好了。复位电路方面LAN8720A的复位引脚是低电平有效复位信号保持低电平时间要求至少25us。STM32可以通过GPIO控制PHY的复位引脚这样软件可以随时对PHY进行硬复位。注意复位完成后PHY芯片需要一段时间才能完成初始化一般至少等待150us后再进行SMI总线操作否则读不到寄存器的正确值。2.3 PCB布局与布线的关键要素以太网高速信号的PCB布线是个容易被忽视的坑。RMII接口的时钟频率是50MHz虽然不算特别高但差分信号的布线质量直接影响通信稳定性。布板时几个核心原则第一50MHz的REF_CLK信号线要尽量短走线避免靠近晶振、DC-DC等干扰源。如果使用MCO输出PA8到LAN8720A的REF_CLK引脚之间建议串接一个22欧姆的匹配电阻。第二TXD0、TXD1、TX_EN、RXD0、RXD1、CRS_DV这6根信号线的长度尽量保持一致特别是接收方向的RXD0和RXD1长度差不要超过5mm。第三网络变压器的差分信号线要走差分对线宽根据板厂叠层计算保证单端50欧姆、差分100欧姆的阻抗匹配。第四RJ45外壳的地和数字地之间通过一个高压电容如2kV/1000pF和一个大阻值电阻如1M欧姆连接这样能有效隔离外部干扰同时又不会造成地电位差。3. RT-Thread环境搭建与驱动框架配置3.1 搭建RT-Thread开发环境RT-Thread支持多种开发方式最常用的是基于Env工具和RT-Thread Studio的组合。Env工具可以通过menuconfig的方式灵活配置内核组件RT-Thread Studio则提供了集成化的IDE环境。我的习惯是用RT-Thread Studio建立工程框架用Env工具生成和调整配置最终在RT-Thread Studio里编译下载。如果你习惯用Keil或者IAR也可以RT-Thread的构建系统会生成对应的工程文件。搭建步骤简述第一步从RT-Thread官网下载RT-Thread Studio安装过程没有什么坑一路下一步就行。安装完成后需要下载对应芯片的SDK在Studio的设置选项里添加STM32F407的SDK支持。第二步新建RT-Thread项目选择芯片型号为STM32F407ZGT6或者实际使用的具体型号。Studio会自动生成一个包含基础内核的工程这时候还不会有以太网功能。第三步打开项目下的rtconfig.h文件可以查看当前配置。后面会通过Env工具来添加以太网相关配置。3.2 使用Env工具开启以太网驱动框架Env工具是RT-Thread配置的核心工具。在项目根目录打开Env运行menuconfig命令进入配置界面。以太网相关的配置路径如下RT-Thread Components → Network → Network interface devices → [*] Using network interface devices RT-Thread Components → Network → Socket abstraction layer → [*] Enable socket abstraction layer RT-Thread Components → Network → light weight TCP/IP stack → [*] Enable lwIP这些配置开启后RT-Thread会自动把lwIP和netdev框架编译进来。然后还需要在驱动层开启以太网驱动RT-Thread Components → Device Drivers → [*] Using Ethernet这一步很关键开启后系统才会去尝试初始化以太网设备。另外建议把lwIP的调试选项打开方便排查问题RT-Thread Components → Network → light weight TCP/IP stack → Debug → [*] Enable lwIP debug保存配置后运行pkgs --update拉取软件包然后运行scons --targeteclipse或者直接使用Studio的构建命令重新生成工程。3.3 PHY驱动适配LAN8720A寄存器配置RT-Thread的以太网驱动框架已经抽象出了PHY层接口用户只需要实现phy_read、phy_write两个函数并在初始化时正确配置PHY地址和ID。在libraries/HAL_Drivers/drv_eth.c文件中找到PHY相关配置段。默认情况下很多BSP的PHY地址写的是0x01或者其它值需要改成LAN8720A实际使用的地址。以我的板卡为例PHYAD0引脚接了上拉所以LAN8720A的地址是0x01对应的宏定义修改为#define PHY_ADDRESS (0x01)同时PHY ID寄存器的值也需要确认。LAN8720A的PHY ID是0x0007C0F1其中bit3:0是版本号可能有差异。RT-Thread在做PHY探测时会读取寄存器2和寄存器3的内容来匹配PHY型号。如果你的驱动代码支持自动探测这一步一般能自动识别出来。如果使用自动识别失败可以手动指定PHY ID#define PHY_ID_LAN8720A (0x0007C0F0)还有一个需要注意的地方是PHY的link状态检测方式。LAN8720A的BMSR寄存器地址0x01的bit2表示链路状态RT-Thread的phy驱动通过轮询这个寄存器的值来维护链路的up/down状态。如果硬件上没有把LAN8720A的中断引脚接到STM32采用轮询方式是标准做法没必要为了中断方式强行改硬件。3.4 STM32 ETH外设与引脚复用配置接下来是最容易出问题的一步STM32的ETH外设引脚复用配置。使用CubeMX打开或者直接手写初始化代码把以太网RMII相关引脚配置为复用功能。以STM32F407ZGT6为例RMII接口的引脚复用如下PA1ETH_RMII_REF_CLK复用功能AF11PA2ETH_MDIO复用功能AF11PA7ETH_RMII_CRS_DV复用功能AF11PC1ETH_MDC复用功能AF11PC4ETH_RMII_RXD0复用功能AF11PC5ETH_RMII_RXD1复用功能AF11PB11ETH_RMII_TX_EN复用功能AF11PB12ETH_RMII_TXD0复用功能AF11PB13ETH_RMII_TXD1复用功能AF11引脚复用配置有两处容易踩坑。第一GPIO的速度等级要设置高一点建议设置为GPIO_SPEED_FREQ_HIGH或者VERY_HIGH。如果速度等级太低信号边沿变缓高速通信时容易出现误码。第二某些引脚的复用映射可能不是唯一的。比如PA7既能复用为ETH_RMII_CRS_DV也能复用为其它外设功能配置时一定要确保选择的是AF11这一档同时检查有没有其它外设占用同一引脚。RT-Thread的BSP工程里如果开启了一些默认外设比如SPI、UART有可能刚好冲突了。4. 驱动移植与编译烧录实操过程4.1 修改驱动代码适配自制开发板在RT-Thread的BSP工程里以太网驱动初始化函数一般叫rt_stm32_eth_init。这个函数里会进行MAC地址设置、PHY复位、PHY初始化检测等操作。首先要确认MAC地址。无特殊要求时可以随便用一个私有MAC地址但要注意不能是全0或者全F否则交换机可能会丢弃报文。我的习惯是在驱动里预留一个可配置的MAC地址存储位置默认使用一个固定的测试地址后续量产时再考虑从外部存储读取。PHY复位逻辑也需要确认。我的板卡上PHY的复位引脚接在了STM32的PD3上所以在初始化函数里增加GPIO控制逻辑#define PHY_RESET_PIN GET_PIN(D, 3) static void phy_reset(void) { rt_pin_write(PHY_RESET_PIN, PIN_LOW); rt_thread_mdelay(50); rt_pin_write(PHY_RESET_PIN, PIN_HIGH); rt_thread_mdelay(200); }复位时序很重要低电平保持时间至少25us我留了50ms的余量稳定优先。复位结束后等200ms再开始SMI访问确保PHY内部初始化完成。4.2 编译烧录过程记录完成代码修改后在RT-Thread Studio中点击编译。如果编译报错多数情况是配置选项没有生效检查一下rtconfig.h中是否包含了#define RT_USING_ETH相关的宏定义。编译通过后使用ST-Link通过SWD接口烧录。烧录前注意确认芯片型号和Flash地址是否正确F407的Flash起始地址是0x08000000。第一次上电运行在FinSH控制台打印日志我看到了关键的初始化信息[I/eth] eth0 link up [I/eth] eth0 start这就说明PHY的链路检测已经通过了但是不要急着高兴这时候ping还不一定能通。我遇到的情况是链路状态为up但用PC去ping板子的IP地址完全不通。继续往下排查。4.3 使用FinSH命令行验证网络状态RT-Thread的FinSH控制台提供了丰富的调试命令可以用来验证网络状态。输入ifconfig命令查看网络接口信息msh /ifconfig network interface: eth0 MAC address: xx:xx:xx:xx:xx:xx IP address: 192.168.1.30 gw: 192.168.1.1 netmask: 255.255.255.0IP地址需要在lwIP的配置中指定或者通过DHCP获取。为了快速验证在menuconfig里把lwIP的DHCP关闭或者开启根据实际网络环境来手动指定静态IP。然后是ping命令在FinSH里可以直接ping对端msh /ping 192.168.1.1如果ping不通先用arp -a查看ARP表项确认二层通信是否正常。ARP表的输出如果能看到PC的MAC地址说明二层链路是通的问题大概率出在lwIP的三层配置上。5. 常见问题与排查技巧实录5.1 PHY芯片检测不到寄存器读回来全为0xFFFF这个问题在自制板卡上非常常见主要原因可能有好几个。第一SMI接口的MDC和MDIO引脚配置错误。检查PC1MDC和PA2MDIO是否配置为复用功能并且确认上层驱动中SMI通信的GPIO不是被其它外设占用。有时候开启SDIO或者其它外设后引脚复用表就冲突了。第二PHY的复位引脚被拉低导致PHY一直处于复位状态。检查复位GPIO的电平逻辑一般高电平为正常运行。第三PHY供电异常。用万用表量一下PHY各电源引脚的电压特别是内部稳压器输出VDDCR的1.2V是否正常。如果VDDCR电压不对先查外部的滤波电容。第四PHY地址不匹配。默认LAN8720A地址是0x00如果PHYAD0引脚接上拉变为了0x01驱动里写的还是0x00也会读不到。排查时可以用SMI总线扫描所有可能的PHY地址读到非0xFFFF的寄存器值就是PHY的真实地址。5.2 链路状态显示up但是ping不通链路up说明二层同步已经建立PHY能够正常协商到100M或者10M速率但ping不通问题出在更高层。先用ifconfig查看IP地址是否配置正确。常见的一个坑是开发板和电脑不在同一个网段或者lwIP的网关配置错误。直接手动指定静态IP保证和电脑在同一网段再试。然后排查MAC地址是否冲突换个MAC地址试试。有些路由器或者交换机对重复的MAC地址会做告警处理导致通信异常。再往下可能是ARP的问题。在FinSH里执行arp -a如果看不到电脑的IP对应MAC说明ARP请求发出去了但没收到响应。这时抓包看PHY是否有发送和接收动作确认RMII的数据线是否连对了。特别是RXD0和RXD1的顺序如果接反了系统能协商到链路up但数据完全不通。5.3 网络连接速度只能协商到10M无法上100M这个问题多半是硬件问题软件配置一般不容易导致协商速度下降。排查重点放在REF_CLK时钟质量上。如果MCO输出的50MHz时钟抖动过大PHY在协商时会自动降级到10M以保证通信可靠性。用示波器看一下PA8引脚的时钟波形频率是否稳定在50MHz幅度是否足够一般要求不低于2.7Vpp。另一个原因是RMII信号线布线不规范。TXD和RXD的长度差过大或者阻抗不连续也会导致高速模式协商失败。如果PCB已经做好了没法改只能先确认硬件问题在哪个环节下板改进。还有一种可能变压器的中心抽头电容没有接对。如果网络变压器中心抽头直接接地而不是通过电容接地100M协商也是不达标的。5.4 lwIP内存不足导致TCP通信异常在RT-Thread下使用lwIP时如果发生内存不足通常表现为TCP连接建立失败、数据收发卡顿。最典型的错误日志是[E/eth] no memory for pbuf这个问题的解决办法是增大lwIP的内存池。在menuconfig中检查lwIP的配置项RT-Thread Components → Network → light weight TCP/IP stack → Memory configuration → Link layer max frame size MEM_SIZE PBUF_POOL_SIZE PBUF_POOL_BUFSIZE我的习惯是MEM_SIZE设为65536以上PBUF_POOL_SIZE设为64PBUF_POOL_BUFSIZE设为1518一个标准以太网帧的大小。这样既保证了TCP收发缓冲区充足又不会因为过大而导致SRAM不足。注意STM32F407的SRAM有限如果lwIP内存配置过大RT-Thread的内核对象创建可能会失败。适当的时候可以启用F407的CMM内存管理或者外部SDRAM来扩展RAM空间。5.5 复位后第一次ping不通第二次以后才正常这个问题很隐蔽我排查了很久才发现是PHY的复位时序和MAC初始化顺序不匹配导致的。现象是板子上电后马上ping会丢包或者超时等待几秒后再ping就正常了。原因是MAC控制器的初始化在PHY完全就绪之前就开始发送数据导致以太网MAC的状态机出现了错误。解决办法是在MAC初始化之前确保PHY已经完全从复位状态恢复并且内部PHY固件加载完成。在我的驱动代码里PHY复位后等待200ms再执行MAC的初始化。如果板子对启动时间有严格要求这200ms可以用PHY的BMSR寄存器bit2link status来动态判断等到link up后再初始化MAC。6. 从驱动跑通到实际应用的扩展以太网驱动跑通只是第一步接下来就能在RT-Thread上做很多实际应用了。这块板卡最终要实现的功能是工业数据采集和远程监控网络通了以后我陆续集成了几个典型的应用场景给大家做个参考。第一启用RT-Thread的MQTT软件包实现和云平台的数据对接。在menuconfig中开启MQTT软件包后通过enetdev获取本地IP地址建立和Broker的连接定时上报数据。注意MQTT通信需要用到lwIP的TCP协议TCP连接对lwIP的内存配置要求比UDP高不少上面提到的内存配置要提前备份调整。第二启用RT-Thread的webnet软件包做设备配置页面。这样可以通过浏览器访问板子的IP地址直接查看设备状态和修改配置参数。实测下来webnet的稳定性还不错但是页面资源需要放在文件系统里需要挂载一个Flash文件系统。第三使用SNTP协议做网络对时。工业设备对时间同步要求比较高通过lwIP的SNTP客户端获取网络时间校准系统时间。RT-Thread本身有netutils软件包里面包含了NTP客户端实现开箱即用。这些应用场景跑起来后这块板卡才真正成为一个可用的网络节点。7. 实测总结与避坑心得折腾完这一整套流程把里面最有价值的经验总结一下。第一硬件设计阶段就要把PHY的地址和接口模式定下来。PHYAD0和MODESEL这两个引脚的上下拉电阻直接决定了驱动代码里的PHY地址和RMII/MII模式选择。要是原理图阶段没在意后面软件适配会非常痛苦。第二时钟方案要提前评估好。用MCO输出省成本但要注意时钟质量用外部有源晶振更稳。在这块板卡上MCO方案运行稳定但如果产品要做EMC认证或者对可靠性要求极高强烈建议使用独立的有源晶振。第三调试以太网驱动时紧跟PHY寄存器的状态是关键。RT-Thread的FinSH控制台是个好帮手多打印一些调试信息特别是BMSR寄存器的值以及link状态变化能快速定位问题方向。第四遇到问题先分层排查。通信链路从物理层到应用层每一层都有独立的验证方法PHY寄存器值确认物理层ARP表确认二层ping确认三层TCP连接确认四层。一层一层定位不会白忙活。最后分享一个调试时的小技巧如果板子和电脑直连可以给电脑网卡强制设置IP为192.168.1.x网段避免DHCP等待时间。调试阶段关闭防火墙和杀毒软件的网络防护有时候它们会拦截ICMP包让你误以为板子没通。这套配置过程虽然走了不少弯路但跑通之后再看每一步都有清晰的逻辑硬件上保证PHY正常工作软件上把RT-Thread的驱动框架和实际芯片映射起来再通过分层验证逐一打通。把这套思路记录下来下次再做以太网方案核心链路基本不用再重复踩坑了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。