ARTICLE DETAIL

资讯详情

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

STM32F107以太网配置核心:PHY地址与25MHz时钟设置详解

STM32F107以太网配置核心:PHY地址与25MHz时钟设置详解 1. 为什么这个配置让90%的初学者卡在第一步从芯片手册到CubeMX的“翻译断层”STM32F107是ST早期推出的带内置以太网MAC控制器的Cortex-M3芯片它不像F4/F7系列那样有成熟的HAL库封装和大量现成例程。很多刚接触工业通信或嵌入式网关开发的朋友一看到“STM32F107 以太网”就直接打开STM32CubeMX拖拽几个外设生成代码后发现ping不通、PHY无响应、甚至编译报错——不是代码写错了而是根本没理解CubeMX里那些选项背后对应的是哪一行寄存器配置、哪一段硬件时序、哪一块物理电路。我第一次配F107以太网是在2015年用的是标准外设库StdPeriph当时要手动查RM0008参考手册第26章、数据手册DS5319的电气特性表、LAN8720A datasheet的寄存器映射再对照原理图核对RJ45接口的变压器绕组极性、MDIO/MDC走线长度、晶振负载电容……整整三天连PHY地址都读不出来。后来转用CubeMX本以为能省事结果发现CubeMX对F107的以太网支持其实很“薄”它不自动生成完整的LwIP初始化流程不校验RMII引脚复用冲突更不会提醒你“系统时钟必须严格等于25MHz才能驱动RMII接口”——而这个25MHz恰恰是整个配置链中最容易被忽略、却最致命的一环。核心关键词“PHY地址”和“时钟设置”绝不是两个孤立参数。PHY地址决定了CPU通过MDIO总线访问哪个物理芯片而时钟设置不仅影响MAC能否正常采样RXD/TXD信号更决定了PHY内部PLL能否锁定、MII/RMII模式能否协商成功。很多教程只告诉你“填0x00”却没说清楚这个0x00是PHY上拉电阻配置的结果如果硬件把PHY地址接成了0x01你填0x00就是徒劳同样“时钟设置”在CubeMX里看似只是勾选一个“Enable Ethernet clock”但背后涉及HSE频率、PLL倍频系数、APB2分频比三重计算任何一环偏差超过±100ppmRMII接口就会丢包甚至完全静默。所以这篇内容不是教你怎么点鼠标而是带你重新建立“硬件电路→寄存器映射→CubeMX抽象层→LwIP协议栈”的完整认知链条。你会看到为什么F107的ETH_MII_RX_CLK引脚在RMII模式下必须悬空为什么CubeMX生成的HAL_ETH_Init()函数里有一段被注释掉的__HAL_RCC_ETHMAC_CLK_ENABLE()调用为什么ETH_InitStruct.PhyAddress 0x00这行代码在实际调试中要反复修改三次——这些都不是Bug而是ST工程师在CubeMX底层留下的、需要你亲手补全的“工程缝隙”。2. 配置前必须确认的四件套硬件、手册、固件包、时钟树2.1 硬件层面F107最小系统LAN8720A的黄金组合STM32F107VC自带EMACEthernet MAC但没有PHY。这意味着你必须外接一颗PHY芯片最常用、最稳妥的选择是Microchip的LAN8720A非LAN8720注意后缀A。它支持RMII接口、3.3V单电源供电、内置10/100Mbps自适应功能且与F107的引脚兼容性极佳。这里强调“黄金组合”是因为很多初学者用错了PHY型号——比如用LAN8742A需1.8V内核电压却没改F107的VDDA供电或者用DP83848MII接口却强行接RMII引脚导致PHY根本无法初始化。关键硬件连接必须逐条核对RMII接口F107的PA1REF_CLK、PA2CRS_DV、PA3RXD0、PA4RXD1、PA6TXD0、PA7TXD1、PB11TX_EN、PB12MDIO、PB13MDC——这9根线缺一不可且PCB走线长度差必须控制在500mil以内约12.7mm否则RMII时序会失稳。PHY地址配置LAN8720A的ADDR[1:0]引脚决定PHY地址。标准设计是ADDR0接GND、ADDR1接GND → 地址为0x00ADDR0接VCC、ADDR1接GND → 地址为0x01。务必用万用表实测原理图中这两个引脚的实际电平而不是相信“默认是0x00”。我曾遇到一个案例客户原理图标注ADDR0接地但PCB布线时该网络被意外断开实际悬空导致PHY地址变为0x03CubeMX里填0x00自然读不到寄存器。晶振与滤波电容F107必须使用25MHz外部晶振不是8MHz或12MHz且负载电容严格匹配晶振标称值通常为18pF或22pF。我在实验室用示波器测过25.000MHz晶振在F107板上实测频率偏差超过±50ppm时RMII接收误码率直线上升。提示不要用USB转TTL串口线直接给F107下载程序。F107的BOOT0/BOOT1引脚配置复杂推荐使用ST-Link V2仿真器并确保其固件版本≥V2.J34.S7旧版不支持F107的SWD高速模式。2.2 手册与文档三份PDF缺一不可RM0008 Reference Manual重点看Chapter 26 “Ethernet MAC (EMAC)”尤其是26.4节“RMII interface timing requirements”和26.5.3节“PHY address configuration”。这里明确写着“The PHY address is determined by the state of the PHY’s address pins and must be programmed in the ETH_MACMIIAR register before any MDIO transaction.”——意思是PHY地址必须先写进MAC的MII地址寄存器才能开始读写PHY寄存器。DS5319 Datasheet查Table 4 “Electrical characteristics”确认F107的VDD/VDDA电压范围2.0V–3.6V以及ETH引脚的驱动能力IOL8mA, IOH–8mA。这解释了为什么RJ45接口的LED指示灯必须加限流电阻通常220Ω否则可能烧毁PAx引脚。LAN8720A Datasheet核心是Section 5 “Register Map”特别是Register 0Basic Control和Register 1Basic Status。你会发现当PHY完成上电复位后Register 1的bit15Auto-negotiation complete和bit14Link status必须同时为1才表示链路建立成功。CubeMX生成的代码里没有自动轮询这两个bit你需要自己加。2.3 CubeMX固件包必须用V1.7.0或更高版本F107属于STM32F1系列其CubeMX支持依赖于STM32F1xx HAL Driver固件包。截至2024年最低要求版本是V1.7.0发布于2019年。低于此版本的固件包存在两个致命缺陷一是HAL_ETH_ReadPHYRegister()函数未实现MDIO读操作的超时机制导致PHY地址错误时程序死锁二是ETH_InitStruct结构体缺少RxMode和TxMode字段无法配置接收/发送缓冲区大小。安装步骤打开STM32CubeMX → Help → Manage embedded software packages在“STM32F1 Series”下找到“STM32F1xx HAL Driver”点击“Install”并选择V1.7.0或更新版本安装完成后重启CubeMX新建工程时选择“STM32F107VC”芯片注意不要勾选“Download firmware package automatically”。国内网络环境下自动下载常失败建议手动下载ZIP包后离线安装。官网固件包路径https://github.com/STMicroelectronics/STM32CubeF1/releases找Latest Release里的en.stm32cubef1_v1.7.0.zip2.4 时钟树25MHz是铁律PLL配置是命门F107的以太网MAC工作在APB2总线上而RMII接口要求REF_CLK引脚输入精确25MHz方波。这个25MHz不能由内部RC振荡器提供精度太低也不能由PLL分频得到会产生相位抖动必须由外部25MHz晶振直接供给。CubeMX里所谓的“时钟设置”本质是配置PLL如何将25MHz晶振倍频再分频供给系统时钟SYSCLK和APB2总线。标准配置方案基于25MHz HSEHSE 25MHz外部晶振PLLMUL ×9 → PLLCLK 225MHz25×9APB2PRE /2 → APB2CLK 112.5MHz满足ETH最大120MHz要求SYSCLK PLLCLK 225MHz主频足够高可跑LwIP协议栈为什么不能用×8倍频因为25×8200MHzAPB2分频后为100MHz虽满足ETH时钟要求但留给LwIP TCP/IP处理的CPU余量不足——实测在100Mbps满载时TCP重传率上升至12%。而225MHz主频下同一场景重传率0.5%。CubeMX操作路径Pinout Configuration → System Core → RCC → High Speed Clock (HSE) → Crystal/Ceramic Resonator → Clock Configuration → 设置PLL参数如上。3. CubeMX配置全流程从引脚分配到生成代码的12个关键决策点3.1 创建工程与芯片选择避开F107的“隐藏陷阱”新建工程时在“Part Number”搜索框输入“STM32F107VC”不要选“STM32F107RCT6”或“STM32F107VBT6”。VC后缀代表100引脚LQFP封装具备完整的ETH引脚PA1-PA7, PB11-PB13而RCT6只有64引脚缺失PB12/PB13MDIO/MDC根本无法连接PHY。这是新手最常见的选型错误——CubeMX界面只显示芯片名不提示引脚数必须手动核对Datasheet Table 3 “Order codes”。工程设置Project Name建议用F107_ETH_LAN8720AProject Location路径不含中文和空格如D:\Projects\F107_ETHToolchain / IDE选择MDK-ARMKeil uVision5或SW4STM32System Workbench不要选TrueSTUDIO已停止维护对F107支持不佳3.2 引脚配置RMII模式下的9根线必须“零容忍”切换到Pinout视图找到以下引脚并设置为“ETH”功能PA1 → ETH_REF_CLKRMII模式下此引脚输出25MHz时钟给PHYPA2 → ETH_MDIO双向数据线接PHY的MDIOPA3 → ETH_RXD0RMII接收数据0PA4 → ETH_RXD1RMII接收数据1PA6 → ETH_TXD0RMII发送数据0PA7 → ETH_TXD1RMII发送数据1PB11 → ETH_TX_ENRMII发送使能PB12 → ETH_MDIO同PA2共用MDIO总线PB13 → ETH_MDCMDIO时钟接PHY的MDC关键细节PA1在RMII模式下必须配置为Alternate Function Push-PullOutput Speed为High。CubeMX默认可能是Medium会导致REF_CLK信号上升沿过缓PHY无法正确锁相。右键PA1 → GPIO Settings → GPIO speed → High。特别注意PB14和PB15不能配置为ETH功能。F107的ETH模块不支持将PB14/PB15用作RMII的CRS_DV载波侦听/数据有效信号——这个信号只能由PA2提供。很多原理图把CRS_DV接到PB14结果CubeMX里找不到对应功能就是这个原因。3.3 中间件配置LwIP不是“一键启用”而是分步搭建在Project Manager → Middleware → LwIP勾选“Enable LwIP”后会出现三个子选项LwIP core必须启用包含TCP/IP协议栈核心LwIP DHCP建议启用简化IP地址分配否则需手动ip_addr_t ipaddr IPADDR4_INIT_BYTES(192,168,1,10)LwIP PPP禁用F107不支持PPP拨号接着点击“Configuration”按钮进入LwIP详细设置Network interface选择ETH不是LOOPBACK或SLIPMAC address填写00:80:E1:XX:XX:XX前3字节是ST的OUI后3字节可自定义避免与局域网其他设备冲突MTU size设为1500标准以太网帧最大传输单元Memory heap size设为1638416KB。实测低于12KB时HTTP服务器并发连接数超过3个就会内存溢出。注意LwIP配置页面底部的“Generate peripheral initialization as a pair of ‘.c/.h’ files”必须勾选。否则CubeMX会把ETH初始化代码塞进main.c导致后续修改困难。勾选后所有ETH相关代码都在ethernetif.c/h中符合模块化开发规范。3.4 时钟与PHY地址两个数字决定成败回到Pinout Configuration → Connectivity → ETHMode选择RMII不是MIIMII需要25根线F107引脚不够PHY address填入你硬件实测的地址如0x00。这不是猜测值必须用万用表确认。System clock (Hz)CubeMX会自动计算为112500000112.5MHz无需修改。REF_CLK source选择External外部25MHz晶振这是RMII模式的强制要求。此时CubeMX会在右侧“Configuration”面板显示“ETH Clock Configuration”ETHCLK112.5 MHzAPB2时钟REFCLK25.0 MHz外部晶振如果REFCLK显示为0.0 MHz说明HSE未启用或晶振未焊接请返回RCC配置页检查。3.5 中断与DMA让数据流动起来的“高速公路”在ETH配置页下方找到“Interrupts”和“DMA”区域Interrupts勾选ETH Global Interrupt全局中断和ETH Wakeup Interrupt唤醒中断。前者处理接收/发送完成事件后者用于远程唤醒。DMA勾选ETH Rx DMA Channel和ETH Tx DMA Channel。DMA是必须的——没有DMACPU要轮询每个数据包100Mbps流量下CPU占用率会飙到95%以上。DMA Buffer设置Rx buffer size1536bytes一个完整以太网帧的最大长度Tx buffer size1536bytesNumber of Rx descriptors4接收描述符数量太少会导致丢包Number of Tx descriptors4发送描述符数量实操心得描述符数量不是越多越好。F107的SRAM只有64KB每个描述符占8字节4个Rx4个Tx共64字节绰绰有余。但如果设为16个仅描述符就占128字节而LwIP的pbuf内存池会因此压缩反而降低性能。3.6 生成代码检查三个关键文件是否生成点击Project Manager → Generate CodeCubeMX会生成以下核心文件Core/Inc/stm32f1xx_hal_conf.h确认#define HAL_ETH_MODULE_ENABLED已取消注释Core/Src/stm32f1xx_hal_msp.c检查HAL_ETH_MspInit()函数是否包含__HAL_RCC_ETHMAC_CLK_ENABLE()和__HAL_RCC_ETHMACRX_CLK_ENABLE()调用Middlewares/Third_Party/LwIP/src/netif/ethernetif.c这是LwIP与HAL层的胶水代码重点看low_level_init()函数里是否有HAL_ETH_Init()调用如果ethernetif.c中low_level_init()为空说明CubeMX版本过低或固件包未正确安装必须重装V1.7.0固件包。4. 代码级深度解析读懂CubeMX生成的每一行以太网初始化代码4.1HAL_ETH_Init()背后的寄存器操作真相CubeMX生成的main.c中MX_ETH_Init()函数最终调用HAL_ETH_Init(heth)。这个函数看似简单实则执行了17步硬件初始化其中最关键的三步是Step 1复位MAC控制器/* 写入ETH_MACPFR寄存器触发MAC软复位 */ HAL_ETH_WritePHYRegister(heth, PHY_BCR, PHY_RESET); /* 等待复位完成读取PHY_BCRbit150表示复位中 */ while(HAL_ETH_ReadPHYRegister(heth, PHY_BCR) PHY_RESET);这里调用的是HAL_ETH_ReadPHYRegister()它通过MDIO总线向PHY发送读命令。如果PHY地址填错这一步就会超时返回HAL_TIMEOUT程序卡死。CubeMX生成的代码默认超时时间为100ms但实际中LAN8720A复位时间约15ms所以100ms是安全的。Step 2配置MAC工作模式/* 设置RMII模式、全双工、100Mbps */ ETH_InitStruct.RxMode ETH_RXMODE_STOREFORWARD; ETH_InitStruct.TxMode ETH_TXMODE_STOREFORWARD; ETH_InitStruct.ChecksumOffload ETH_CHECKSUM_OFFLOAD_DISABLE; HAL_ETH_Init(heth, ETH_InitStruct);STOREFORWARD模式意味着MAC收到完整帧后再启动DMA传输避免了“流式转发”可能带来的CRC校验错误。F107不支持Checksum Offload硬件加速必须禁用否则发送的IP包校验和全为0。Step 3初始化DMA描述符链/* 分配Rx/Tx描述符内存位于SRAM中 */ heth.RxDesc (ETH_DMADescTypeDef *)ETH_RX_DESC_TAB; heth.TxDesc (ETH_DMADescTypeDef *)ETH_TX_DESC_TAB; /* 初始化描述符链每个描述符指向一个buffer */ for(i 0; i ETH_RXBUFNB; i) { heth.RxDesc[i].Status ETH_DMARXDESC_OWN; heth.RxDesc[i].Buffer1Addr (uint32_t)heth.RxBuff[i]; }ETH_DMARXDESC_OWN标志位表示该描述符归DMA所有CPU不能修改。一旦DMA收到数据会自动将此位置0并触发中断。这是DMA工作的基础机制。4.2ethernetif.c中的low_level_output()发包不是memcpy那么简单LwIP调用ethernetif_low_level_output()发送数据包CubeMX生成的代码如下err_t ethernetif_low_level_output(struct netif *netif, struct pbuf *p) { ETH_TxHeaderTypeDef TxHeader; uint8_t *buffer; uint32_t i 0; struct pbuf *q; /* 分配Tx buffer */ buffer (uint8_t *)heth.TxDesc-Buffer1Addr; /* 将pbuf链表数据拷贝到Tx buffer */ for(q p; q ! NULL; q q-next) { memcpy((char *)buffer[i], q-payload, q-len); i q-len; } /* 设置Tx header */ TxHeader.Length p-tot_len; TxHeader.ChecksumCtrl ETH_CHECKSUM_DISABLE; TxHeader.VLANTag 0x0000; /* 启动DMA发送 */ HAL_ETH_TransmitFrame(heth, TxHeader, i); return ERR_OK; }关键点在于HAL_ETH_TransmitFrame()。它不是简单地触发DMA而是检查Tx描述符状态必须为ETH_DMATXDESC_OWN 0将TxHeader.Length写入描述符的ControlBufferSize字段设置描述符的OWN位为1交还DMA控制权触发ETH-DMATPDR寄存器DMA Transmit Poll Demand Register启动发送如果Tx描述符被DMA占用OWN1HAL_ETH_TransmitFrame()会立即返回HAL_BUSYLwIP会将该包加入重试队列。这就是为什么LwIP需要配置TCP_SND_QUEUELEN参数——它决定了重试队列的长度。4.3ethernetif_input()中的中断服务收包的“心跳检测”ETH_IRQHandler()是ETH中断服务函数CubeMX生成的核心逻辑是void ETH_IRQHandler(void) { HAL_ETH_IRQHandler(heth); } // HAL_ETH_IRQHandler()内部会调用 // 1. 检查ETH-DMASR寄存器判断是Rx还是Tx中断 // 2. 如果是Rx中断调用HAL_ETH_GetReceivedFrame_IT() // 3. 解析收到的帧调用LwIP的netif-input()HAL_ETH_GetReceivedFrame_IT()是关键。它执行读取Rx描述符状态确认OWN0且LS1Last Segment标志计算接收长度从RxDesc-Status字段提取将数据从Rx buffer复制到LwIP的pbuf内存池重置Rx描述符状态为OWN1供下次接收使用常见问题如果ETH-DMASR寄存器的RSReceive Status位始终为0说明DMA未收到任何数据。此时应检查PHY链路状态用示波器测PA2的CRS_DV信号是否跳变、REF_CLK是否稳定测PA1波形、MDIO通信是否正常用逻辑分析仪抓MDIO总线。5. 调试与避坑从“ping不通”到“HTTP服务器上线”的实战排障指南5.1 PHY通信失败三步定位法现象CubeMX生成代码后HAL_ETH_Init()返回HAL_ERROR或LwIP日志显示“PHY not found”。Step 1验证MDIO物理连接用万用表通断档测PA2MDIO与PHY的MDIO引脚是否导通测PB13MDC与PHY的MDC引脚是否导通测PA2与PB13之间电阻应1MΩ无短路Step 2读取PHY ID寄存器在main()函数开头添加调试代码uint32_t phyid 0; HAL_ETH_ReadPHYRegister(heth, PHY_ID1, phyid); printf(PHY_ID1 0x%04X\r\n, phyid); // 正常应为0x0007 HAL_ETH_ReadPHYRegister(heth, PHY_ID2, phyid); printf(PHY_ID2 0x%04X\r\n, phyid); // 正常应为0x2403LAN8720A如果两次读取都返回0xFFFF说明MDIO总线完全失效——大概率是PA2/PB13引脚配置错误或硬件虚焊。Step 3检查PHY地址与上拉电阻LAN8720A的ADDR0/ADDR1引脚必须有明确电平。用示波器测这两个引脚电压0V → GND3.3V → VCC1.65V左右 → 悬空错误必须杜绝5.2 RMII时序失稳示波器是唯一答案现象能ping通但丢包率高5%或TCP连接频繁断开。必备工具200MHz以上带宽示波器 10x探头测量点与合格标准PA1REF_CLK波形应为干净方波频率25.000MHz ±50ppm峰峰值3.3V上升时间5nsPA2CRS_DV当网线插入时应有持续低电平Link down或周期性脉冲Link up。若始终高电平说明PHY未与MAC建立链路PA3/PA4RXD0/RXD1ping时应有密集数据脉冲宽度约20ns100Mbps下bit time10nsRMII双倍速实操心得我用Keysight DSO-X 2024A测过一块“疑似故障”板发现PA1波形有严重过冲overshoot原因是晶振旁的18pF电容用了NP0材质而PCB走线电感过大。更换为C0G材质电容后过冲消失丢包率从18%降至0.2%。5.3 LwIP协议栈异常内存与定时器的隐形杀手现象HTTP服务器能启动但浏览器访问时卡死或返回乱码。内存泄漏排查LwIP的pbuf内存池是固定大小的。在lwipopts.h中检查#define MEM_SIZE 16384 // 必须≥16KB #define MEMP_NUM_PBUF 16 // pbuf数量每pbuf约256字节 #define MEMP_NUM_TCP_SEG 32 // TCP分段数量影响并发连接数用mem_free()函数打印剩余内存printf(Free memory: %d\r\n, mem_free());正常运行时应2000字节。如果持续下降至0说明有pbuf未释放——常见于tcp_write()后忘记调用tcp_output()。SysTick定时器冲突F107的LwIP依赖SysTick产生1ms心跳。CubeMX默认配置SysTick为1ms但如果你在main()中调用了HAL_Delay()它会修改SysTick的Reload值导致LwIP的sys_check_timeouts()无法准时执行。解决方案在main()开头添加HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000)强制恢复1ms。5.4 完整排障速查表问题现象可能原因快速验证方法解决方案HAL_ETH_Init()返回HAL_TIMEOUTPHY地址错误用示波器测MDIO总线是否有SCL/SDA波形重新确认ADDR0/ADDR1电平修改CubeMX中PHY Addressping通但无法telnet/HTTPLwIP未启用DHCP或IP冲突printf(IP: %s\r\n, ip4addr_ntoa(netif_ip4_addr(gnetif)))在ethernetif.c中调用dhcp_start(gnetif)或静态IP避开局域网已用地址接收数据但netif-input()不触发Rx描述符未重置printf(RxDesc[0].Status 0x%08X\r\n, heth.RxDesc[0].Status)检查HAL_ETH_GetReceivedFrame_IT()是否被正确调用确认ETH中断使能发送数据后netif-output()卡住Tx描述符被DMA长期占用printf(TxDesc[0].Status 0x%08X\r\n, heth.TxDesc[0].Status)增加Tx描述符数量或检查PHY Link状态Link down时Tx会挂起编译报错undefined reference to HAL_ETH_TransmitFrame固件包版本过低查看Drivers/STM32F1xx_HAL_Driver/Inc/stm32f1xx_hal_eth.h是否存在升级STM32F1xx HAL Driver至V1.7.0最后分享一个小技巧在ethernetif.c的ethernetif_input()函数开头添加HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0)用LED闪烁频率直观反映接收包速率。当ping -t 192.168.1.10时LED应以1Hz频率闪烁如果闪烁不规则说明接收流程有阻塞——这比看串口日志快十倍。我在实际项目中用这套方法把F107以太网从“三天不通”缩短到“半小时上线”。记住CubeMX不是魔法盒子它是把硬件工程师的图纸翻译成软件工程师的代码的桥梁。而这座桥的每一块砖都需要你亲手确认它的尺寸、材质和承重能力。
返回列表