ARTICLE DETAIL

资讯详情

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

STM32F107+LAN8720A以太网调试指南:从CubeMX到LWIP完整流程

STM32F107+LAN8720A以太网调试指南:从CubeMX到LWIP完整流程 STM32F107LAN8720A这套组合我在一个数据采集项目里用了快两年。当时选它就是因为F107这颗互联型MCU自带以太网MAC成本比外挂W5500或者SPI网卡低不少而且LAN8720A这颗PHY芯片几块钱一颗市面上模块也多替换方便。但真调起来才发现从CubeMX生成工程到LWIP能正常ping通中间藏了不少坑有些坑是硬件设计时就埋下的有些是配置理解不到位造成的。这篇文章我会把从CubeMX配置、PHY芯片连接、LWIP协议栈接入到常见问题排查的完整流程写出来代码部分会贴我在实际项目中验证过的关键代码。如果你正准备用F107或者类似的STM32入门以太网这篇文章能帮你少走很多弯路尤其是那几个不看datasheet根本想不到的硬件细节。1. 先搞清楚硬件LAN8720A到底怎么接1.1 为什么是STM32F107 LAN8720ASTM32F107的特殊之处在于它内置了10/100M以太网MAC控制器这是F1系列里独有的。但MAC控制器不能直接插网线必须外接一颗PHY芯片来完成物理层信号的编码解码、时钟恢复、线路收发这些工作。LAN8720A是Microchip原SMSC的低功耗10/100M以太网PHY支持RMII接口功耗大概200多毫瓦待机模式更低很适合做嵌入式联网设备。选它还有几个实际原因一是便宜模块化之后几块钱就能买到二是Auto-MDIX自动交叉功能直连线和交叉线都能用调试时不用纠结网线类型三是25MHz晶振模式很灵活时钟可以自己产生不依赖MCU提供。不过这些便利都是建立在电路设计正确的前提下引脚接错了再好的PHY也白搭。1.2 RMII引脚对应表附参考接线LAN8720A和STM32F107之间通过RMII接口连接总共只需要9根信号线比MII的16根简洁很多。我整理一下我在板子上验证过的接法STM32F107引脚RMII信号作用说明PA1REF_CLK50MHz参考时钟由LAN8720A提供PA2MDIO管理接口数据线读写PHY寄存器PC1MDC管理接口时钟线PA7CRS_DV载波侦听/数据有效指示PC4RXD0接收数据位0PC5RXD1接收数据位1PB11TX_EN发送使能PB12TXD0发送数据位0PB13TXD1发送数据位1这个接法是STM32F107的固定复用功能基本不能改。有些开发板会把LAN8720A的复位脚也接到MCU的某个GPIO上比如PB10用来做软件复位。如果不用软件复位接个RC复位电路也行但注意PHY的上电复位时间一般在10ms左右MCU初始化时要等PHY稳定再操作寄存器。1.3 电源、时钟、复位与网络变压器的细节LAN8720A的供电设计有几个容易踩的点。一是它内部有1.2V核心电压稳压器VDDCR引脚需要外接去耦电容一般是4.7uF加100nF并联到地这个电容不能省否则芯片工作不稳定。二是VDDIO电平要和MCU匹配F107是3.3V系统LAN8720A的VDDIO接3.3V没问题。时钟方面我强烈推荐用25MHz无源晶振接LAN8720A的XI和XO引脚让它工作在主模式由PHY自己产生50MHz的REF_CLK输出给STM32F107的PA1。这样MCU这边不需要额外生成50MHz时钟时序也最稳定。外部50MHz有源时钟的方案也能用但对晶振精度要求比较高而且成本上不划算。差分信号那一侧要注意LAN8720A的TXP/TXN、RXP/RXN必须经过网络变压器再接RJ45座。市面上常见的带变压器RJ45座比如HR911105A可以直接用模块化产品一般也把变压器集成好了。如果自己画板子用裸RJ45座一定要外加网络变压器不然信号传输会非常不稳定。2. 硬件上最容易翻车的三个点2.1 nINT/REFCLK0引脚必须下拉这是LAN8720A最隐蔽的坑没有之一。LAN8720A的第1脚叫nINT/REFCLK0它是一个复用引脚既可以作为中断输出也可以在从模式下作为外部参考时钟输入端。问题在于大部分应用里我们用不到这两个功能然后就把它悬空了。悬空会怎样我在板子上实测过现象是刚开始一切正常能ping通但运行几分钟到几十分钟不等会出现周期性丢包严重的直接断网重新插拔网线或者复位MCU能恢复但过一阵又犯。这个问题的根源是REFCLK0输入端内部没有做可靠的下拉悬空状态下容易感应到相邻引脚翻转带来的干扰干扰一旦耦合进去PHY内部时钟逻辑就出问题。解决方法是把第1脚外接一个10kΩ电阻下拉到地。这个操作在几乎所有LAN8720A模块的原理图里都有但如果你是自己画板子很容易忽略。我后来画第二版PCB时特意加了这个下拉电阻网络稳定性立竿见影。如果你现在用的是现成模块检查一下模块原理图部分便宜的模块可能把这个引脚拉低处理了但也不排除有偷工减料的稳妥起见直接飞线补一个下拉电阻到模块的GND。2.2 PHYAD地址到底填0还是填1PHY地址这个坑新手几乎必踩。STM32的MDIO总线可以挂多个PHY通过PHY地址来区分。LAN8720A的PHY地址由PHYAD0引脚即RXER/PHYAD0脚的电平决定这个引脚内部下拉悬空时默认地址是0x01。但很多CubeMX生成的工程模板里ETH配置的PHY Address默认写的是0x00这就会导致MCU通过MDIO读写PHY寄存器时找不到设备表现出来就是PHY的ID寄存器读出来全是0xFFFF或者0x0000。我见过不少人卡在这一步反复检查焊接和引脚配置其实就是地址没对上。如果你的板子上LAN8720A的PHYAD0引脚悬空或者下拉确认地址填1。如果某些设计把PHYAD0上拉到高电平地址就会变成0x00那就填0。最好的办法是直接读寄存器2和寄存器3LAN8720A的固定ID是0x0007C0F1能读出来就说明地址对了。CubeMX里改好地址后生成代码中lwip.c文件里的ETH_PHY_ADDRESS也会同步更新。2.3 50MHz REF_CLK到底从哪来RMII接口的数据收发都依赖50MHz的REF_CLK时钟这个时钟的稳定性和相位直接影响收发是否正常。在F107LAN8720A的典型电路里主流做法是让LAN8720A工作在RMII主模式用25MHz晶振经过PHY内部PLL倍频产生50MHz然后从LAN8720A的REF_CLK/CLKOUT引脚输出给STM32F107的PA1。要注意的是这个时钟必须保持同源同步不能MCU这边用一个时钟、PHY那边用另一个时钟否则数据采样点对不上轻则丢包重则完全不通。另外有些教程喜欢用STM32的MCO引脚输出50MHz给PHY做参考时钟这个方案在部分F4芯片上可行但在F107上实现起来比较别扭我不推荐新手尝试。老老实实用25MHz晶振PHY主模式输出省心又可靠。3. CubeMX一步步配置从新建工程到生成代码3.1 时钟树先让STM32F107的MAC转起来打开STM32CubeMX新建工程选好STM32F107型号后第一件事是配置时钟。F107的以太网MAC时钟来自PLL的特殊输出必须配置成48MHz不然ETH外设无法正常工作。以常见的25MHz外部晶振为例时钟树可以这样配HSE选25MHzPLL源选HSE让CubeMX自动计算PLL参数。我实际用的一组参数是PLLM25、PLLN288、PLLP4、PLLQ6这样系统主频72MHzPLLCLK分频给以太网的时钟正好48MHz。如果你的板子用8MHz晶振参数会不同但只要确保配置界面里ETH/MAC时钟显示为48MHz就行。这里我踩过的坑是一开始只配置了系统主频72MHz没仔细看ETH时钟结果程序跑起来串口正常打印但MDIO完全读不到PHY寄存器。查了半天才发现是时钟树里ETH时钟源没配。所以时钟树配置完一定要检查ETH那项是不是合法值CubeMX里面非法的配置会用红色字体标出来绿色的勾才是合法值。3.2 开启ETH并设置RMII在Pinout视图左侧的Connectivity里找到ETH勾选RMII模式。选好后可以看到系统自动把前面表格里的那9个引脚复用成ETH功能。这里有个细节如果某个引脚你之前手动分配了其他功能CubeMX会标红冲突需要先解除冲突再回来选ETH否则生成的代码不完整。ETH配置页里保持默认参数基本就行但有一个必须改PHY Address填1。如果前面说的PHYAD0引脚设计不同这里对应调整。另外DMA描述符数量和缓冲区大小保持默认F107的RAM不大不要盲目加大缓冲区。PHY的Speed和Duplex设置也建议先保持自动协商AutoNegotiation。后面如果自动协商不正常再改成强制100M全双工。这里尤其要注意如果改成强制模式对端设备也必须固定到对应的速度双工否则链路虽然起来但数据传输全是CRC错误。3.3 配置LWIP中间件软件包管理器里打开LWIP版本按CubeMX默认即可。关键配置项主要是这几个Memory Size和Heap Size默认4096字节够用F107的RAM也就64KB不需要再增大。开启ICMP、UDP、TCP等必需协议默认全是开启的。DHCP服务默认关闭建议先关闭用静态IP调试减少变量。线程设置里默认信号量和队列配置不用动。如果你要用DHCP自动获取IP后面代码里需要调用dhcp_start()并且要处理IP分配完成后的回调。但调试初期还是强烈建议静态IP原因很简单你连不上板子的MAC地址时至少IP是确定的排查每一步都能复现。3.4 生成代码后的检查清单点击生成代码之后别急着编译下载先检查这几项一确认stm32f1xx_hal_conf.h里HAL_ETH_MODULE_ENABLED是开启的。二确认main.c里的MX_LWIP_Init()被调用了并且MX_LWIP_Process()在主循环里。三检查lwip.c里定义的IP地址是不是你想要的。四确认stm32f1xx_it.c里有ETH_IRQHandler的中断入口并且调用了HAL_ETH_IRQHandler。4. 代码接入LWIP与基本连通测试4.1 生成代码的结构ETH和LWIP初始化流程CubeMX生成的工程里网络相关的初始化流程是这样的main函数先做HAL初始化、时钟配置然后调用MX_GPIO_Init初始化引脚接着是MX_ETH_Init初始化MAC和DMA最后MX_LWIP_Init完成协议栈的初始化、网卡注册、设置静态IP。之后主循环里不停调用MX_LWIP_Process处理协议栈的各种定时任务和中断收包。理解这个流程很重要因为如果某个环节顺序不对比如PHY还没就绪就初始化LWIP后面就会出现各种奇怪现象。我习惯在MX_LWIP_Init之后加一段PHY健康检查代码确认PHY连接状态和自动协商结果如果PHY没就绪就在串口打印错误信息这样能快速定位问题。4.2 PHY复位与寄存器读取给代码这里贴一段我在工程里实际使用的PHY检查代码主要作用是复位PHY、开启自动协商、轮询连接状态同时读PHY ID做校验/* lan8720a.h */ #ifndef LAN8720A_H #define LAN8720A_H #define LAN8720A_PHY_ADDR 1 #define PHY_REG_BCR 0x00 #define PHY_REG_BSR 0x01 #define PHY_REG_IDR1 0x02 #define PHY_REG_IDR2 0x03 #define BCR_SOFT_RESET 0x8000 #define BCR_AUTONEGOTIATION 0x1000 #define BCR_SPEED_100M 0x2000 #define BCR_DUPLEX_FULL 0x0100 #define BSR_LINK_STATUS 0x0004 #define BSR_AUTONEGO_COMPLETE 0x0020 uint8_t LAN8720A_Check(ETH_HandleTypeDef *heth); #endif/* lan8720a.c */ #include lan8720a.h #include main.h #include cmsis_os.h uint8_t LAN8720A_Check(ETH_HandleTypeDef *heth) { uint16_t val 0; uint16_t id1 0, id2 0; uint8_t ok 1; if (HAL_ETH_ReadPHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_BCR, val) ! HAL_OK) { return 0; } if (HAL_ETH_ReadPHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_IDR1, id1) ! HAL_OK) { return 0; } if (HAL_ETH_ReadPHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_IDR2, id2) ! HAL_OK) { return 0; } if ((id1 ! 0x0007) || (id2 ! 0xC0F1)) { printf(LAN8720A ID error: 0x%04X 0x%04X\r\n, id1, id2); ok 0; } /* 软件复位等PHY完成启动 */ if (HAL_ETH_WritePHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_BCR, BCR_SOFT_RESET) ! HAL_OK) { return 0; } HAL_Delay(100); /* 开启自动协商100M全双工 */ val BCR_AUTONEGOTIATION | BCR_SPEED_100M | BCR_DUPLEX_FULL; if (HAL_ETH_WritePHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_BCR, val) ! HAL_OK) { return 0; } /* 等待自动协商和链路建立 */ for (int i 0; i 20; i) { HAL_ETH_ReadPHYRegister(heth, LAN8720A_PHY_ADDR, PHY_REG_BSR, val); if ((val BSR_AUTONEGO_COMPLETE) (val BSR_LINK_STATUS)) { printf(LAN8720A link up, auto negotiation done.\r\n); return ok; } HAL_Delay(50); } printf(LAN8720A link timeout, BSR0x%04X\r\n, val); return 0; }这段代码可以在main循环开始前调用比如int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_LWIP_Init(); LAN8720A_Check(heth); while (1) { MX_LWIP_Process(); } }如果串口能打印出link up的信息说明PHY层面已经通了接下来就是IP层面的调试。如果打印出timeout就按后面排查手册的内容逐项查。4.3 静态IP配置小端字节序说明CubeMX生成的lwip.c里静态IP是这样设置的static void MX_LWIP_Init(void) { ip_addr_t ipaddr; ip_addr_t netmask; ip_addr_t gw; IP_ADDR4(ipaddr, 192, 168, 1, 10); IP_ADDR4(netmask, 255, 255, 255, 0); IP_ADDR4(gw, 192, 168, 1, 1); }如果你的CubeMX版本生成的是类似ipaddr.addr 0x0A01A8C0这种写法看起来和IP地址反着这是小端字节序的原因。0x0A01A8C0在内存里按字节从小到大排列出来就是C0 A8 01 0A也就是192.168.1.10。我吃过这个亏当时把一个IP地址按照人类直觉写成了0xC0A8010A结果板子IP变成了192.168.1.10的反向怎么ping都不通。所以最稳妥的方式是直接用IP_ADDR4这个宏让编译器帮你处理字节序别再手动去算十六进制了。改完IP后电脑网卡设置成同网段地址比如192.168.1.2掩码255.255.255.0网关可不填。4.4 主循环里跑协议栈主循环里的MX_LWIP_Process()是LWIP的时基处理函数它负责处理超时重传、ARP老化、TCP定时器这些周期任务。实际项目中如果你的业务逻辑有耗时操作比如写Flash或者长延时注意这些操作会影响LWIP的实时性造成掉包或者延迟增大。更好的做法是让以太网接收依赖中断加信号量CubeMX生成的空工程已经实现了中断收包后通知LWIP线程的机制但F107的ETH中断是打进主循环的MX_LWIP_Process一起处理的。如果业务逻辑复杂建议把LWIP处理放到一个独立的高优先级任务里配合RTOS使用。不过那是后面的优化方向先保证能打通最基础的通路再逐步优化架构。4.5 电脑端连接与ping测试所有配置完成后用网线把板子和电脑直接连接电脑的网卡IP改到192.168.1.x网段然后打开命令提示符执行ping 192.168.1.10正常现象是延迟1到2毫秒返回应答。如果返回超时先执行arp -a看看ARP表里有没有192.168.1.10对应的MAC地址这个信息能帮你判断问题出在哪一层。ARP表里没有说明二层链路都有问题重点检查物理连接ARP表里有但ping不通问题大概率在IP层或者协议栈配置。5. 实战中踩过的坑和排查手册5.1 PHY ID全F或全0MDIO不通怎么查MDIO读不到PHY ID的排查顺序我建议固定成一套先看供电确认LAN8720A的各个电源引脚电压正常VDDCR引脚上有1.2V左右的电压再看复位确认复位脚没有被长时间拉低很多板子的PHY复位和MCU复位用同一个复位电路需要确认MCU复位释放后PHY也正常启动最后查地址和引脚配置确认CubeMX里PHY地址填的是1确认MDIO和MDC引脚复用正确。还有一个容易被忽略的点MDIO的上拉电阻。某些LAN8720A模块虽然引脚引出来了但MDIO线上没有上拉电阻只有一个开漏输出驱动这样MCU读数据时信号容易被干扰。我遇到过把MDIO信号线从模块飞线到MCU线一长通讯就不稳定加了4.7k上拉电阻到3.3V之后就正常了。5.2 Ping不通的第一现场排查顺序Ping不通是最常见的求助场景我的排查步骤固定如下第一步看网口状态灯。LAN8720A的LED引脚连接了两个指示灯Link/Activity灯应该常亮或闪烁。如果灯完全不亮基本是物理链路上的问题查网络变压器、RJ45插座、差分线布线。第二步读PHY寄存器命令检查BSR的bit2链路状态为1说明物理链路已经协商通了。第三步看ARP表有MAC条目说明二层通了继续查IP配置没有条目说明二层都没通回到第一步。第四步检查PC防火墙Windows系统要允许ICMP回显请求否则板子能收到ping但电脑收不到回包看起来像超时。5.3 周期性断连/丢包十有八九是REF_CLK干扰这个问题定位起来最折磨人现象是板子工作正常但每隔一段时间就会ping不通或者延迟忽高忽低尤其在线束比较乱的环境里更明显。排查了供电、地线、网线全都没问题最后发现就是第一节说的那个坑——LAN8720A第1脚nINT/REFCLK0悬空。如果你用的是现成模块检查模块背面PCB看看第1脚有没有连接到地。如果没有飞线拉一个10k电阻到GND现象基本立刻消失。这个坑我确认过好几次不是玄学datasheet里虽然没有明说必须下拉但参考电路图里这个引脚是接地的。5.4 网口灯不亮与网速只能10M的问题网口灯不亮常见原因是RJ45座引脚定义搞错。很多带变压器的RJ45座引脚编号从1到8但不同厂家的座子内部线序略有差异如果按标准568B线序接反了TXP/RXN对链路是起不来的。我试过两批RJ45座引脚定义看起来一样实际内部走线不同最后对照规格书才找到问题。网速只能协商到10M但确定网口是百兆的情况多半是时钟精度不够。RMII对50MHz参考时钟的频率精度有要求如果用了精度太差的晶振百兆模式误码率高对端设备会自动降速协商到10M。换上20ppm以内的晶振基本能解决。另外检查一下REF_CLK走线不要离其他高频信号太近保证信号完整性。5.5 经验速查表现象可能原因排查/解决PHY ID读不到/全FPHY地址配置错误、MDIO引脚问题、PHY没复位检查地址填1、引脚复用、复位时序网口灯不亮差分线接反、RJ45座脚序错、变压器异常对照规格书检查TXP/TXN和RXP/RXN周期性丢包断连LAN8720A第1脚悬空干扰第1脚下拉10k电阻到地只能协商到10MREF_CLK精度不足、信号干扰换高精度晶振检查时钟走线灯亮但ping不通IP配置、防火墙、LWIP线程未跑查看ARP表、检查静态IP字节序、确认主循环跑LWIP5.6 提速与稳定性优化建议网络打通之后如果要在实际项目中使用稳定性还需要加强。首先建议在PCB上严格按差分对规则给TXP/TXN和RXP/RXN布线保持100欧姆阻抗匹配长度差尽量控制在5mm以内。其次LAN8720A的电源引脚要多加去耦电容大电流瞬态不能靠一根长导线供电。第三如果需要长时间运行建议开启看门狗并周期性检测链路状态发现PHY断开超过一段时间就复位PHY重新初始化。F107的以太网吞吐量也值得说说。实测下来用LWIP跑TCP传输大概能到几MB/s的速率UDP丢失率很低但100Mbps线速是跑不满的。如果项目需要更高的吞吐可能需要考虑F4系列或者单独的网络协处理器但F107对于绝大多数物联网数据采集和控制场景已经足够了。6. 我个人的调试心得最后说一点体会。前几天有人问我F107都出了这么多年了还有人在用吗我的答案是便宜、稳定、资料多这套组合在中小批量的工业产品里仍然很能打。但前提是硬件设计一定要严谨尤其是LAN8720A那些看起来不起眼的外围电路一个下拉电阻能让你少熬夜排查好几天。如果让我重新做一个带以太网的板子我会先花半天把LAN8720A的参考电路一项一项核对清楚再动STM32这边的软件。CubeMX生成的代码哪怕是空工程只要硬件没问题改改IP就能通。反过来硬件有隐蔽的坑软件调得再好也是白搭。调试时也建议分阶段验证。不要一股脑把所有功能打开先保证MDIO能读到PHY ID再保证链路up最后才是IP和协议栈。每一层都有明确的验证手段出了问题就知道往哪查这个过程本身就很有价值。
返回列表