ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

STM32F429以太网外设深度解析:MAC、PHY与RMII接口实战指南

STM32F429以太网外设深度解析:MAC、PHY与RMII接口实战指南 1. 从零吃透 STM32F429 以太网外设为什么它值得单独花时间啃STM32F429IGT6 这颗片子做以太网通信很多人第一反应是直接上 LwIP 不就行了但真到调试阶段十有八九会卡在底层——PHY 不握手、RMII 时钟不对、DMA 描述符跑飞、ping 不通还找不到原因。我自己在几个工业网关和边缘采集项目里反复用过这套组合踩过的坑足够写满一个笔记本。这篇笔记就专门聊以太网外设本身也就是 MAC 层和 PHY 层之间的那点事把 LwIP 之前的地基打扎实。先说清楚这篇内容适合谁看。如果你手上正好有一块 STM32F429IGT6 的开发板想跑通以太网通信但对着参考手册里那一堆 MAC 寄存器、DMA 描述符、RMII 引脚复用发懵那这篇就是给你写的。如果你已经能 ping 通但不知道数据是怎么从网线进到内存的也建议往下看理解底层之后调 LwIP 会顺很多。核心关键词就三个STM32F429IGT6、以太网外设、RMII 接口外加一个绕不开的LwIP作为最终目标。STM32F429 的以太网外设严格来说叫Ethernet MAC它符合 IEEE 802.3 标准内部集成了 MAC 控制器和 DMA 控制器但不含 PHY。PHY 是外部芯片常见的是 LAN8720A、DP83848 这类。MAC 和 PHY 之间通过RMII或MII接口连接。F429 支持这两种但实际项目里几乎都用 RMII因为引脚少一半PCB 布线压力小很多。这个选择背后的逻辑后面会详细展开。我见过太多人一上来就抄 LwIP 的例程结果 PHY 地址填错、时钟没配、复位引脚没拉折腾好几天。所以这篇笔记的定位很明确把以太网外设从硬件连接到寄存器配置再到数据收发链路一层一层拆开讲透。等你理解了 MAC 怎么通过 DMA 搬数据、PHY 怎么通过 SMI 配置、RMII 的 50MHz 时钟从哪来再去接 LwIP 就是水到渠成的事。下面我会按整体设计思路 → 核心细节 → 实操配置 → 问题排查这条线走中间穿插我自己项目里真实踩过的坑和验证过的参数。所有代码和配置都基于 STM32F429IGT6 LAN8720A 这个最常见的组合你可以直接对照着改。2. 以太网通信整体设计与方案选型拆解2.1 为什么是 MAC 外部 PHY 的架构STM32F429 没有把 PHY 集成进去这不是偷懒而是成本和灵活性的权衡。PHY 涉及模拟电路对工艺要求高集成进 MCU 会推高成本和功耗。分离设计的好处是你可以根据项目需求选不同等级的 PHY消费级用便宜的 LAN8720A工业级用带宽温的 DP83848需要光纤的还能换其他方案。MAC 部分负责数字逻辑PHY 负责模拟信号和线路编码两者通过标准接口通信各司其职。这个架构里MAC 是大脑PHY 是嘴巴和耳朵。MAC 按照 802.3 格式组装帧、计算 CRC、管理流控PHY 负责把数字信号变成差分模拟信号发到网线上同时把收到的信号还原成数字。两者之间的 RMII 接口就是神经传输数据和时钟。理解这个分工很重要因为调试时你要能判断问题出在哪一层。ping 不通可能是 MAC 配置错也可能是 PHY 没握手还可能是 RMII 时钟偏了。分层排查能省大量时间。2.2 RMII 与 MII 的取舍引脚、时钟与布线MII 需要 16 根信号线RMII 只要 9 根。对于 F429 这种 176 脚的片子引脚虽然够但 PCB 上少走 7 根线意味着更少的过孔、更短的走线、更好的信号完整性。RMII 的代价是时钟要求更严格MII 的收发时钟是分开的TX_CLK 25MHz、RX_CLK 25MHzRMII 则统一用 50MHz 参考时钟收发共用。RMII 的 50MHz 时钟来源有两种一是外部 PHY 提供二是 MCU 自己产生。LAN8720A 可以从外部晶振产生 50MHz 输出给 MCU也可以接收 MCU 提供的 50MHz。我一般推荐由 PHY 提供 50MHz 给 MCU因为 PHY 那边通常已经有一个 25MHz 晶振内部 PLL 倍频到 50MHz 很方便而且这样 MCU 侧不用额外配置时钟输出减少一个潜在故障点。注意RMII 的 50MHz 时钟必须非常干净抖动要小。我遇到过因为时钟走线太长导致丢包的情况后来把时钟线缩短并加串阻才稳定。这个后面排查章节会细说。2.3 DMA 描述符机制数据怎么从网线进到内存以太网外设最核心也最容易让人懵的就是DMA 描述符。你可以把它理解成一串快递单每个描述符记录了一个数据缓冲区在哪、多大、属于哪个帧、有没有错误。MAC 收到数据后DMA 自动把数据写进描述符指向的缓冲区然后更新描述符状态最后触发中断告诉 CPU有活了。发送方向反过来CPU 把要发的数据放进缓冲区填好描述符置位拥有权归 DMADMA 就会自动把数据发出去。这个拥有权OWN 位是理解描述符的关键——OWN1 表示 DMA 拥有CPU 不能碰OWN0 表示 CPU 拥有DMA 不能碰。搞错这个就会出现数据被覆盖或者发不出去的诡异问题。F429 的以太网 DMA 支持环形描述符链收发各一条链。每条链的描述符数量可以配置我一般收发各用 4 到 8 个。太少容易丢包太多浪费内存。这个数量要根据你的数据吞吐量和中断处理速度来权衡后面实操部分会给具体计算。3. 核心细节解析与实操要点3.1 PHY 地址与 SMI 接口怎么跟 PHY 对话MAC 要配置 PHY靠的是SMIStation Management Interface也就是 MDC 和 MDIO 两根线。MDC 是时钟MDIO 是双向数据。通过 SMIMAC 可以读写 PHY 内部的寄存器比如控制寄存器、状态寄存器、PHY 标识寄存器。每个 PHY 有一个地址范围 0 到 31。LAN8720A 的地址由 PHYAD0 引脚决定接地下拉就是地址 0接上拉就是地址 1。很多开发板默认下拉所以地址是 0。但如果你用的是自己画的板子一定要确认这个引脚的状态否则 MAC 找不到 PHY表现为读 PHY ID 全是 0 或 0xFFFF。读 PHY 标识寄存器地址 2 和 3是验证 SMI 通不通的最快方法。LAN8720A 的 ID 应该是 0x0007 和 0xC0F1具体值看数据手册。如果读出来不对先查 MDC 时钟是否在 1.2 到 2.5MHz 之间再查 MDIO 上拉电阻通常 1.5k 到 10k最后查 PHY 地址。实操心得MDC 时钟不能太快LAN8720A 最高支持 2.5MHz。F429 的 MAC 时钟是 25MHzRMII 模式下通过分频得到 MDC。我一般设分频系数为 16得到约 1.56MHz稳定可靠。3.2 RMII 引脚映射与 GPIO 配置F429 的以太网引脚是固定的不能随便映射。RMII 模式下需要配置的引脚包括信号方向F429 引脚说明REF_CLK输入PA150MHz 参考时钟MDIO双向PA2SMI 数据MDC输出PC1SMI 时钟CRS_DV输入PA7载波侦听/数据有效RXD0输入PC4接收数据位0RXD1输入PC5接收数据位1TX_EN输出PB11发送使能TXD0输出PB12发送数据位0TXD1输出PB13发送数据位1这些引脚要配置成复用推挽输出或浮空输入速度等级建议设为Very High因为 RMII 是 50MHz 信号。GPIO 的复用功能编号要查数据手册的复用表F429 上以太网用的是 AF11。这里有个容易忽略的点PA1 作为 REF_CLK 输入时要配置成浮空输入或者复用输入不能配成输出。我见过有人把它配成推挽输出结果时钟冲突PHY 直接不工作。3.3 MAC 配置寄存器帧过滤、双工与速率MAC 有一堆配置寄存器但实际项目里必须配的就那么几个。MACCRMAC Configuration Register是最关键的RE 位接收使能必须置 1TE 位发送使能必须置 1DM 位双工模式1 为全双工0 为半双工FES 位速率1 为 100Mbps0 为 10MbpsBL 位退避限制半双工时用全双工不用管双工和速率最好跟 PHY 自协商结果保持一致。如果你强制 MAC 全双工 100M但 PHY 协商成半双工 10M就会出现大量冲突和丢包。我一般让 PHY 自协商然后读 PHY 的状态寄存器把结果同步到 MACCR。MACFFRFrame Filter Register控制帧过滤。默认情况下MAC 会接收所有广播帧和发给自己 MAC 地址的帧。如果你要做网络抓包或者特殊协议可能需要打开混杂模式PM 位接收所有帧。但正常通信时不要开会增加 CPU 负担。3.4 DMA 描述符的初始化与对齐要求描述符的结构在参考手册里有明确定义每个描述符 16 字节包含状态、控制、缓冲区地址、下一描述符地址等字段。初始化时要特别注意内存对齐描述符必须 4 字节对齐缓冲区建议 4 字节对齐虽然不强制但对齐后 DMA 效率更高。我一般这样组织内存// 描述符必须4字节对齐 __attribute__((aligned(4))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB]; __attribute__((aligned(4))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB]; // 缓冲区建议4字节对齐大小要能容纳最大帧 __attribute__((aligned(4))) uint8_t Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE]; __attribute__((aligned(4))) uint8_t Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE];ETH_MAX_PACKET_SIZE通常定义为 1524 或 1536要能容纳最大的以太网帧1518 字节加上可能的 VLAN 标签。ETH_RXBUFNB和ETH_TXBUFNB我一般设 4 到 8。注意描述符和缓冲区不要放在栈上也不要用局部变量。它们必须在整个通信过程中持续有效所以要用全局变量或静态分配。我见过有人把描述符定义在函数里函数返回后内存被回收DMA 直接跑飞。3.5 中断配置与优先级分组以太网外设有多个中断源接收完成、发送完成、接收溢出、总线错误等。我一般只开接收中断和错误中断发送用轮询或者发送完成中断。接收中断优先级要设得比较高但不能高过系统关键中断比如 SysTick。F429 的中断优先级分组建议用NVIC_PRIORITYGROUP_4全部 4 位用于抢占优先级。以太网中断抢占优先级设 5 左右既保证及时响应又不会打断更关键的任务。中断服务函数里要做的事很简单检查中断标志如果是接收中断就通知上层有数据如果是错误中断记录错误类型并清除标志。不要在中断里做耗时操作比如解析协议、拷贝大量数据这些留给主循环或任务去做。4. 实操过程与核心环节实现4.1 硬件连接检查清单在写代码之前先确认硬件没问题。我整理了一个检查清单每次新板子都过一遍PHY 供电LAN8720A 需要 3.3V确认电压稳定去耦电容到位晶振LAN8720A 需要 25MHz 晶振确认起振用示波器看波形复位引脚PHY 的 nRST 引脚要由 MCU 控制或上电复位确保 PHY 正常初始化RMII 连线对照前面表格逐根检查特别是 REF_CLK 和 CRS_DV网口变压器RJ45 和 PHY 之间要有网络变压器不能直连PHY 地址引脚确认 PHYAD0 状态确定 PHY 地址这一步看起来简单但我统计过至少三成的以太网问题出在硬件。软件调半天最后发现是晶振没起振或者变压器虚焊这种亏吃一次就够了。4.2 时钟树配置50MHz 从哪来RMII 需要 50MHz 参考时钟。如果由 PHY 提供MCU 侧要把 PA1 配置成浮空输入同时使能以太网时钟。如果由 MCU 提供需要配置 MCO 输出 50MHz但这会占用一个引脚而且 MCO 的时钟源要选 PLL。我推荐 PHY 提供方案。LAN8720A 的 REF_CLK 引脚可以输出 50MHz直接连到 PA1。这样 MCU 侧只需要在 RCC 里使能ETHMAC 时钟和ETHMACTX/RX 时钟不用额外配置。RCC 配置代码大概这样// 使能以太网时钟 RCC-AHB1ENR | RCC_AHB1ENR_ETHMACEN; RCC-AHB1ENR | RCC_AHB1ENR_ETHMACTXEN; RCC-AHB1ENR | RCC_AHB1ENR_ETHMACRXEN; RCC-AHB1ENR | RCC_AHB1ENR_ETHMACPTPEN; // 使能 GPIO 时钟 RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN; RCC-AHB1ENR | RCC_AHB1ENR_GPIOBEN; RCC-AHB1ENR | RCC_AHB1ENR_GPIOCEN;注意ETHMACPTPEN 是时间戳时钟如果不用 PTP 可以不使能但使能了也不影响。4.3 PHY 初始化与自协商流程PHY 初始化分几步软复位、等待复位完成、配置自协商、等待自协商完成、读取结果。软复位是写BCR 寄存器地址 0的 bit15然后轮询这个位直到清零。LAN8720A 复位时间大概几毫秒不要用固定延时要轮询。自协商配置是写BCR 的 bit12ANEN和 bit9RESTART_AN。然后轮询BSR 寄存器地址 1的 bit5ANCOMPLETE直到置 1。自协商完成后读BSR 的 bit2 和 bit3判断速率和双工或者读 LAN8720A 的特殊状态寄存器地址 31获取更详细的信息。// 软复位 PHY ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, PHY_RESET); while (ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BCR) PHY_RESET); // 配置自协商 ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, PHY_AUTONEGOTIATION); ETH_WritePHYRegister(PHY_ADDRESS, PHY_BCR, PHY_AUTONEGOTIATION | PHY_RESTART_AUTONEGOTIATION); // 等待自协商完成 while (!(ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR) PHY_AUTONEGO_COMPLETE)); // 读取结果 uint16_t phy_status ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR); uint16_t speed (phy_status PHY_SPEED_STATUS) ? 100 : 10; uint16_t duplex (phy_status PHY_DUPLEX_STATUS) ? FULL : HALF;这段代码里PHY_ADDRESS要根据实际硬件填 0 或 1。PHY_SPEED_STATUS和PHY_DUPLEX_STATUS是位掩码具体值查 LAN8720A 数据手册。4.4 MAC 与 DMA 初始化寄存器级配置MAC 初始化主要是配 MACCR、MACFFR、MACMIIAR 等寄存器。MACMIIAR 控制 SMI 时钟分频和读写操作前面说的分频系数 16 就是配在这里。DMA 初始化更复杂一些要配DMABMR总线模式寄存器、DMATDLAR发送描述符列表地址、DMARDLAR接收描述符列表地址、DMAOMR操作模式寄存器等。DMABMR 里要设AAB地址对齐突发、PBL可编程突发长度、RDP接收描述符跳过长度等。我一般用默认值只改FB固定突发和PBL。PBL 设 32 比较通用。DMAOMR 里要设SR启动接收、ST启动发送、OSF操作模式存储转发、TSF发送存储转发、RSF接收存储转发。存储转发模式能减少 FIFO 溢出但会增加一点延迟。我一般全开。// 配置 DMA 总线模式 ETH-DMABMR | ETH_DMABMR_AAB | ETH_DMABMR_PBL_32Beat; // 配置发送和接收描述符列表地址 ETH-DMATDLAR (uint32_t)DMATxDscrTab; ETH-DMARDLAR (uint32_t)DMARxDscrTab; // 配置操作模式 ETH-DMAOMR | ETH_DMAOMR_TSF | ETH_DMAOMR_RSF | ETH_DMAOMR_OSF; ETH-DMAOMR | ETH_DMAOMR_ST | ETH_DMAOMR_SR;4.5 数据收发链路验证从 ping 到实际通信配置完成后先别急着上 LwIP。我建议先写一个最简单的收发测试构造一个 ARP 请求帧发出去看能不能收到回复。如果能收到说明 MAC、PHY、DMA 链路全通了。构造 ARP 请求帧要注意目标 MAC 是广播地址 FF:FF:FF:FF:FF:FF源 MAC 是你自己设的以太网类型是 0x0806后面跟 ARP 报文。发送时把帧放进 Tx 缓冲区填好描述符置位 OWN然后启动发送。接收方向轮询或者中断里检查 Rx 描述符的 OWN 位如果变成 0 说明有数据。读出来解析看是不是 ARP 回复。这个测试能过再上 LwIP 就稳了。如果过不了按下面的排查表逐项检查。4.6 关键参数计算缓冲区数量与中断频率缓冲区数量不是随便定的。假设你的应用每秒要处理 1000 个帧每个帧平均 500 字节那么每秒数据量是 500KB。如果每个缓冲区 1524 字节4 个接收缓冲区就是 6096 字节。DMA 填满一个缓冲区的时间取决于网络速率100Mbps 下1524 字节的帧传输时间约 122 微秒。4 个缓冲区能缓冲约 488 微秒。如果你的中断处理能在 488 微秒内完成就不会丢包。中断频率也要算。如果每个帧都触发中断1000 帧每秒就是 1000 次中断。F429 跑 180MHz中断处理开销可以接受但如果帧率更高就要考虑中断合并或者轮询模式。我一般用 4 个接收缓冲区中断里只做标记主循环处理。这样中断频率可控处理也及时。5. 常见问题与排查技巧实录5.1 PHY 读不到 ID 或 ID 全 F这是最常见的问题。现象是读 PHY 寄存器返回 0x0000 或 0xFFFF。排查顺序PHY 供电用万用表量 PHY 的 VDD 引脚确认 3.3V晶振用示波器看 25MHz 晶振是否起振幅度是否够复位确认 nRST 引脚在上电后已经拉高MDC/MDIO确认 MDC 有波形MDIO 有上拉PHY 地址确认 PHYAD0 引脚状态地址填对我遇到过晶振起振但幅度只有 0.5V 的情况PHY 工作不稳定换晶振后正常。还有一次是 MDIO 上拉电阻忘了焊读出来全是 0。5.2 ping 不通但 PHY 已握手PHY 自协商成功但 ping 不通。可能原因MAC 地址冲突确认你的 MAC 地址在网络里唯一IP 配置错误确认 IP、子网掩码、网关跟对端在同一网段帧过滤太严检查 MACFFR确认没有误开过滤DMA 没启动检查 DMAOMR 的 SR 和 ST 位描述符 OWN 位错误确认发送时置 1接收时 DMA 会清 0我遇到过一次是 MAC 地址设成了全 0结果所有帧都被过滤掉了。改成合法地址后正常。5.3 丢包严重或通信不稳定丢包通常跟缓冲区、时钟、中断有关现象可能原因解决方法大量丢包接收缓冲区太少增加到 8 个偶发丢包中断优先级太低提高以太网中断优先级高速率丢包DMA 突发长度太小PBL 设为 32特定帧丢包帧长度超过缓冲区增大 ETH_MAX_PACKET_SIZE随机丢包时钟抖动大缩短 REF_CLK 走线加串阻我有个项目在 100Mbps 满速时丢包后来把接收缓冲区从 4 增加到 8并把中断优先级从 6 提到 5问题解决。5.4 发送卡死或描述符跑飞发送卡死通常是描述符 OWN 位没置对或者描述符链断了。检查发送描述符的 OWN 位是否置 1描述符的下一描述符地址是否正确发送缓冲区地址是否有效DMA 的 ST 位是否置 1我见过有人把描述符定义在栈上函数返回后内存被覆盖DMA 读到垃圾数据直接跑飞。改成全局变量后正常。5.5 常见问题速查表问题排查第一步常见根因PHY ID 读不到量 PHY 供电供电/晶振/复位ping 不通查 MAC 地址地址冲突/过滤丢包查缓冲区数量缓冲区不足发送失败查 OWN 位描述符配置错通信不稳定查 REF_CLK时钟抖动中断不触发查 NVIC 配置优先级/使能独家避坑技巧每次改完硬件或软件先用回环测试验证 MAC 和 DMA。把 MACCR 的 LM回环模式位置 1发送的帧会直接回到接收端不经过 PHY。这样能快速判断问题在 MAC 侧还是 PHY 侧。这个技巧帮我省了无数次来回换板子的时间。5.6 调试工具与手段调试以太网光靠串口打印不够。我常用的工具示波器看 REF_CLK、MDC、MDIO 波形判断时钟和通信是否正常逻辑分析仪抓 RMII 数据线分析帧内容网络抓包工具在 PC 侧抓包看帧有没有发出来、格式对不对PHY 寄存器读取通过 SMI 读 PHY 内部寄存器判断链路状态我一般先用示波器确认时钟再用 PHY 寄存器确认链路最后用抓包确认数据。这三步能定位九成以上的问题。5.7 从以太网外设到 LwIP 的衔接要点以太网外设调通后接 LwIP 就简单了。LwIP 需要一个网络接口结构体里面包含发送函数和接收函数。发送函数就是把你构造好的 pbuf 数据通过以太网 DMA 发出去接收函数就是以太网中断里把数据交给 LwIP 处理。关键点pbuf 和 DMA 缓冲区的拷贝LwIP 的 pbuf 和 DMA 缓冲区是两块内存接收时要拷贝发送时也要拷贝。拷贝有开销但安全。中断与主循环的分工中断里只标记主循环里调用 LwIP 的ethernetif_input处理。PHY 状态变化处理如果网线拔了又插PHY 会重新自协商LwIP 要能感知链路状态变化。我在实际项目里以太网外设初始化大概占整个网络调试时间的六成剩下四成是 LwIP 配置和协议调试。把底层吃透上层就是搭积木。最后分享一个小技巧如果你用的是 LAN8720A它的特殊模式寄存器地址 31能读出很多有用信息包括链路是否建立、速率、双工、甚至线序。调试时把这个寄存器读出来打印比只看 BSR 直观得多。这个寄存器在数据手册里叫 Special Modes Register值得花十分钟研究一下。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表