ARTICLE DETAIL

资讯详情

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

STM32CubeMX配置LAN8720以太网:RMII时钟与PHY地址避坑指南

STM32CubeMX配置LAN8720以太网:RMII时钟与PHY地址避坑指南 1. 为什么LAN8720在CubeMX里总是不通先搞清楚RMII和时钟的底层逻辑搞STM32以太网的人十个里有八个在LAN8720上翻过车。我自己第一次用STM32F407配LAN8720的时候CubeMX里点几下生成了代码烧进去ping不通示波器一打波形发现REF_CLK根本没有输出折腾了整整一个下午才发现是PHY地址和时钟源的问题。后来陆续用STM32F107、F429、H743配过LAN8720、YT8512、DP83848踩的坑多了慢慢总结出一些CubeMX图形化配置界面里“看不见”的细节。这篇内容主要面向正在用STM32CubeMX配置LAN8720以太网外设的嵌入式开发者尤其是用RMII接口、自己画板子或者用现成模块的朋友。我会把RMII时钟的来龙去脉、PHY地址的确定方法、CubeMX里那些容易漏掉的勾选项以及实际调试中怎么用示波器和寄存器读写来定位问题全部拆开讲清楚。不管你是第一次接触以太网还是已经调过几块板子但总有些莫名其妙的问题这些细节都值得过一遍。LAN8720是一颗10/100M自适应的以太网PHY芯片支持RMII和MII两种MAC-PHY接口。在STM32的生态里绝大多数人用的是RMII模式因为引脚少、接线简单。但RMII有个硬性要求MAC和PHY必须共享同一个50MHz参考时钟。这个50MHz时钟可以由外部晶振直接提供也可以由STM32的MCO引脚输出还可以由LAN8720自己产生。三种方案各有各的坑CubeMX里对应的配置项也不一样。很多人以为CubeMX里把ETH选上、引脚分配好就完事了实际上CubeMX只帮你生成了GPIO和ETH外设的初始化代码PHY地址、时钟源选择、RMII模式确认这些关键参数要么在CubeMX里藏得比较深要么需要你自己在代码里补。更麻烦的是CubeMX不同版本对这些选项的呈现方式还不一样有的版本默认值就是错的。下面我按实际调试中问题出现的频率从时钟方案开始一个一个拆。2. RMII的50MHz参考时钟三种方案的选择逻辑与CubeMX配置差异2.1 方案一外部有源晶振直接喂50MHz这是最省心也最不容易出问题的方案。板子上放一颗50MHz的有源晶振输出直接连到LAN8720的REF_CLK引脚第5脚和STM32的ETH_RMII_REF_CLK引脚PA1。两边同时收到同一个50MHz时钟MAC和PHY的时序天然对齐。这种方案在CubeMX里怎么配打开Connectivity - ETH在Parameter Settings里把PHY Interface改成RMII然后注意看下面有个“RMII Clock Source”或者类似的选项。不同版本的CubeMX这个选项位置不一样有的在ETH的Parameter Settings里有的在Clock Configuration页面。如果用的是外部晶振直接供时钟这个选项要选“External Clock”或者直接不勾选MCO输出。我实测过几块板子外部有源晶振方案的成功率最高基本上只要焊接没问题、晶振起振正常CubeMX生成代码后直接就能通。但要注意有源晶振的供电和使能引脚有些封装的有源晶振有个OE引脚悬空可能不输出必须拉高或者拉到指定电平。2.2 方案二STM32的MCO1输出50MHz给PHY这个方案在成本敏感的产品里很常见省掉一颗有源晶振。STM32F4系列的MCO1可以输出PLL分频后的时钟配置成50MHz输出到PA8然后PA8接到LAN8720的REF_CLK。CubeMX里的配置步骤稍微多一点。首先在Clock Configuration页面找到MCO1的时钟源选择一般选PLLCLK或者HSE。然后设置MCO1的分频系数让输出正好是50MHz。比如HSE是8MHzPLL配置好后PLLCLK是168MHzMCO1分频系数设成4168/442MHz不对。得仔细算让MCO1的输出精确等于50MHz。这里有个容易忽略的点MCO1的输出频率精度直接影响RMII通信的稳定性。50MHz允许的偏差一般在±50ppm以内用PLL分频出来的时钟如果PLL的参考晶振本身精度不够或者分频系数算出来不是整数累积误差可能导致通信不稳定。我遇到过一块板子MCO输出49.8MHzping通但丢包率很高换成外部晶振后立刻正常。在CubeMX的ETH配置里如果选MCO输出要把“RMII Clock Source”设成“MCO1”或者对应的选项。有些版本的CubeMX不会自动帮你配置MCO1需要你手动在Clock Configuration里使能。2.3 方案三LAN8720自己产生50MHz反喂给STM32LAN8720有一个REF_CLK输出模式可以在它的XTAL1/XTAL2引脚接一颗25MHz晶振然后内部PLL倍频到50MHz从REF_CLK引脚输出给STM32。这个方案省了50MHz有源晶振但多了一颗25MHz无源晶振。关键点在于LAN8720的REF_CLK引脚方向。在RMII模式下LAN8720的REF_CLK既可以作为输入接收外部50MHz也可以作为输出产生50MHz给MAC。这个方向由芯片内部的寄存器或者引脚配置决定。具体来说LAN8720的nINT/REFCLKO引脚第14脚和REGOFF引脚第20脚的电平组合决定了时钟模式。我查过LAN8720的数据手册当REGOFF拉低、nINT/REFCLKO配置为REFCLKO输出时LAN8720会从REF_CLK引脚输出50MHz。但这里有个坑很多现成的LAN8720模块比如那种十几块钱的蓝色小板默认是把REF_CLK配置成输入的也就是期待外部给它50MHz。如果你直接拿这种模块想让它输出时钟给STM32需要改板上的电阻或者跳线。CubeMX里对应的配置是ETH的RMII Clock Source选“PHY”或者“External PHY Clock”意思是时钟来自PHY。但CubeMX不会帮你配置LAN8720的寄存器那部分得自己写代码或者改硬件跳线。2.4 三种方案的对比与选型建议方案成本配置复杂度稳定性适用场景外部50MHz有源晶振高一颗有源晶振低最高产品原型、对稳定性要求高的场景STM32 MCO输出低中中成本敏感、STM32主频足够高的场景LAN8720自产时钟中一颗25MHz无源晶振高中高模块化设计、想省有源晶振的场景我个人建议如果是第一次调LAN8720直接用外部有源晶振方案先把通信跑通再考虑换方案省成本。因为时钟问题引起的故障现象很隐蔽有时候能ping通但丢包有时候干脆连不上排查起来非常耗时。3. PHY地址从0x00到0x1F你的LAN8720到底在哪3.1 PHY地址是怎么确定的LAN8720的PHY地址由PHYAD0引脚第10脚在芯片复位时的电平决定。这个引脚内部有下拉悬空或者拉低时地址是0x00拉高时地址是0x01。注意LAN8720只支持两个地址0和1。不像有些PHY芯片支持5位地址LAN8720的地址范围很窄。但实际使用中很多模块或者板子会把PHYAD0引脚通过电阻上拉或下拉还有的模块用LED引脚复用做地址配置。我见过一块板子PHYAD0通过一个10k电阻上拉到3.3V地址是0x01但CubeMX生成的代码里默认PHY地址是0x00结果HAL_ETH_Init一直返回错误。3.2 CubeMX里PHY地址在哪里配CubeMX的ETH配置页面里有一个“PHY Address”或者“PHY Address Value”的输入框。默认值通常是0。如果你不确定板子上PHYAD0的电平最稳妥的办法是先用0试不通再试1。但这里有个更隐蔽的问题有些LAN8720模块的PHYAD0引脚被LED电路复用了。比如LAN8720的LED1/PHYAD0是同一个引脚第10脚模块上如果接了LEDLED的限流电阻和LED本身会影响复位时的电平。这种情况下PHY地址可能不是你以为的那个值。我的做法是拿一块新板子或者新模块先用万用表量PHYAD0引脚在复位后的电平确定地址是0还是1再去CubeMX里填。如果量不到就写一段代码用HAL_ETH_ReadPHYRegister分别读地址0和地址1的PHYID寄存器地址0x02和0x03能读出0x0007和0xC0F1的就是LAN8720。3.3 代码里怎么验证PHY地址CubeMX生成的代码里PHY地址通常定义在ethernetif.c或者lan8720.c里也可能在main.h里有个宏定义。比如#define LAN8720_PHY_ADDRESS 0x00你可以写一个简单的测试函数在ETH初始化之前扫描所有可能的PHY地址uint32_t phy_id 0; for (uint8_t addr 0; addr 32; addr) { HAL_ETH_ReadPHYRegister(heth, addr, 0x02, phy_id); if ((phy_id 0xFFFF) ! 0xFFFF (phy_id 0xFFFF) ! 0x0000) { printf(Found PHY at address: 0x%02X, ID: 0x%08X\n, addr, phy_id); } }这段代码会把总线上所有能响应的PHY地址打出来。LAN8720的PHYID1寄存器0x02读出来应该是0x0007PHYID20x03是0xC0F1。如果你读出来全是0xFFFF说明MDIO总线有问题可能是MDIO引脚没配置对或者PHY没供电。3.4 一个真实的踩坑案例有一次我用一块STM32F407的开发板配LAN8720模块CubeMX里PHY地址填的0死活不通。用上面的扫描代码一跑发现地址1有响应。回头查原理图发现开发板上LAN8720的PHYAD0被一个电阻拉高了。改成1之后立刻通了。这个坑的教训是不要相信默认值一定要实测。4. CubeMX里那些默认值会坑你的选项从GPIO速度到RMII模式确认4.1 GPIO速度等级别用默认的LowCubeMX在配置ETH的RMII引脚时会自动把相关GPIO配成复用推挽输出。但默认的GPIO速度等级往往是Low或者Medium。对于RMII的50MHz时钟和数据线来说Low速度的翻转速率不够会导致信号边沿变缓眼图变差通信不稳定。正确的做法是把ETH相关的GPIO速度设成Very High。具体是哪些引脚以STM32F407为例PA1: ETH_RMII_REF_CLKPA2: ETH_MDIOPA7: ETH_RMII_CRS_DVPC1: ETH_MDCPC4: ETH_RMII_RXD0PC5: ETH_RMII_RXD1PB11: ETH_RMII_TX_ENPB12: ETH_RMII_TXD0PB13: ETH_RMII_TXD1在CubeMX的GPIO配置页面把这些引脚一个个找出来Maximum output speed改成Very High。这个操作很繁琐但很关键。我实测过不改速度的情况下短距离通信可能没问题但线缆稍微长一点或者电磁环境复杂一点丢包率就上去了。4.2 RMII模式确认别选成MIICubeMX的ETH配置里PHY Interface有两个选项MII和RMII。默认可能是MII。如果你用的是RMII接线这里必须改成RMII。改完之后下面的引脚分配会自动变化MII需要16根线RMII只需要9根。改完接口模式后一定要检查引脚分配页面确认所有RMII引脚都正确映射了。有时候CubeMX会因为引脚冲突自动把某个引脚分配到别的位置比如把REF_CLK从PA1挪到PA8这时候你要么改硬件接线要么在CubeMX里手动锁定引脚。4.3 自动协商与速度双工设置CubeMX的ETH配置里有个“Auto Negotiation”选项默认是Enable。这个选项让MAC和PHY自动协商速度和双工模式。对于LAN8720来说自动协商是支持的但有时候协商结果不对比如协商成10M半双工导致吞吐量上不去。如果你确定要跑100M全双工可以在CubeMX里把Auto Negotiation关掉手动设置Speed为100MDuplex Mode为Full Duplex。但这样做的风险是如果对端设备不支持100M全双工链路可能起不来。我的建议是先用自动协商通了之后再根据实际需求调整。4.4 中断和回调CubeMX不会帮你写的部分CubeMX生成的ETH初始化代码里中断优先级和使能需要你自己配。在NVIC配置页面把ETH全局中断使能优先级根据你的系统需求设置。一般来说以太网中断优先级不要设得太高避免影响其他实时任务。另外CubeMX不会自动生成PHY的中断处理代码。LAN8720的中断引脚nINT可以接到STM32的某个GPIO上用于检测链路状态变化。这部分需要你自己写GPIO外部中断的回调函数在回调里读PHY的状态寄存器判断链路是否up/down。5. 调试实战用示波器和寄存器读写定位问题5.1 第一步确认50MHz时钟有没有不管用什么方案第一步永远是拿示波器打REF_CLK引脚。探头接地要短最好用弹簧地针避免引入噪声。正常的50MHz时钟应该是干净的方波峰峰值3.3V左右上升时间在几纳秒以内。如果REF_CLK没有波形按以下顺序排查时钟源有没有输出外部晶振方案量晶振输出引脚MCO方案量PA8PHY自产方案量LAN8720的XTAL1引脚有没有25MHz。时钟路径上的电阻、电容有没有焊错有些板子在REF_CLK上串了33欧姆的匹配电阻如果电阻没焊或者焊错阻值信号可能到不了。LAN8720的供电和复位是否正常量VDD引脚应该是3.3VnRST引脚在复位后应该是高电平。5.2 第二步MDIO总线能不能读到PHYMDIO是管理接口用来读写PHY的寄存器。如果MDIO不通后面的一切都免谈。用示波器打MDC和MDIO引脚在ETH初始化的时候应该能看到MDC上有时钟脉冲MDIO上有数据翻转。如果MDC没有时钟检查CubeMX里MDC的引脚分配和GPIO配置。MDC是推挽输出速度建议设成Medium或者High。如果MDIO没有数据检查MDIO的上拉电阻RMII规范要求MDIO上拉1.5k到10k欧姆。用前面提到的扫描代码能读到PHYID就说明MDIO通了。读不到的话先确认PHY地址再确认MDIO引脚。5.3 第三步链路状态和自协商结果MDIO通了之后读LAN8720的BSR寄存器地址0x01bit 2是Link Statusbit 5是Auto-Negotiation Complete。如果Link Status是0说明网线没插好或者对端设备没通电。如果Auto-Negotiation Complete是0等一会儿再读自协商需要时间。读PHYSTS寄存器地址0x10可以看协商结果bit 2-4是速度指示bit 0是双工模式。LAN8720的寄存器定义在数据手册里有详细说明我一般会把关键寄存器的值打印出来对照手册看。5.4 第四步ping测试和丢包率统计如果链路起来了PHY寄存器也正常接下来就是ping测试。在PC上ping STM32的IP地址看能不能通。通了之后用ping -t连续ping几百个包看丢包率。正常的局域网环境丢包率应该是0。如果有丢包重点查时钟质量和GPIO速度。我遇到过一次丢包率5%左右的情况查了半天发现是MCO输出的50MHz时钟抖动太大。换成外部有源晶振后丢包率降到0。所以时钟质量对通信稳定性的影响是决定性的。5.5 常见问题速查表现象可能原因排查方法REF_CLK无波形时钟源未配置、晶振未起振、路径断示波器逐级测量MDIO读不到PHYPHY地址错、MDIO上拉缺失、引脚配置错扫描地址、量上拉电阻链路起不来网线问题、对端设备问题、自协商失败换网线、读BSR寄存器ping通但丢包时钟抖动、GPIO速度低、电磁干扰换时钟方案、改GPIO速度初始化卡死中断优先级冲突、HAL库版本问题检查NVIC、更新CubeMX固件包6. 几个CubeMX版本差异和固件包更新的坑6.1 CubeMX版本不同ETH配置界面不一样我用过CubeMX 5.x、6.0、6.5、6.10几个版本ETH的配置界面改动挺大的。早期版本里RMII Clock Source的选项藏得很深在Clock Configuration页面里要展开ETH的时钟树才能看到。新版本里直接在ETH的Parameter Settings里就有下拉框。如果你照着网上的教程配发现界面跟教程里不一样先确认CubeMX版本。我建议用6.5以上的版本对STM32F4和H7的ETH支持比较完善。6.2 固件包版本对ETH驱动的影响CubeMX生成代码时会用到一个STM32Cube MCU Package里面包含HAL库和ETH驱动。不同版本的固件包里HAL_ETH_Init的实现有差异。比如某些版本的HAL库在ETH初始化时对PHY地址的处理有bug会导致初始化失败。我遇到过F4系列固件包1.27.0版本里ETH驱动的一个问题HAL_ETH_Init里读PHYID的循环没有正确处理超时PHY没响应时会卡死。后来更新到1.28.0就修复了。所以如果遇到初始化卡死先检查固件包版本去ST官网看有没有更新。6.3 生成代码后的手动修改清单CubeMX生成的代码不是万能的以下几处通常需要手动改PHY地址宏定义改成实际值。GPIO速度等级改成Very High。如果用了MCO输出时钟确认Clock Configuration里MCO1使能且频率正确。在ethernetif.c里low_level_init函数中确认ETH初始化参数与硬件一致。如果需要静态IP在lwip.c或者ethernetif.c里改IP地址、网关、掩码。这些改动每次重新生成代码都会被覆盖所以建议把改动集中在一个单独的文件里或者用CubeMX的User Code区域保护起来。7. 个人经验调LAN8720最省时间的路径调了这么多块板子我总结出一条最省时间的路径先确保硬件没问题再用CubeMX最小配置生成代码然后按“时钟-MDIO-链路-ping”的顺序逐级验证。硬件上我最关注三件事50MHz时钟的质量、PHYAD0的电平、MDIO的上拉电阻。这三样确认了软件上基本不会有大问题。CubeMX配置里我最关注RMII模式、PHY地址、GPIO速度这三个选项。其他的用默认值一般都能跑。还有一个小技巧在main函数里ETH初始化之前加一段延时等PHY的电源稳定和内部复位完成。LAN8720的上电复位时间大概需要几十毫秒有些板子的电源上升慢不加延时可能初始化失败。我一般加HAL_Delay(100)再初始化ETH从来没出过问题。最后如果你用的是现成的LAN8720模块注意模块上的晶振和跳线配置。不同厂家的模块默认配置可能不一样有的默认外部50MHz输入有的默认自产时钟。买回来先用示波器和万用表确认一下比看卖家给的资料靠谱。
返回列表