尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32F7+LAN8720网络驱动开发实战:从RMII到LWIP

STM32F7+LAN8720网络驱动开发实战:从RMII到LWIP 简介基于STM32F7系列单片机的LAN8720网口驱动程序源码核心封装了PHY芯片初始化、复位时序、中断控制与MAC地址配置等底层操作可直接对接LwIP协议栈适用于嵌入式以太网通信、物联网设备联网及STM32网络应用学习。压缩包共627个文件大小25.92MB内部以大量C语言源码和头文件为主辅以Keil工程文件、编译产物和readme文档等便于在MDK中直接打开工程也可参考hex与axf文件做烧录验证结构完整适合按模块移植。已有227人学习下载。代码清晰展示LAN8720_Init等关键函数并包含完整的F7系列HAL驱动及相关外设例程可以帮助开发者快速掌握STM32F7以太网外设的驱动流程和LwIP集成方法对于从事工业控制、智能网关等网络场景的工程师也是一份可直接参考的底层驱动模板能够减少重复开发、缩短联网功能调试周期。1. 基于STM32F7与LAN8720的网络驱动为什么值得动手写一遍串口和Modbus在局域网设备里越用越吃力尤其在传输日志、固件升级或对接TCP协议时网口几乎是绕不开的选择。STM32F7系列单片机自带了完整的以太网MAC控制器但MAC不是网口它需要一颗外部PHY芯片来完成物理层编码和线缆收发而LAN8720正是这个角色。市面上大量开发板都会把STM32F7与LAN8720搭在一起做成带网络功能的整套方案。下载下来的“软件源码.zip”看起来齐全但直接掏出来用得顺不顺取决于你是否明白驱动初始化顺序、RMII引脚分配以及底层收发路径是怎么串起来的。这个标题锁定在STM32F7系列单片机和LAN8720网口驱动程序上目标很直接通过代码让MAC和PHY建立起稳定链路再挂上LWIP协议栈让单片机真正具备收发TCP/IP报文的能力。适合的读者范围其实不小——做嵌入式网络设备、搞工业网关、写RTOS网络应用或者单纯想把手里的F7开发板连上网的人都会在这套驱动里找到自己的痛点。与其把源码当黑盒抄一遍不如顺着驱动程序的骨架把PHY寄存器、DMA描述符和中断处理这些关节拆开看一遍。2. RMII接口与PHY地址硬件设计的三个决定性选择2.1 为什么F7的MAC几乎绑定了LAN8720方案STM32F7的以太网控制器支持MII和RMII两种接口但实际项目里大家几乎都在用RMII。原因在于引脚数量MII需要16根数据信号线而RMII把发送、接收各压缩到2位整体只占7根信号线。对于LQFP100或LQFP144封装的F7芯片来说省出来的引脚足够给其他外设腾地方。LAN8720恰好是典型的10/100M RMII PHY逻辑简单驱动轻量工业级温度范围也够用。选型层面还有一层考量。LAN8720的寄存器布局与DP83848这类老牌PHY存在差异但它的基本控制/状态寄存器BMCR、BMSR完全遵循IEEE 802.3标准这意味着驱动代码的底层逻辑可以在不同PHY之间复用。只要把PHY地址和芯片型号相关的寄存器位定义抽出来换PHY时只需修改头文件配置。很多F7官方的评估板直接把PHY焊死在板上而自己画板时LAN8720A的功耗和外围电阻数量都比同类芯片更友好。2.2 RMII引脚分配与时钟方向的坑F7做RMII时硬件上最容易被忽略的是REF_CLK的来源方向。标准RMII要求外部提供50MHz参考时钟给PHY和MAC两侧LAN8720支持两种模式一种是由外部有源晶振直接供给PHY的XI引脚同时PHY的REF_CLK输出往返馈给MCU另一种是通过MCU的MCO引脚输出50MHz时钟给PHY。后者更常见因为整个板子只需要一颗25MHz晶振由F7内部PLL倍频后从MCO引脚输出。配置CubeMX时如果忘了打开MCO的时钟输出MCU和PHY各跑各的时钟网络链路永远起不来。引脚分配上RMII的发送和接收信号必须落在F7以太网控制器的固定引脚上。RMII_TXD0、RMII_TXD1、RMII_TX_EN、RMII_RXD0、RMII_RXD1、RMII_CRS_DV、RMII_MDIO和RMII_MDC这8根信号在CubeMX里会以黄色高亮强制绑定不能随意重映射。唯一能自由选的是MDIO的引脚位置但通常建议跟随默认值。另外LAN8720的PHY地址由RXER引脚上的上下拉电阻决定硬件设计时如果PHY_AD0上拉对应地址就是0下拉则为1。注意作为驱动软件这里是唯一与硬件强绑定的参数必须核对原理图再写进代码不能照搬默认。2.2.1 硬件配置清单参考信号MCU引脚说明RMII_TXD0PD4发送数据位0RMII_TXD1PD5发送数据位1RMII_TX_ENPD11发送使能高电平有效RMII_RXD0PD12接收数据位0RMII_RXD1PD13接收数据位1RMII_CRS_DVPD15载波侦听/数据有效RMII_MDCPC1管理时钟最大2.5MHzRMII_MDIOPA2管理数据双向REF_CLKPA1MCO输出50MHz给PHY3. PHY驱动核心寄存器操作、复位时序与状态机3.1 SMI/MDIO读写的固件实现F7的MAC控制器内部自带SMISerial Management Interface模块专门用来读写PHY寄存器。硬件上只有两根线MDC提供时钟MDIO串行传输数据。驱动的底层函数就是围绕这个时序做封装。以下是使用STM32 HAL库的PHY寄存器读函数uint32_t LAN8720_ReadReg(uint16_t phy_addr, uint16_t reg_addr) { uint32_t tmpreg; uint32_t timeout 0xFFFF; /* 构造SMI地址bit[19:16]为PHY地址bit[15:11]为寄存器地址 */ ETH-MACMDIAR ((phy_addr 16) ETH_MACMDIAR_PA) | ((reg_addr 11) ETH_MACMDIAR_MR) | ETH_MACMDIAR_MB; // 置位BUSY位启动读操作 /* 等待SMI总线忙标志自动清除 */ while ((ETH-MACMDIAR ETH_MACMDIAR_MB) timeout) { timeout--; } /* 从数据寄存器取出16位结果 */ return ETH-MACMDIAR ETH_MACMDIAR_MB ? 0 : (ETH-MACMDIAR 16); }逻辑说明STM32的ETH外设寄存器MAP结构里MACMDIAR的第0位是忙标志写入1后硬件自动发起SMI事务。读取完成后忙标志清零同时数据被送入MACMDIAR的高16位。等待循环不可省但超时机制可以避免PHY无应答时卡死程序。PHY的标准响应时间在微秒级0xFFFF的循环次数在72MHz主频下足够宽松。3.2 LAN8720初始化从硬件复位到自动协商初始化流程最关键的一步是复位。LAN8720有硬件复位和软件复位两种方式硬件复位靠NRST引脚拉低至少1微秒软件复位则是向寄存器0写BMCR的第15位。工程里如果硬件复位引脚没接MCU的GPIO就得用软复位。软复位后不能马上读状态因为PHY内部PLL稳定需要时间一般延时100ms较为稳妥。接下来是自动协商。自动协商让两端设备交换能力信息选择双方都支持的最高速率和工作模式。LAN8720上电默认开启自动协商寄存器0的默认值0x1000就包含了这项配置。代码层面的初始化顺序如下void LAN8720_Init(void) { /* 软件复位PHY等待自清除 */ LAN8720_WriteReg(PHY_ADDR, PHY_BMCR, 0x8000); HAL_Delay(100); /* 重启自动协商允许10M/100M全双工与半双工 */ uint16_t bmcr LAN8720_ReadReg(PHY_ADDR, PHY_BMCR); bmcr | 0x1200; // 半双工100M 全双工100M bmcr | 0x0100; // 半双工10M bmcr | 0x0040; // 全双工10M bmcr | 0x1000; // 开启自动协商 bmcr ~0x8000; // 清除软复位位 LAN8720_WriteReg(PHY_ADDR, PHY_BMCR, bmcr); /* 等待协商完成BMSR寄存器bit5置1 */ uint32_t timeout 0xFFFFFF; while (!(LAN8720_ReadReg(PHY_ADDR, PHY_BMSR) 0x0020) timeout) { timeout--; } }流程拆解先往BMCR写0x8000让PHY执行一次完整的复位序列。延时后在原有配置基础上叠加100M全双工0x0100是IE0x0040是AF0等标志位的组合并重新开启自动协商。协商完成标志在BMSR寄存器位5该位会在协商成功或失败后都置1因此业务代码需要额外读取协商结果寄存器。3.3 链路状态机驱动里隐藏的可靠性设计实际工程中PHY状态机最好独立成一块而不是塞在主循环里用HAL_Delay硬等。常见做法是在底板10ms定时中断或RTOS钩子函数里周期性读取PHY状态判断链路是否丢失再决定是否重启自动协商。链路状态寄存器LAN8720的寄存器1BMSRbit2是链路状态位注意它是一个锁存器读取后自动清零所以必须连续读两次第一次清除旧状态第二次读实时值。typedef enum { LINK_DOWN 0, LINK_UP, LINK_NEGOTIATING } link_state_t; link_state_t LAN8720_CheckLink(void) { uint16_t reg 0; reg LAN8720_ReadReg(PHY_ADDR, PHY_BMSR); /* 第一次读清除锁存 */ HAL_Delay(5); reg LAN8720_ReadReg(PHY_ADDR, PHY_BMSR); /* 第二次读获取实时值 */ if (reg 0x0020) { /* 自动协商完成 */ return LINK_NEGOTIATING; } if (reg 0x0004) { /* 链路正常 */ return LINK_UP; } return LINK_DOWN; }中断方式也可以LAN8720的INT引脚可以配置为链路状态变化中断输出连接MCU的EXTI引脚。主控可以从轮询中解放出来。但大部分无RTOS工程更倾向轮询因为PHY状态变化本身发生在毫秒级50ms一次的采样密度完全不会漏判。4. 从裸机驱动到LWIPDMA描述符与底层收发框架4.1 驱动代码的目录结构怎么摆软件源码zip里常见的组织方式是把PHY驱动和MAC驱动分开。工程里至少要有eth.c和phy_lan8720.c两个文件前者负责ETH外设初始化、DMA描述符数组和中断入口后者专管PHY寄存器操作。这种拆分的好处是HW算法不会互相污染PHY驱动出错时不需要排查DMA配置反之亦然。同时把寄存器地址宏定义和PHY芯片特定参数放在lan8720.h里换PHY时只动这个文件。STM32F7的HAL库提供了统一的ETH驱动接口但直接使用HAL_ETH_TransmitFrame和HAL_ETH_ReceiveFrame并不能满足LWIP的收发要求因为LWIP要求底层驱动提供零拷贝的缓冲区管理。常见方案是用F7的DMA描述符数组构建一个环形缓冲池描述符数量建议配4个接收、4个发送。每个描述符指向一个独立的缓冲区发送描述符缓冲区由用户维护接收描述符缓冲区则交给DMA硬件自动填充。4.2 与LWIP对接的接口函数LWIP协议栈的底层接口集中在ethernetif.c它要求驱动实现low_level_init、low_level_output和low_level_input三个函数。low_level_init负责创建DMA描述符链并配置ETH外设low_level_output在LWIP要发包时把pbuf里的数据复制到DMA发送缓冲区然后触发硬件发送low_level_input则在接收中断发生后把DMA接收缓冲区里的数据封装成pbuf返回给上层。典型实现片段static err_t low_level_output(struct netif *netif, struct pbuf *p) { uint8_t *buffer (uint8_t *) ETH-DMATXNDTPADDR; /* 复制pbuf数据到DMA描述符关联的缓冲区 */ pbuf_copy_partial(p, buffer, p-tot_len, 0); /* 配置发送描述符的缓冲区长度和状态位 */ HAL_ETH_TxBufferChainConfig(heth, dma_tx_desc_index); HAL_ETH_TransmitFrame(heth, p-tot_len); return ERR_OK; }核心逻辑很简单LWIP的pbuf可能是一个链表不连续而DMA传输需要物理连续的内存所以必须做一次memcpy。描述符索引通过全局变量记录当前到达链表的哪个位置发送完成后在DMA中断里递增索引。如果发送失败常见的坑是LSLast Segment位和FSFirst Segment位没有正确设置导致PHY收到不完整帧。HAL库函数内部会自动处理这种描述符状态位但要留意在快速发包时缓冲区是否被前一次数据覆盖必要时增加ISR等待回路。4.3 内存屏障与缓存一致性F7的D-Cache是一把双刃剑。ETH DMA写内存时绕过CPU缓存而CPU在接收中断里读数据时如果D-Cache正处于开启状态会从缓存中读取陈旧数据导致以太网帧内容损坏。解决办法有两个最简单粗暴的写法是在low_level_input里调用SCB_InvalidateDCache_by_Addr把当前缓冲区地址范围无效化SCB_InvalidateDCache_by_Addr((uint32_t *)buffer, length);另一种思路是把DMA描述符数组和缓冲区显式放置到非缓存内存段利用STM32F7工程的分散加载文件如.sct或.ld在MPU配置中把该区域设为不缓存。推荐用MPU方式因为它在中断频繁收发时省去每次刷缓存的操作时间转发率能提升不少。不过MPU配置需要额外维护新手阶段先用库函数补缓存一致性反而是最稳的。5. 链路调试的最后一公里从ping通到长时间稳定写完了驱动和协议栈board上用ping能通只算完成了第一步。想要交付还要经过连接稳定性、收发性能和掉线重连三关考验。这里有一个验证技巧给F7加上一个持续发送UDP报文的测试任务每秒发一帧同时在PC端用Wireshark紧盯报文序号。如果出现乱序或丢包先看PHY的链路状态是否出现抖动再检查DMA描述符是否耗尽。抓取PHY状态的调试代码放在系统节拍里每次读取寄存器以及自动协商结果如果检测到链路状态为DOWN立刻重启PHY并重新初始化LWIP。很多板子断电重连后无法重新ping通就是因为PHY的自动协商状态在异常断电后没有恢复而MAC和PHY的初始化顺序固定不能重启MCU核心部分却忘了对PHY执行软复位。另一个容易被忽略的参数是MAC地址。F7芯片出厂时没有固化MAC地址需要从外部EEPROM或编译时烧录的固定值中读取。如果所有板子共用同一个MAC地址在同一个二三层网络里会出现目的MAC漂移通信时断时续且极其难以定位。批量出货的设计至少要把MAC地址的低字节映射到芯片的唯一ID上保证每台设备不冲突。实验板阶段也建议在代码里预留出修改入口方便测试时分段模拟不同设备。最后PHY芯片的功耗并不低特别是在100M全双工加上线缆较长时芯片温度会比同板MCU高不少长时间可靠性测试里观察PHY底部PCB区域的温度变化往往能提前发现焊接虚焊或供电不足的隐患。本文还有配套的精品资源点击获取
返回列表