ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32F407+LAN8720实战:从CubeMX配置到LWIP以太网Ping通全流程

STM32F407+LAN8720实战:从CubeMX配置到LWIP以太网Ping通全流程 做了好几个采用STM32F407加LAN8720跑以太网的项目之后我发现自己每次重新搭工程还是会在同样的几个坑里打转。F407的MAC外设本身不难LAN8720这颗PHY也便宜、大碗、开发板用得最多但问题往往出在CubeMX配置、PHY地址、时钟来源、中断优先级这些“看起来无关紧要”的地方。这篇文章就把我从CubeMX 6.4建工程开始到LWIP在FreeRTOS上跑起来、最终Ping通的全过程做一个完整复盘把最容易踩的坑提前标出来给准备用这套组合做项目的朋友一个可以直接抄的作业路径。如果你正在做MQTT网关、工业数据采集、OTA升级、板间通信这类需要以太网能力的嵌入式设备而且主控恰好是F407这篇文章应该能帮你省下至少一整天的排查时间。文章里涉及到的PHY地址修正、RMII时钟来源判断、LWIP内存参数调整、FreeRTOS中断优先级配合这几个关键点都是我在实际调试中真正卡过的地方我会把现象、原因、解决办法都写清楚尽量让不同基础的读者都能照着做出来。1. 项目定位与整体方案拆解1.1 为什么选STM32F407加LAN8720这套组合STM32F407是ST在Cortex-M4系列里的常青树主频168MHz内置10/100M以太网MAC支持MII和RMII两种接口。以太网MAC负责数据链路层的帧收发但它不处理物理层的编码、电平转换、时钟恢复这些事情所以必须外接一颗PHY芯片。LAN8720是Microchip原SMSC家族的10/100M低功耗PHY价格便宜、外围电路简单、封装也有QFN的小体积版本在国产开发板和工业板卡上出镜率非常高。F407自带MAC加上一颗LAN8720是很多以太网产品的基础方案。相比使用STM32F429或F767F407的优势是够用且成本可控相比串口转以太网模块比如W5500F407加LAN8720的优势是可以直接跑LWIP协议栈灵活性更高、吞吐量也更大不依赖专用芯片内部的协议栈实现。这套组合适合的场景包括设备数据采集上报、远程固件升级、多设备局域网通信、边缘网关的以太网接入。如果你需要在产品里实现一个稳定、可控、成本不高的以太网接口F407加LAN8720基本上是最合适的起点。1.2 RMII硬件设计到底有哪些关键点在决定用CubeMX搭工程之前先得把硬件接口关系理清楚。STM32F407和LAN8720之间支持MII和RMII两种模式MII需要16根数据线外加控制线RMII把数据线缩减到2根发送加2根接收一共只需要9根信号线是绝大多数板子采用的接法。RMII模式下的一个核心要求是参考时钟必须是50MHz。这个时钟可以由MCU这边产生也可以由PHY这边产生关键是两边必须保持同步。常见的板子方案有两种。一种是LAN8720的XI和XO引脚之间接一个25MHz无源晶振PHY内部通过PLL倍频产生50MHz然后把REF_CLK引脚作为输出送到STM32F407的PA1ETH_RMII_REF_CLK此时PA1配置为输入。另一种是直接用外部50MHz有源晶振接到LAN8720的XI同样由PHY把REF_CLK送给MCU。还有一种不太常见但确实存在的接法用STM32F407的PA8MCO1输出50MHz给PHY的REF_CLK。但这种做法的限制比较多因为F407的MCO1实际上是从PLL主时钟分频得到要在系统时钟168MHz的基础上精确得到50MHz并不容易通常需要专门调整PLL参数而且往往会影响PLL48CLKUSB和RNG需要这个时钟。所以我个人强烈建议优先采用前两种由PHY提供参考时钟的方案硬件设计上更省心软件上也少一个变量。RMII模式下固定的引脚映射是这样的信号MCU引脚说明ETH_RMII_REF_CLKPA150MHz参考时钟通常由PHY输出ETH_RMII_CRS_DVPA7载波侦听/数据有效ETH_RMII_TX_ENPB11发送使能ETH_RMII_TXD0PB12发送数据位0ETH_RMII_TXD1PB13发送数据位1ETH_RMII_RXD0PC4接收数据位0ETH_RMII_RXD1PC5接收数据位1ETH_MDCPC1管理接口时钟ETH_MDIOPA2管理接口数据拿到一块新板子第一件事是翻原理图确认REF_CLK是谁给谁的以及LAN8720的复位引脚接在哪里。很多开发板的PHY复位脚直接接在系统复位上上电时由整个MCU的复位过程带起来这种情况软件上不需要额外控制但如果你板上PHY的复位脚单独接了一个GPIO就必须在初始化代码里先拉低再拉高让PHY完成上电复位。这个细节没处理好的话PHY会一直处于复位状态MDIO读写全部失败后面怎么做都是白搭。1.3 软件架构LWIP和FreeRTOS到底怎么配合LWIP是嵌入式领域最常用的开源TCP/IP协议栈它本身有一套独立于操作系统的实现方式。在没有RTOS的环境下LWIP靠周期调用sys_check_timeouts函数来驱动定时器接收则通过中断或轮询方式处理。在引入FreeRTOS之后LWIP可以有更清晰的任务模型一个专门的接收任务等待中断信号收到以太网帧后交给协议栈处理另一个低优先级任务周期调用超时检查函数应用层任务则直接使用socketAPI或RAW API。在CubeMX生成的默认工程里这套结构已经帮你搭好了。ethernetif.c里有一个接收线程通过信号量等待ETH中断回调收到信号后从DMA描述符里取数据调用netif-input把数据交给LWIP的tcpip线程。另一个lwip线程循环调用sys_check_timeouts负责TCP重传、ARP超时、DHCP定时等周期任务。这个架构的好处是LWIP的协议栈处理和应用层完全隔离应用层任务不会被网络中断频繁打断。缺点是必须处理好中断优先级和FreeRTOS的配合这也是后面最容易出问题的环节之一。如果ETH中断优先级设得太高超过了FreeRTOS允许调用API的最高优先级在中断回调里使用osSemaphoreRelease等函数时就会触发断言或进入HardFault。2. CubeMX 6.4配置实操2.1 时钟树168MHz系统时钟和RMII参考时钟不能混打开CubeMX新建工程后第一件事是配置时钟树。STM32F407的以太网MAC使用AHB1总线时钟所以只要系统时钟正常跑起来MAC本身不会有大问题。关键是RMII参考时钟的50MHz来源必须和硬件接法一致。这里我把两种情况分开讲。如果你的板子是LAN8720自带25MHz晶振、通过REF_CLK引脚把50MHz输出给MCU那么CubeMX时钟树里不需要做任何特殊配置只需要正常把系统时钟配到168MHz即可。编译生成的代码里PA1会自动被定义为复用功能ETH_RMII_REF_CLKMCU侧接收PHY送来的50MHz时钟。如果你的板子比较特殊是外部有源50MHz晶振直接接到PHY的XI而不是由PHY输出REF_CLK给MCU接线和上面是一样的MCU的PA1仍然是输入模式。如果真的遇到非要用MCO1输出50MHz给PHY的板子你需要在CubeMX时钟树里找到MCO1输出选好时钟源和分频系数确保输出精确50MHz同时还要确保系统时钟和PLL48CLK仍然满足要求。但前面说过这个方案不推荐遇到这种硬件设计我只能说尽量改板子。需要注意的是不要看到网上有人说“PA8输出50MHz”就不加思考地照搬。正点原子和野火的F407开发板默认都是LAN8720模块自带25MHz晶振PHY内部倍频后由REF_CLK输出给PA1。这一点以你自己手头板子的原理图为准。具体到时钟树参数以最常见的8MHz外部晶振为例PLL_M配置为4PLL_N配置为168PLL_P配置为2这样VCO输出336MHz最终系统时钟168MHzPLL_Q配置为7得到48MHz给PLL48CLK。如果外部晶振是25MHz那PLL_M等于25参数含义是一样的。用CubeMX的时钟树图形界面点几下就能自动算好不需要手算但一定要留意右下角有没有红色报错。2.2 ETH外设配置模式、PHY地址和中断优先级在Pinout视图中搜索ETH启用ETH外设然后在Configuration里把接口模式选为RMII。CubeMX会自动把PA1、PA2、PA7、PB11、PB12、PB13、PC1、PC4、PC5这9个引脚设置为对应的复用功能不需要手动逐个配置。重点在ETH的参数配置页面。打开ETH配置后你会看到一个PHY Address的输入框这是整个工程里第一个大坑。CubeMX的默认值通常是1但很多LAN8720模块的PHY地址实际上是0。PHY地址由芯片的PHYAD0引脚电平决定LAN8720的PHYAD0引脚默认下拉为0如果板子上没有专门接上拉电阻那么PHY地址就是0而不是1。如果PHY地址不对HAL_ETH_Init初始化时会去读取PHY的ID寄存器读到的值不对初始化直接返回HAL_ERROR后面的以太网功能自然全部失效。判断PHY地址最可靠的方法是读取PHY ID寄存器我后面在代码调试部分会写一个简单的验证函数。但既然现在在配置界面先根据板子原理图把PHY Address填对再说。ETH中断的优先级也要在NVIC设置里配好。由于LWIP的接收线程依赖ETH中断回调来发信号量而这个回调在FreeRTOS环境中会调用osSemaphoreRelease所以ETH中断优先级数值不能小于FreeRTOS配置的configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY。在STM32的优先级表达里数值越大优先级越低通常把ETH全局中断优先级设置为5到7是比较安全的。如果设置成0或1表面看中断响应更快但一旦在中断回调里调用FreeRTOS的API轻则断言失败重则直接HardFault。2.3 LWIP中间件配置内存参数和协议开关在Middleware里启用LWIP后需要重点关注LWIP配置页面里的几个关键参数。这个页面里的选项非常多但不是每个都需要动真正影响项目稳定性的主要有这几个MEM_SIZE是整个LWIP协议栈的堆内存大小用于分配TCP报文段、路由表、接口数据结构等。默认的1600字节对于只跑UDP的简单应用勉强够用但如果你要跑TCP客户端或服务器建议调到4096以上否则连接建立后很容易出现内存分配失败表现为连接建立后传输停滞或直接断开。PBUF_POOL_SIZE是数据包缓冲池的数量默认16建议保持16或增大到32。PBUF_POOL_BUFSIZE是每个缓冲池里单个缓冲区的大小必须在1518以上因为以太网最大帧长就是1518字节太小会导致大包被丢弃。这两个参数直接影响网络吞吐量如果项目有较大的数据发送需求PBUF_POOL_SIZE可以适当加大。TCP_WND、TCP_SND_BUF这两个参数决定TCP窗口大小和发送缓冲大小默认值通常偏小。对于一般的数据上报业务把TCP_WND设置为4倍TCP_MSS以上、TCP_SND_BUF设置为4倍TCP_MSS以上传输性能会明显改善。如果你不跑TCP只跑UDP这些参数可以不动。DNS、DHCP这类功能按需打开。DHCP适合产品调试阶段自动获取IP省去手动配置的麻烦但正式产品里往往需要固定IP所以我一般建议用静态IP加DHCP备用的方案。此外在LWIP配置页面里设置的静态IP地址、子网掩码、网关地址会在生成的代码里直接生效后面不需要再手动改。2.4 FreeRTOS配置任务优先级和堆栈在Middleware里启用FreeRTOS时我建议使用CMSIS-RTOS V2封装层这样CubeMX生成的代码风格统一后面创建任务、信号量、队列都方便。FreeRTOS的堆大小FreeRTOS Heap Size建议保持在默认的15360字节以上如果应用层任务比较多或者TCP缓冲需求大可以适当增加到32768甚至更大。LWIP的接收任务和tcpip线程都要从堆里分配任务控制块和栈空间堆太小会导致任务创建失败。任务优先级方面CubeMX默认生成的lwip任务和eth_rx_thread任务优先级偏高这个默认配置其实是可以用的但你要注意自己的业务任务不要和它们抢占得太厉害。我的经验是网络接收任务优先级略高保证数据不丢超时检查任务可以稍低应用层业务任务放在网络任务之下避免业务任务里做耗时操作时影响TCP重传和ARP超时处理。如果应用层任务很多建议把ETHRX线程的优先级保持在默认水平或略高不要低于普通业务任务。LWIP线程负责超时检查如果优先级太低在网络繁忙时可能出现TCP重传超时不准的问题表现为连接异常断开。最稳妥的做法是先用默认优先级把系统跑通再根据实际业务调整。3. 代码生成后的必要改造与核心实现3.1 修正PHY地址并验证PHY IDCubeMX生成代码之后进入eth.c文件找到HAL_ETH_MspInit或MX_ETH_Init函数。在MX_ETH_Init里你会看到一行类似这样的代码heth.Init.PhyAddress 1;这行代码必须和你的硬件匹配。如果LAN8720的PHYAD0引脚是接地的把这里的值改成0heth.Init.PhyAddress 0;改完之后怎么确认PHY地址是对的在main.c里的用户代码区加一段调试代码复位PHY后读取PHY的ID寄存器uint32_t phy_id1 0; uint32_t phy_id2 0; HAL_ETH_ReadPHYRegister(heth, 2U, phy_id1); HAL_ETH_ReadPHYRegister(heth, 3U, phy_id2); printf(PHY ID1: 0x%04X, ID2: 0x%04X\r\n, phy_id1, phy_id2);LAN8720正常的返回值是ID1等于0x0007ID2等于0xC0F1。如果读出来是0xFFFF说明MDIO通信失败优先检查PHY地址、PHY复位引脚、以及MDIO和MDC的引脚复用是否正确。如果你的板子PHY地址确实是1那CubeMX生成的默认值就不用改。但不管怎样这个验证步骤都不能省它能帮你把PHY层面的问题在5分钟内定位出来避免后面辛辛苦苦调半天发现是PHY地址不对。3.2 网卡初始化顺序与LWIP启动流程在CubeMX生成的默认代码里MX_LWIP_Init函数中会依次完成这几件事设置MAC地址、注册网卡接口、把网卡状态设置为UP、创建LWIP相关的线程。MAC地址这个细节很容易被忽略。CubeMX生成的代码里默认MAC地址是全零或者一个固定值为了确保设备在网络中不冲突建议设置一个唯一的MAC地址。嵌入式产品的MAC地址一般从厂商分配的地址段中取或者使用本地管理地址范围。一个常见的做法是#define MAC_ADDR0 2U #define MAC_ADDR1 0U #define MAC_ADDR2 0U #define MAC_ADDR3 0x12U #define MAC_ADDR4 0x34U #define MAC_ADDR5 0x56U第一个字节的最低位为1表示这是本地管理的单播MAC地址不会和全球唯一的MAC冲突。初始化顺序上CubeMX生成的代码已经帮我们做好了。MX_ETH_Init先初始化MAC和DMAMX_LWIP_Init再基于MAC初始化LWIP协议栈。你只需要确保MX_FREERTOS_Init在MX_LWIP_Init之后执行因为LWIP创建任务时依赖FreeRTOS的内核已经就绪。在main.c里这个顺序是已经排好的不需要额外调整。3.3 静态IP设置与Ping通的第一步如果你在CubeMX的LWIP配置页面里填好了静态IP地址比如192.168.1.10子网掩码255.255.255.0网关192.168.1.1那么生成的lwip.c里会自动包含对应的配置。在你的开发板上电后网卡会使用这个IP地址启动。此时把电脑的有线网卡配置为同一网段比如192.168.1.100子网掩码255.255.255.0然后用网线直接连接电脑和开发板。在电脑的CMD里执行ping 192.168.1.10 -t这是整个调试过程中最令人期待的一步。如果通了意味着MAC、PHY、DMA、LWIP、FreeRTOS这一整条链路全部正常后面的业务逻辑开发就有了一个坚实的基础。如果不通不要慌。先检查电脑网卡有没有识别到链路正常情况下插上网线后电脑右下角网络图标会从红叉变成正在识别或已连接。如果电脑仍然显示网络断开说明物理层链路就没建立起来问题基本锁定在PHY一侧时钟没起来、复位没完成、或者LAN8720的供电有问题。此时可以通过读写PHY寄存器来进一步确认PHY是否处于正常工作状态。3.4 应用层任务与LWIP的交互写法在FreeRTOS环境下跑LWIP时应用层任务最重要的一个原则是不要在中断回调或以太网接收线程里做耗时操作也不要在协议栈内部回调函数里直接发送大块数据。我一般把业务任务和网络任务分开业务任务里调用LWIP的socketAPI或者通过队列把待发送数据发给一个专门负责TCP/UDP发送的任务。举一个最简单的TCP客户端写法void tcp_client_task(void *argument) { struct netconn *conn; struct netbuf *buf; err_t err; ip_addr_t server_ip; IP4_ADDR(server_ip, 192, 168, 1, 100); conn netconn_new(NETCONN_TCP); while (1) { if (netconn_connect(conn, server_ip, 8080) ERR_OK) { // 连接成功后循环发送数据 while (1) { buf netbuf_new(); netbuf_alloc(buf, 64); memcpy(buf-p-payload, hello, 5); netconn_write(conn, buf-p-payload, 5, NETCONN_COPY); netbuf_delete(buf); vTaskDelay(pdMS_TO_TICKS(1000)); } } vTaskDelay(pdMS_TO_TICKS(3000)); } }这个例子只是演示了任务里使用netconn API的连接和发送流程实际项目中要注意连接失败时的重连退避策略以及多客户端连接时需要使用线程安全机制。4. 常见问题排查实录4.1 PHY读不到IDMDIO通信全失败这个问题的现象是程序执行到HAL_ETH_Init时返回HAL_ERROR或者在读取PHY寄存器时得到0xFFFF。排查顺序是这样的先确认PHY地址对不对这占了大约六成的可能性。然后确认PHY的复位引脚状态很多板子的LAN8720复位由MCU的一个GPIO控制代码里必须在初始化PHY之前把它拉高否则PHY一直处于复位状态。接着确认MDIO和MDC配置是否正确这两个引脚需要在CubeMX里被设置为ETH_MDC和ETH_MDIO的复用功能。最后检查供电LAN8720通常需要3.3V和1.2V两组电源某些模块还有单独的VDDIO引脚供电异常也会导致MDIO读不到数据。我在一个定制板卡上曾经遇到一个很奇怪的现象PHY ID时而能读到时而读不到。最后定位到是PHY复位引脚在其它初始化函数里被重新配置成了普通GPIO导致PHY被周期性复位。这种排查思路值得记录一下如果PHY时好时坏优先检查有没有代码在ETH初始化之后又改动过相关GPIO的状态。4.2 链路状态一直Down网口灯不亮网口灯不亮或者链路状态寄存器始终显示未连接这个问题通常和网络数据通路没有关系问题出在物理链路层面。先检查网线本身是否正常换一根已知好的网线测试。然后检查PHY的参考时钟是否为50MHz用示波器测量REF_CLK引脚这是最直接的方法。如果没有示波器可以通过修改PHY的寄存器从LED状态间接判断但效率低很多。再检查变压器和RJ45座子的焊接这个问题在新打样的板子上尤其常见看起来焊好了实际上虚焊用放大镜看一圈能省不少时间。还有一个容易被忽略的坑是LAN8720的RMII模式配置。这颗PHY芯片在上电或复位后默认情况下未必是RMII模式需要确认PHY的配置寄存器里工作模式正确。如果你在CubeMX里把STM32配成了RMII但PHY实际工作在MII或者别的模式链路层的信号就对不上即使物理链路有信号也Ping不通。这种情况一般通过读取和设置PHY特殊控制寄存器来处理开发板上出厂已经配置好的话通常不用管。4.3 Ping不通的完整排查路径当链路状态已经UP、但Ping不通的时候就要按照网络分层的思路来排查。我把这个步骤写成了一套固定流程每次遇到都按这个顺序走第一步检查电脑和板卡的IP地址是否在同一网段。板卡192.168.1.10电脑192.168.1.100掩码255.255.255.0这个组合是最不容易出错的。第二步关闭电脑的防火墙。Windows的防火墙默认会拦截来自开发板的ICMP回显请求。测试时我都是直接在Windows防火墙里把ICMP回显请求设置为允许或者干脆临时关闭防火墙。第三步用arp -a命令查看电脑的ARP缓存表里有没有开发板的MAC地址。如果在Ping了之后ARP表里出现了板卡的MAC说明二层链路是通的问题出在IP层的发送或接收如果ARP表里始终看不到板卡的MAC说明开发板发出的ARP请求没有到达电脑问题在PHY、DMA或LWIP接收链路。第四步用Wireshark抓包。在电脑有线网卡上打开抓包再Ping一次观察是否有开发板发出的ARP请求或应答。这一招可以直接定位问题是在物理层、数据链路层还是更高层的协议配置上非常高效。第五步确认板卡终端有没有打印LWIP的初始化日志。CubeMX生成的LWIP代码默认会向printf输出一些调试信息如果能看到类似registered 0或网卡信息说明协议栈注册成功如果什么都没有检查串口重定向是否正常。4.4 死机与HardFault中断优先级和内存泄漏在FreeRTOS环境下跑LWIP死机问题主要集中在两个地方ETH中断优先级和内存分配。ETH中断优先级的问题前面已经提过在FreeRTOS配置里configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY默认是5如果你的ETH中断优先级数值小于5也就是优先级高于内核那么中断回调调用osSemaphoreRelease时会导致系统进入断言失败或者HardFault。解决方法是把ETH中断优先级改成5或6或者把configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY调整为和ETH中断优先级匹配的值。内存泄漏的问题通常在长时间运行后暴露设备刚开始一切正常跑了几个小时或几天后开始出现Ping不通、连接失败、自动复位等故障。排查时重点关注LWIP的内存堆和PBUF池是否耗尽。可以在LWIP里打开内存统计功能通过串口周期性打印剩余内存和PBUF数量观察数值是否持续下降。如果持续下降说明某个地方在持续分配内存却没有释放常见的元凶是TCP连接异常关闭时缓冲区没有被正确回收或者应用层发送数据时没有及时释放netbuf。4.5 LWIP内存分配失败导致连接断开如果传输数据量较大或者并发连接数较多LWIP可能因为内存不足而分配失败。表现为TCP连接建立后传一会儿数据就断开重连后又能传一会儿如此循环。这类问题本质上就是LWIP的MEM_SIZE、PBUF_POOL_SIZE、PBUF_POOL_BUFSIZE这几个参数和实际业务不匹配。我的建议是哪怕当前项目只用UDP也把MEM_SIZE先调到4096以上给协议栈留足余量。跑TCP时TCP_SND_BUF和TCP_WND不要低于四倍TCP_MSS否则大文件的传输效率会非常低。如果做完这些调整仍然频繁分配失败那就说明内存确实不够用需要在CubeMX里增大FreeRTOS的堆大小因为LWIP的内存池本质上是从FreeRTOS的堆里静态分配的。5. Ping通实测与稳定性验证5.1 完整Ping通测试流程当整个工程配置完成、代码编译下载之后我有一套固定的验证流程确保不是侥幸Ping通。先用串口助手打开板卡的调试串口观察启动日志。正常情况下ETH初始化和LWIP初始化成功后串口会打印出网卡注册信息。把电脑网卡IP改成192.168.1.100子网掩码255.255.255.0网关留空或填192.168.1.1都行。然后用网线直连板卡执行ping 192.168.1.10 -t。如果通了先不急着庆祝我用一个长ping来验证稳定性连续ping 2000个包观察丢包率。一个健康的F407加LAN8720方案在没有业务流量的情况下局域网内的丢包率应该是0平均延迟一般在1ms以内。如果在长ping过程中出现个别超时优先怀疑PHY芯片的散热、电源纹波、以及网线质量。这类偶发性丢包和软件配置的关系不大更多是硬件设计上的细节问题。5.2 用TCP/UDP进一步验证网络通路Ping通只能验证ICMP协议也就是IP层和ARP层的工作情况应用层业务能不能跑通还需要用TCP或UDP实测。我习惯用两个方法验证。第一个是在电脑上用网络调试助手开一个TCP服务器监听8080端口然后让板卡主动连接并周期发送数据。如果能稳定收到数据说明TCP连接、数据分包、重传机制都是正常的。第二个方法是用板卡开一个TCP服务器或UDP服务电脑端周期发送数据板卡接收后原样回发在电脑上验证回显内容是否正确。这一步非常重要因为很多问题在Ping阶段暴露不出来只有在TCP连接多次建立、断开、重连之后才会暴露。比如TCP连接反复断开重连时如果LWIP的TIME_WAIT状态处理不当会在内存里积累大量残留连接最终导致新的连接无法建立。5.3 提升稳定性的几个实践建议跑通只是第一步真正可靠的产品还要经过稳定性打磨。在我的项目里有几个做法对提升整个以太网链路的稳定性帮助很大。第一个是给PHY做定期的链路状态监控。通过任务周期读取PHY的基本状态寄存器检测link状态是否变化。对于工业应用网线松动、对端设备断电都是常见故障检测到link down后在应用层做相应的报警或重连处理比让协议栈自己去碰运气要可靠得多。第二个是TCP连接发送数据的节奏控制。嵌入式设备的内存资源有限如果应用层在短时间内连续向网络上发送大量数据LWIP内部缓冲会迅速打满。我一般在业务逻辑里做简单的发送节流比如把大数据分成小块每块之间加几毫秒间隔或者依赖TCP的发送缓冲区判断缓冲区满了就等待。第三个是设计好异常恢复路径。以太网设备在实际使用中一定会遇到网线拔出、对端重启、路由器切换这些情况。如果软件里没有对应的恢复机制网络恢复后设备可能无法重新建立连接。我的做法是在应用层任务里加入连接状态监控断线后自动重连重连次数和间隔采用指数退避策略避免在网络不稳定的情况下对PHY和路由器造成持续的连接风暴。最后分享一个我自己的调试习惯每次拿到一块新的F407加LAN8720板子我从来不急着开CubeMX而是先做三件事翻原理图确认REF_CLK从哪来、确认PHYAD0引脚是拉高还是拉低、确认PHY复位脚接在哪里。这三个信息确定了编译出来的第一版代码八成就能跑通。软件调试时遇到问题我也习惯先验证PHY层的寄存器读写再往上层查因为从底层往上层排查效率最高也最不会绕远路。这套组合虽然从配置到Ping通的过程不算太轻松但只要跑通一次后面的项目复用起来就会非常顺手。CubeMX生成的工程结构、LWIP和FreeRTOS的协作方式、PHY层的调试方法这些沉淀下来之后再做新的以太网设备就只是业务逻辑和时间问题。
返回列表