ARTICLE DETAIL

资讯详情

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

STM32F429+LWIP+LAN8720A以太网开发全解析:从硬件到协议栈实战

STM32F429+LWIP+LAN8720A以太网开发全解析:从硬件到协议栈实战 简介面向STM32F429嵌入式开发者的以太网通信工程资源包基于STM32F429LAN8720ALWIP实现高效网络通信适合有基础STM32开发经验、想掌握TCP/IP协议栈移植的工程师学习。压缩包约46.15MB主要内容为C语言驱动源码、STM32 HAL库文件、CubeMX工程配置文件与说明文档覆盖GPIO配置、PHY初始化、MAC/DMA收发链路及LWIP协议栈集成等关键环节。目前已有961人浏览学习常用于物联网设备、工业数据采集和板卡网络功能开发场景。对照工程源码与CubeMX配置读者可以快速搭建STM32F429的以太网通信环境掌握LAN8720A的MIIM管理接口操作、MAC地址设置与DMA高效传输方法并深入理解LWIP在无操作系统下的工作流程。 做网络通信开发的人看到“STM32F429_ETH_LWIP_LAN8720A.rar”这个压缩包名字应该就知道里面是什么货色了。过去几年里这几乎成了STM32以太网入门的标准套餐主控用F429因为内置了MAC控制器外部只挂一颗PHY芯片软件协议栈用LWIP应用层不用自己写TCP/IP。这套组合在工业网关、数据采集、智能家居中控上反复出现是很多应届生和转行工程师的第一个网络项目。这篇博文我就以这个压缩包为切入点把从硬件选型到LWIP跑通的整个链路完整拆一遍。无论你拿到的是别人整理好的工程模板还是准备从零开始搭一套这篇文章都能帮你少走弯路。尤其是几个容易卡住的地方——PHY地址配置、RMII时钟来源、描述符数量设置——我会把原理和排查方法都说透。1. 项目整体设计与思路拆解1.1 为什么是F429LAN8720A这个组合STM32F429这颗芯片内部集成了完整的以太网MAC层支持10M/100M速率但是MAC和物理层之间还差一个PHY芯片负责把数字信号变成差分模拟信号送到网线上。说白了F429只管“说人话”真正跟外部世界“喊话”的是PHY。选择LAN8720A核心原因有三个。第一它走RMII接口相比MII接口信号线从16根左右砍到7根PCB布局压力小很多这对两层板或者空间紧张的板子特别友好。第二价格便宜一颗LAN8720A在正常行情下也就几块钱比同类PHY芯片如DP83848便宜不少在量产项目里这个成本差异非常可观。第三它自带50MHz时钟输出功能可以在某些硬件设计方案里省掉一颗有源晶振虽然我后面会说到这个功能并不建议在生产环境里使用但前期调试确实方便。这个方案还有一个隐性优势是生态成熟。因为用的人太多网上能找到大量现成工程、FAQ、坑位记录哪怕你接手的是一个远古版本的CubeMX生成的工程也不至于孤立无援。相比动辄上百引脚的Stm32H7系列F429加LAN8720A的学习曲线非常平缓。1.2 整体数据流和协议栈角色分配很多初学者拿到这个工程第一反应是“LWIP是个什么神奇的东西”。其实LWIP只是一个开源的TCP/IP协议栈它负责把你要发送的数据包装成标准的IP包、TCP包也负责从收到的数据里剥掉这些协议的头部把真正的业务数据交给你的应用代码。数据流大概是这样的。发送方向应用程序调用LWIP的API接口把数据塞进协议栈LWIP在内存里分配pbuf一层层加上TCP头、IP头、以太网头最后把这一整块数据交给STM32的MAC控制器MAC再通过MII/RMII接口把数据送到LAN8720ALAN8720A完成并串转换、电平调整最终通过网线发出去。接收方向完全相反数据从网线进来经过PHY变成数字信号MAC通过DMA把数据搬运到内存里LWIP检查包头把有效载荷交给上层应用。这里要理解一个关键点STM32的MAC控制器是有DMA功能的。也就是说数据从PHY进入内存或者从内存发往PHY这个过程CPU不需要全程参与DMA会自动搬运。这跟用SPI接口外挂W5500这种方案有本质区别W5500内部虽然也有协议栈但SPI传输本身要占用一部分CPU时间而F429以太网的DMA几乎不消耗CPU。1.3 压缩包工程的典型目录结构如果你下载到的压缩包是完整工程通常打开后会看到下面这几类文件很多人拿到工程第一件事就是打开main.c开始找代码结果一头雾水。我先帮你梳理一下工程骨架。Core目录包含main.c、stm32f4xx_it.c中断处理和系统主逻辑都在这里。Drivers目录CMSIS和HAL库源码主要跑stm32f4xx_hal_eth.c、stm32f4xx_hal_eth.h。LWIP目录协议栈源码和arch相关适配层重点文件是lwip.c、ethernetif.c。User目录有些模板工程会把你自己的应用层代码放在这里比如tcp_echo.c、udp_hello.c。在EThernet接口的实现里ethernetif.c是最重要的文件它负责把LWIP协议栈和STM32的HAL以太网驱动衔接起来。很多工程能编译过但是ping不通问题就出在这个文件的low_level_init函数那里的MAC地址、描述符配置、PHY地址必须跟你的实际硬件一一对应。2. 核心细节解析与实操要点2.1 RMII接口的时钟设计最容易翻车的地方先看硬件接口LAN8720A工作在RMII模式下需要的参考时钟是50MHz。这里有一个非常容易踩的坑这个50MHz到底由谁来提供方案一外部25MHz晶振接到LAN8720A的XI和XO引脚LAN8720A内部通过PLL倍频出50MHz然后从它的REF_CLK引脚输出给STM32F429的ETH_RMII_REF_CLK引脚。这个方案的好处是省了一颗50MHz有源晶振。但我实测发现LAN8720A的REF_CLK输出在极端温度情况下会有抖动量产产品最好别这么干。方案二直接用一颗50MHz有源晶振作为系统的MCO或者外部时钟源。STM32F429的PA8引脚可以复用为MCO1功能输出HSE的二分频。如果外部晶振是25MHzPA8可以输出12.5MHz这不行。但如果你的板子上本来就有50MHz有源晶振直接把它接到F429的ETH_RMII_REF_CLK引脚同时给LAN8720A的REF_CLK引脚也配上这就最干净。方案三用PA1引脚的MCO1输出把HSE倍频后的PLL时钟的特定分频输出到RMII引脚。这个配置比较绕需要在CubeMX里仔细设置。我最推荐的还是第二种硬件上干脆利落。但要注意STM32F429的RMII接口对时钟的要求是MAC和PHY必须共用同一个50MHz参考时钟相位差要控制在可接受范围内。在实际布线时这条时钟线的走线要尽量短不要打过孔旁边最好铺地隔离。2.2 PHY地址与寄存器操作要点LAN8720A的PHY地址默认是0x00这是由它的PHYAD0引脚的电平决定的。很多F429开发板上PHYAD0直接接地所以地址是0。但在网上的模板工程里有人写的是1有人写的是0你拿到别人的工程第一步就要确认这个地址对不对。判断方法很简单。ST官方的HAL库有一个函数HAL_ETH_ReadPHYRegister你可以在系统初始化时读一下LAN8720A的PHY ID寄存器地址是0x02和0x03。正常情况下读出来的高16位应该是0x0007低16位应该是0xC0F1LAN8720A的型号ID。如果读不出来有两个可能一是PHY地址写错二是RMII接口的时钟没过来。MDIO时序也是个容易忽略的点。MDC时钟频率不能超过2.5MHz这在HAL库初始化里是可配置的。如果你的STM32主频跑在168MHzMDC分频系数可以设置成42或者更大确保MDC在合规范围内。很多ping不通的怪问题最后查出来是MDC时钟太快PHY芯片正常工作但寄存器偶尔读错。2.3 关于W25Q256的SPI下载算法跟以太网有什么关系顺便提一下热词里的“stm32f429的w25q256的spi下载算法”。很多人会问我在做以太网为什么要碰Flash下载算法原因在于LWIP工程编译出来之后二进制文件往往不小加上调试信息可能超过几百KB如果只烧内部Flash很快就会捉襟见肘。所以很多正经的F429以太网项目会把程序放到外部QSPI Flash里比如W25Q256这时候你就需要编写一个SPI下载算法让Keil或者IAR能直接把程序烧进外部Flash并从那里启动。这个下载算法本质上是一个独立的Flash接口程序包含Init、UnInit、EraseChip、EraseSector、ProgramPage这些回调函数。W25Q256的扇区是4KB编程页是256字节这些参数必须跟下载算法的配置一致。如果你拿到手的以太网工程里恰好带了ExtSPI的烧录算法文件那这个项目大概率是配合外部Flash使用的。真正要搞懂以太网还是得优先把内部Flash版本跑通再考虑外部Flash的玩法。3. 实操过程与核心环节实现3.1 CubeMX关键配置项逐条过一遍我建议你不管手头有没有现成模板都自己用CubeMX重新生成一遍工程这样配置项各是什么含义你心里有数。第一步是引脚的配置。RMII接口需要用到以下引脚PA1或PC1配置为ETH_RMII_REF_CLKPA2配置为ETH_MDIOPC1配置为ETH_MDCPA7配置为ETH_RMII_CRS_DVPC4配置为ETH_RMII_RXD0PC5配置为ETH_RMII_RXD1PG11配置为ETH_RMII_TX_ENPG13配置为ETH_RMII_TXD0PG14配置为ETH_RMII_TXD1。这里要特别注意不同的板子复用映射可能不同一定要以芯片参考手册的复用表为准。第二步使能ETH外设在Middleware和软件包那一栏选中LWIP。关键参数MAC地址可以随便填但注意不能全0不能全1推荐类似02:00:11:22:33:44这种本地管理的单播地址。PHY Address填0或者1跟你的原理图对应。如果硬件是LAN8720APHY的时钟源可以选择BSP的默认配置但最好在代码里显式设置低速模式。第三步配置LWIP。LWIP版本建议选择2.1.2或者1.4.1如果你不需要DHCP就直接设置静态IP我习惯用192.168.1.10子网掩码255.255.255.0网关192.168.1.1。内存设置里MEM_SIZE决定堆内存大小这个值跟你的应用有关如果只是做TCP收发给个几十KB就够了。PBUF池的大小也会直接影响吞吐量如果数据量大建议把PBUF_POOL_SIZE调到20以上。3.2 修改低层驱动让PHY识别正常CubeMX生成的代码不会自动识别LAN8720A的ID有时候你直接跑起来HAL库里的PHY初始化流程会卡住因为标准的STM32评估板用的是DP83848它会在初始化时用默认的PHY地址读取ID。我在工程里通常会在ethernetif.c的low_level_init函数里手动加上一段PHY检测代码读取寄存器0x02和0x03把读到的值打串口确认是0x0007C0F1以后再继续。如果读不到就打印“PHY Not Found”然后死循环这样你就知道是硬件问题还是软件问题。还有一个细节是LAN8720A的自动协商Auto-Negotiation需要时间通常上电后要等2到3秒。很多工程在系统启动后立刻去操作网络接口结果发现Link是断的。我一般会在初始化之后加一个查询PHY状态寄存器的循环等它halftime后再继续后续操作。这个状态寄存器的地址是0x01bit2是Link Status为1代表Link up。3.3 验证TCP收发的最小代码LWIP跑起来之后最简单的测试就是写一个TCP回环服务器监听8080端口收到什么就回什么。核心代码大致是这样的void tcp_echo_init(void) { struct tcp_pcb *pcb tcp_new(); if (pcb ! NULL) { err_t err tcp_bind(pcb, IP_ADDR_ANY, 8080); if (err ERR_OK) { pcb tcp_listen(pcb); tcp_accept(pcb, tcp_echo_accept); } } } static err_t tcp_echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { tcp_recv(newpcb, tcp_echo_recv); return ERR_OK; } static err_t tcp_echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { if (p ! NULL) { tcp_write(tpcb, p-payload, p-len, 1); tcp_recved(tpcb, p-len); pbuf_free(p); } else if (err ERR_OK) { tcp_close(tpcb); } return ERR_OK; }这段代码在每次有数据进来时调用tcp_write把数据回写同时用tcp_recved告知协议栈缓冲区已经释放。注意tcp_write的第四个参数1代表PBUF_FLAG_ACK表示这帧数据发了之后期待对端ACK。如果设置成0也不会错但ACK行为会有细微差别。跑通这个回环服务器后你用PC上的网络调试助手连接它的IP和8080端口发什么收什么说明整个链路已经通了。再往上加HTTP服务器、MQTT客户端都是在这个基础上扩展。4. 常见问题与排查技巧实录4.1 Ping不通时按这个顺序查Ping不通是每个人都会遇到的事我总结了一个排查顺序照着走基本能定位问题。第一步看PHY有没有Link Up。用万用表或者示波器量LAN8720A的LED引脚或者直接读它的状态寄存器看bit2是不是1。不是1说明物理层就没通查网线、查RJ45的差分线焊接、查LAN8720A供电。很多人一上来就怀疑代码其实大部分问题出在硬件。第二步查PHY地址是否匹配。把HAL_ETH_ReadPHYRegister的返回值打印出来如果是0xFFFF大概率是地址不对或者接口没配好。如果读到非零但不是0x0007C0F1说明MDIO通信正常但识别的不是LAN8720A注意芯片是不是被打磨过或者贴错了。第三步查LWIP的IP配置。打开你的电脑命令行ping板子的IP如果提示“请求超时”但板子的串口已经开始打印Link Up了那很大概率是IP没配好或者网线连的是电脑的另一个网口。还有一次我把板子的IP设成跟电脑的IP完全一样结果两台设备冲突ping不通还一直报错。第四步查DMA描述符。如果以上的都正常但是ping大的包会超时小包能通这往往是DMA描述符数量不够或者以太网DMA接收缓冲区太小的症状。把接收描述符的数量从默认的4增加到8很多问题会消失。4.2 LWIP内存不足导致的不稳定现象这类问题比较隐蔽现象是系统运行一段时间后网络收发突然没反应重启又好了。排查方法是在LWIP的配置文件里开启内存统计然后在定时器中断里打印mem_free和pbuf_pool_free的值。如果发现内存持续减少直到0说明你的应用代码有pbuf泄漏。泄漏原因通常是在recv回调里接收数据后用了tcp_write发送但忘记调用tcp_recved导致接收窗口一直没有恢复或者自己申请了pbuf_alloc之后因为某个分支提前return没有执行pbuf_free。这类问题用静态分析工具也能查但经验法则是每次tcp_write之后确保对端数据被确认后缓冲区才会被真正释放所以在回调里一定要及时tcp_recved。另外一个内存相关的坑是MEMP_NUM_TCP_SEG太小。TCP分段缓冲的默认值可能只有几十个如果你在短时间内要发送大量数据包分段缓冲队列就满了后面的数据直接被丢弃。代码上表现为TCP吞吐量突然掉到0但连接依然存在。调大这个宏定义编译前在lwipopts.h里改成128重新编译烧录现象就消失了。4.3 一些调试工具和心得调试网络问题我强烈建议你准备这几样东西。USB转RJ45的调试网卡可以快速确认网线链路是否OK但真正好用的是一个简单的网络抓包工具比如Wireshark。在电脑上抓包看ARP请求和ARP响应是否正常如果板子收到了ARP请求但不回说明协议栈没跑起来可能是LWIP没轮询也可能是MAC地址配置有问题。串口打印也是必不可少的。我习惯在以太网驱动初始化、Link变化、收发首包这些关键节点打标志。调试的时候在HAL_ETH_RxCpltCallback里加一个计数一旦收到数据就打印计数器这样能第一时间判断是MAC收到数据但协议栈没处理还是根本没收到数据。还有一个心得是LWIP的移植代码里ethernetif.c的low_level_input函数里有个判断如果pbuf_alloc失败会直接释放掉这个以太网帧并返回NULL。这个动作本身没错但如果你在调试时发现数据丢包率很高可以在这个分支加一个调试计数看看是不是pbuf池太小导致的。把PBUF_POOL_SIZE从16调到32之后我遇到过接收丢包率从5%降到0的情况。这几种问题排查下来你会发现STM32的以太网链路其实没有那么神秘。硬件上盯紧时钟和PHY地址软件上盯紧内存和描述符剩下的就是LWIP自己的逻辑了。在实际操作中我每次拿到新的F429网口板子都会先把这套最小系统验证流程走一遍确认底层没问题才往上加业务代码这样遇到复杂问题时不需要再回头怀疑底层排查范围一下子就缩小了。本文还有配套的精品资源点击获取
返回列表