资讯动态

STM32F407与DP83848的RMII+LWIP网络移植排查

发布时间:2026/10/26 4:29:08 来源:尧图企业网站定制
简介面向嵌入式网络开发初学者与工程实践者的STM32F407LWIPDP83848 TCP/IP Server移植例程基于无操作系统方案实现TCP/IP数据收发且不依赖外部内存只需一块407核心板搭配DP83848网口模块即可复现适合用于以太网通信入门、协议栈移植验证与小型服务端项目原型。压缩包共3458个文件约22.92MB以C源码和H头文件为主体辅以汇编启动文件、工程配置、HTML/PDF说明及少量图片与库文件覆盖协议栈源码、网口驱动、工程模板和文档资料。已有4769人学习下载。包内附具体说明文件便于按目录梳理移植流程、核对底层驱动与上层应用接口理解内存配置、数据收发链路和工程编译组织可作为排查移植问题、搭建自有STM32网络应用的参考底包。1. 一块焊了 DP83848 的 F407 板子从“灯亮但 ping 不通”说起手上那块 STM32F407 开发板网口用的是一颗老牌工业级 PHYDP83848。插上网线Link 灯亮得挺好看电脑一 ping 却 100% 丢包换成默认配置DHCP 半天拿不到地址串口打印停在netif_set_up之前。这类“物理层通了、协议层没通”的现象我前后遇过三次根源高度集中REF_CLK 走错、PHY 地址认错、ETH DMA 描述符没对齐、ETH_PAD_SIZE漏配、lwipopts.h内存池给得太少。这套组合本身不新但移植链路特别长任何一环没接好都会表现为“ping 不通”。这份例程把从硬件时钟到 TCP 回环的整条链路完整跑过一遍适合正在给 F407 加网口、要把 Modbus TCP 或远程采集做起来的同行也适合刚接触 RMII 想看清底层细节的新手。2. RMII 链路怎么打通25MHz 晶振、PHY 地址和 DMA 描述符三步移植的第一关是让 PHY 能开口说话而不是急着把 LWIP 贴上去。原理上STM32F407 的 ETH 外设自带 MAC 和 DMADP83848 只负责物理层编解码两者之间通过 RMII 总线连接。只要 RMII 参考时钟、MDIO 通信、DMA 描述符三件事都对了HAL_ETH_Init才会返回成功后面才有 LWIP 的戏份。2.1 先把时钟和引脚对齐REF_CLK 到底由谁供给RMII 相比 MII 少了 8 根数据线但对时钟更敏感。STM32F407 的 ETH_RMII_REF_CLK 必须是一路稳定 50MHz 输入来源有两种常见做法一种是在 PHY 侧挂一颗 25MHz 晶振让 DP83848 内部的 PLL 倍频后从 CLK_OUT 引脚输出 50MHz 回灌给 PA1另一种是省掉 PHY 晶振由 MCU 的 MCO1PA8输出 50MHz 给 PHY 的 X1。两种都能工作但混用会直接翻车比如 PHY 侧晶振是 25MHz 却指望它 CLK_OUT 输出 50MHz 时寄存器没配对PA1 上量出来就是 25MHzETH DMA 直接不工作。引脚分配我用的是下面这套避开 JTAG 冲突也方便布线信号STM32F407 引脚方向说明RMII_REF_CLKPA1输入50MHz 参考时钟RMII_MDIOPA2双向管理数据RMII_CRS_DVPA7输入载波侦听/数据有效RMII_MDCPC1输出管理时钟约 2.5MHzRMII_RXD0PC4输入接收数据 bit0RMII_RXD1PC5输入接收数据 bit1RMII_TX_ENPB11 或 PG11输出发送使能RMII_TXD0PB12 或 PG13输出发送数据 bit0RMII_TXD1PB13 或 PG14输出发送数据 bit1引脚确定后在 CubeMX 里勾选 ETH 为 RMII 模式会自动打开 SYSCFG 时钟并配置复用功能。手工写代码时先开__HAL_RCC_SYSCFG_CLK_ENABLE()再配置 GPIO 复用顺序反了会出现引脚悬空。2.2 PHY 地址扫描DP83848 的 ID 是 0x20005C90DP83848 的 PHY 地址由 SA3~SA0 四个引脚的上拉/下拉组合决定板上常见是 0x01 或 0x00但不同板厂布法不一样。直接写死 0x01 是常见错误来源。我一般会从 0 扫到 31读 PHYIDR1/PHYIDR2 拼出 ID命中 0x20005C90 再继续往下走。这个 ID 是 DP83848 的固定标识读到别的值基本可以断定地址或 MDIO 时序不对。/* heth.Init.PhyAddress 已设置HAL 内部按该地址发 MDIO 帧 */ static uint32_t phy_read(uint16_t reg) { uint32_t val 0U; if (HAL_ETH_ReadPHYRegister(heth, reg, val) ! HAL_OK) { Error_Handler(); /* MDIO 超时MDC 频率过高或 PHY 未上电 */ } return val; } /* 命中 DP83848 固定 ID 才继续否则说明地址错或 PHY 没工作 */ bool dp83848_probe(void) { uint32_t id1 phy_read(0x02); /* PHYIDR1固定 0x2000 */ uint32_t id2 phy_read(0x03); /* PHYIDR2DP83848 为 0x5C90 */ return (((id1 16) | (id2 0xFFFFU)) 0x20005C90U); }PHY 寄存器地址 0x02、0x03 是 IEEE 802.3 规定的标识寄存器所有合规 PHY 都有。HAL_ETH_ReadPHYRegister的返回值必须判等早期有人把HAL_StatusTypeDef当成读到的值用结果把 HAL_OK0当成 PHY 数据后面所有判断全错。2.3 ETH DMA 初始化描述符、收发缓冲和校验和模式MAC 初始化这一步最容易被忽视的是 DMA 描述符所在的内存区域。STM32F407 的 CCM RAM0x10000000 起只挂 CPU 的 D-busETH DMA 访问不到。如果链接脚本把DMARxDscrTab、DMATxDscrTab、Rx_Buff、Tx_Buff分到 CCM 区上电后能跑一阵一有流量就进 HardFault现象随机到让人怀疑人生。默认 HAL 把它们放在主 SRAM 的.bss段是安全的除非你手动改了 section。ETH_HandleTypeDef heth; void MX_ETH_Init(void) { heth.Instance ETH; heth.Init.AutoNegotiation ETH_AUTONEGOTIATION_ENABLE; heth.Init.Speed ETH_SPEED_100M; heth.Init.DuplexMode ETH_MODE_FULLDUPLEX; heth.Init.PhyAddress 0x01; /* 扫描后确认的实际地址 */ heth.Init.MACAddr[0] 0x02; /* 本地管理地址前缀避免撞真实 MAC */ heth.Init.MACAddr[1] 0x00; heth.Init.MACAddr[2] 0x00; heth.Init.MACAddr[3] 0x00; heth.Init.MACAddr[4] 0x00; heth.Init.MACAddr[5] 0x01; heth.Init.RxMode ETH_RXINTERRUPT_MODE; /* 中断收包裸机跑不用轮询 */ heth.Init.ChecksumMode ETH_CHECKSUM_BY_HARDWARE; heth.Init.MediaInterface ETH_MEDIA_INTERFACE_RMII; if (HAL_ETH_Init(heth) ! HAL_OK) { Error_Handler(); /* 多半是自协商超时或 PHY 没回应 */ } }ChecksumMode选硬件校验和节省 CPU但要求 LWIP 侧ETH_PAD_SIZE和描述符对齐完全正确配错的表现是“小包能通、大包 Taxi 校验错”。RxMode选中断是因为裸机主循环里如果只靠轮询DHCP 续租这类定时事件容易漏。自协商阶段别只信HAL_ETH_Init的返回值紧接着轮询 BMSR 的 link 位读到置位再读 PHYSTS 确认速率和双工才是稳妥做法。3. 把 LWIP 接进 F407 工程netif 注册、DHCP 和第一个 TCP 回环协议栈这层的活核心是把 ST 提供的ethernetif.c和 LWIP 的 netif 结构接上再把 DHCP 的启动顺序理顺。很多人卡在“能收 ARP 但拿不到 IP”其实是netif_set_up和dhcp_start的调用顺序反了DHCP 的 UDP 报文根本发不出去。下面按裸机 NO_SYS 模式展开RTOS 模式下把sys_check_timeouts换成tcpip_thread轮询即可。3.1 lwipopts.h 的最小可运行集先别急着调大参数第一版只要把功能开关打开、内存给够就能跑。我最常用的最小集合如下各字段后面第 4 章再细算/* lwipopts.h —— 裸机 NO_SYS 模式先跑通再调性能 */ #define NO_SYS 1 #define LWIP_SOCKET 0 #define LWIP_NETCONN 0 #define LWIP_DHCP 1 #define LWIP_ICMP 1 #define LWIP_UDP 1 #define LWIP_TCP 1 #define LWIP_ARP 1 #define MEM_SIZE (10 * 1024) #define PBUF_POOL_SIZE 16 #define PBUF_POOL_BUFSIZE 1536 #define MEMP_NUM_PBUF 16 #define MEMP_NUM_TCP_PCB 8 #define MEMP_NUM_TCP_SEG 32 #define MEMP_NUM_UDP_PCB 6 #define TCP_MSS 1460 #define TCP_WND (4 * 1024) #define TCP_SND_BUF (4 * 1024) #define TCP_SND_QUEUELEN ((4 * TCP_SND_BUF) / TCP_MSS) #define ETH_PAD_SIZE 2 /* F407 ETH DMA 发送缓冲头部留 2 字节必须配 2 */ETH_PAD_SIZE值 2 是 F407 平台的硬规则ETH DMA 在发送时会在缓冲区起始位置预留 2 字节用于对齐LWIP 的 pbuf 如果不跟着偏移MAC 收到的帧头就会错 2 字节表现为“ARP 回、TCP 不复位”或“ping 小于 18 字节能过、大包全丢”。PBUF_POOL_BUFSIZE 1536要覆盖 1500 MTU 加以太网头、加 2 字节 pad、再加对齐余量。3.2 netif_add 到 dhcp_start顺序反了就是拿不到地址裸机模式下 LWIP 的初始化顺序非常固定lwip_init→netif_add→netif_set_default→netif_set_up→dhcp_start。最后两步尤其不能换LWIP 的 DHCP 客户端在dhcp_start里会检查 netif 的 flags网卡没 up 就直接返回ERR_IF表面上返回成功但一个 DISCOVER 都不会发出去经验不足的同行会误以为是 DHCP 服务器的问题。struct netif gnetif; void MX_LWIP_Init(void) { ip4_addr_t ipaddr, netmask, gw; IP4_ADDR(ipaddr, 0, 0, 0, 0); IP4_ADDR(netmask, 0, 0, 0, 0); IP4_ADDR(gw, 0, 0, 0, 0); lwip_init(); netif_add(gnetif, ipaddr, netmask, gw, NULL, ethernetif_init, ethernet_input); netif_set_default(gnetif); netif_set_up(gnetif); /* 必须先 upDHCP 才会真正发包 */ if (dhcp_start(gnetif) ! ERR_OK) { Error_Handler(); /* 常见原因MEMP_NUM_UDP_PCB 不够 */ } }ethernetif_init和ethernet_input来自 ST 的ethernetif.c前者负责把 MAC 收发的底层函数注册进 netif后者负责把 DMA 收到的帧转成 pbuf 交给 LWIP。这两个名字不要改CubeMX 生成的代码也是同一套约定。如果你用的是 LWIP 1.4.1ip4_addr_t要换成struct ip_addrnetif_add少一个状态参数其余顺序一致。3.3 主循环里跑 TCP 回环验证链路真的通了在业务代码之前先放一个 TCP echo 服务能回环就说明 MAC、LWIP、netif 三件都对了。裸机状态下 UDP/TCP 的定时处理挂在sys_check_timeouts上主循环里的MX_LWIP_Process要周期调用间隔别超过 1ms否则 TCP 重传定时器会偏。/* tcp_echoserver.c 里摘出来的 accept 回调收到什么原样发回 */ static err_t echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { if (p NULL) { tcp_close(tpcb); /* 对端关闭回收 PCB */ return ERR_OK; } tcp_recved(tpcb, p-tot_len); /* 告诉 LWIP 已消费否则窗口会塌 */ tcp_write(tpcb, p-payload, p-tot_len, TCP_WRITE_FLAG_COPY); tcp_output(tpcb); /* 显式 flush省得等 Nagle 定时 */ pbuf_free(p); return ERR_OK; }在主循环里加一句MX_LWIP_Process()内部就是ethernetif_input(gnetif)加sys_check_timeouts()再配一个 DHCP 轮询while (dhcp_supplied_address(gnetif) 0)里加 5 秒超时打印方便定位是 DHCP 服务器没应答还是本地没发出去。PC 端telnet 板子IP 7敲几行字符能原样回显这条链路就算立起来了。4. lwipopts.h 怎么调PBUF 池、TCP 窗口和 128KB SRAM 的分配账功能跑通以后调参的目标只有一个让吞吐和稳定性匹配业务。F407 主频 168MHz、SRAM 192KB其中 64KB CCMLWIP 默认配置明显偏保守直接上大包或长连接就会出现“跑一阵断、过一会儿又恢复”的现象。下面按内存池、窗口、零拷贝三个维度分别算账。4.1 MEM_SIZE、PBUF_POOL_SIZE、MEMP_NUM_PBUF 三者的关系三个参数别混在一起背它们管的不是一回事。MEM_SIZE是 LWIP 自己的堆TCP 控制块、ARP 表、DHCP 结构都从这里 mallocPBUF_POOL_SIZE是预分配的 pbuf 池数量每个大小PBUF_POOL_BUFSIZEMEMP_NUM_PBUF是池外的零散 pbuf 数量用于应用层pbuf_alloc的场景。100M 全双工、单连接压测的配置我一般这么给参数默认值建议值作用与超出后果MEM_SIZE160016384LWIP 堆太小会在mem_malloc返回 NULL 后静默丢连接PBUF_POOL_SIZE1632接收 pbuf 池太小高并发时pbuf_alloc失败PBUF_POOL_BUFSIZE15241536单个 pbuf 数据区必须覆盖 1500 MTU padMEMP_NUM_TCP_PCB510同时活动的 TCP 连接数超了tcp_new返回 NULLMEMP_NUM_TCP_SEG1664TCP 分段数压测大包时不够会频繁重传MEMP_NUM_UDP_PCB48DHCP/SNMP 等 UDP 客户端数DHCP 拿不到 IP 的常见元凶PBUF_POOL_BUFSIZE有个容易忽略的点它必须能容纳PBUF_LINK_HLEN14 字节以太网头ETH_PAD_SIZE2 字节 MTU1500再加对齐余量才是 1536。算少了会出现接收帧被截断但 LWIP 不一定会报错表现为 ping 大包时通时不通。4.2 TCP_WND 和 TCP_SND_BUF 怎么和 SRAM 抢空间TCP_WND是接收窗口对端按这个窗口发数据给得小吞吐上不去给得大内存吃紧。F407 的 192KB SRAM 要同时养 ETH DMA 缓冲、LWIP 堆、应用任务栈TCP_WND和TCP_SND_BUF各给 8KB 是比较稳的平衡点。更激进的做法是开LWIP_WND_SCALE把窗口乘 2 的幂但 Wireshark 抓包和部分服务器兼容性都要额外确认生产环境慎用。/* 吞吐优先的配置压测阶段临时打开量产按业务回退 */ #define TCP_MSS 1460 #define TCP_WND (8 * 1024) #define TCP_SND_BUF (8 * 1024) #define TCP_SND_QUEUELEN ((4 * TCP_SND_BUF) / TCP_MSS) #define TCP_OVERSIZE TCP_MSS /* 避免小包拆分带来的额外拷贝 */TCP_SND_QUEUELEN不用自己瞎猜公式(4 * TCP_SND_BUF) / TCP_MSS是 LWIP 文档给出的经验值给少了tcp_write返回ERR_MEM应用层要自己处理重试很多“发送偶尔丢数据”的问题根源就在这。4.3 硬件校验和与零拷贝两个省 CPU 开关的代价F407 的 ETH MAC 支持 IP/TCP/UDP 硬件校验和开ETH_CHECKSUM_BY_HARDWARE能省下一大块 CPU代价是发送缓冲区对齐要严格满足ETH_PAD_SIZE 2配错时抓包表现为“checksum incorrect”。零拷贝的取舍更微妙开LWIP_ZEROCOPY后应用发送数据不经过 pbuf 拷贝吞吐能上去但缓冲区生命周期必须由应用自己管一旦在 DMA 发完之前改写缓冲区对端会收到脏数据。我一般的顺序是先开硬件校验和、不开零拷贝跑到稳定吞吐如果 CPU 占用超过 70% 再评估零拷贝评估时先用小流量验证一个月再放量。零拷贝省的是 CPU赌的是代码纪律不值得为几帧吞吐冒险。5. 五个高频翻车现场排查从链路灯不亮到拿不到 IP链路调试最怕“看起来都正常”灯亮、串口有打印、代码没报错但就是不通。下面五个坑是我在不同项目里都撞过的按“现象 → 原因 → 解决”整理出来遇到类似症状直接对照。5.1 坑一Link 灯闪但不稳定ping 时通时断现象网口灯偶尔亮、偶尔灭插拔网线时好时坏Wireshark 能看到零星 ARP 但不成流。原因PA1 上的 REF_CLK 实际频率不是 50MHz。DP83848 的 CLK_OUT 默认输出频率由 PHYCTRL 寄存器0x17的 bit1 决定出厂是 50MHz但有些板子把 X1 接成 25MHz 晶振却没配 PHY 走 25MHz 模式或者 CLK_OUT 根本没用、PA1 悬空。解决示波器量 PA1看到 50MHz 方波才算过。没信号就把 PHY 的 X1/CLK_OUT 配置改对或者改用 PA8 的 MCO1 输出 50MHz 给 PHYMCO 分频在 RCC 配置里改RCC_MCO1Config。5.2 坑二ARP 能回TCP 握手到 SYNACK 就复位现象电脑arp -a能看到板子ping小包能过一发起 TCP 连接就 RST或者大包全丢。原因ETH_PAD_SIZE没配或配错。F407 的 ETH DMA 在发送缓冲区头部预留 2 字节LWIP 如果按 0 处理MAC 发出去的以太网帧头部错位 2 字节硬件校验和会把整个帧判错。解决lwipopts.h里#define ETH_PAD_SIZE 2然后 clean 整个工程重新编译——只增量编译lwipopts.h相关文件不够pbuf.c、ethernetif.c都要重编才生效。5.3 坑三跑几分钟随机 HardFaultMAP 文件里描述符在 CCM现象上电正常跑几十秒到几分钟随机死机调试器显示 HardFault栈回溯落在 ETH 中断里。原因链接脚本把DMARxDscrTab、DMATxDscrTab、Rx_Buff、Tx_Buff分到了 CCM RAM0x10000000。CCM 只挂 CPU 的 D-busETH DMA 根本访问不到写描述符时总线错误但错误上报有延迟所以不是一上电就崩。解决看 MAP 文件确认这几个符号的地址在 0x20000000 区间。如果不在改链接脚本把它们放回主 SRAM或者直接去掉.ccmram的 section 分配。顺手把system_stm32f4xx.c里DATA_IN_ExtSRAM之类的宏也检查一遍。5.4 坑四压测大包时突然断流过几秒自己恢复现象ping -l 1400或 iperf 跑几十秒后连接断开等几秒又自己恢复串口没有报错。原因PBUF_POOL_SIZE或MEMP_NUM_PBUF给小了。大包突发时 pbuf 池瞬间耗尽ethernetif_input里pbuf_alloc返回 NULL帧被静默丢弃TCP 靠重传慢慢恢复。解决先把PBUF_POOL_SIZE翻倍到 32、PBUF_POOL_BUFSIZE提到 1536跑稳定后再按 SRAM 余量回调。开LWIP_STATS看memp_stat的err计数能不丢就不丢。5.5 坑五DHCP 客户端启动了但永远拿不到地址现象串口打印dhcp_start返回成功抓包看不到 DHCP DISCOVER5 秒超时后一直空转。原因两个高发点。一是netif_set_up在dhcp_start之后调用DHCP 的 UDP 报文发不出去二是MEMP_NUM_UDP_PCB给小了DHCP 申请 UDP 控制块失败后状态机卡死。解决把netif_set_up放到dhcp_start前面MEMP_NUM_UDP_PCB至少给 6DHCP 自己占 1DNS 1应用留余量。如果还不行抓包看板子有没有发 ARP 请求网关ARP 都不发说明链路层根本没数据出去回头查 MAC 初始化和 DMA 描述符。6. 压测与长期跑iperf 吞吐、抓包重传和一个月不重启的检查点功能通了只是起点真正难的是让它在现场跑一个月不重启。我习惯分三步验证先看吞吐上限再看重传率最后跑长稳。6.1 三步验证法阶段工具观察指标合格线100M 全双工吞吐iperf3 PC→板子单向稳定带宽30Mbps 以上可靠性Wireshark 抓包TCP Retransmission 比例低于 0.5%长稳24 小时循环 ping TCP 长连接丢包、内存余量0 丢包、mem_stat.avail不持续下降6.2 我常踩的两个“看起来正常”一是 iperf 跑出来 45Mbps 就觉得没问题实际抓包发现重传率 3%量产几个月后现场反馈“偶尔卡顿”。二是长稳只 ping 不跑业务忽略了 TCP PCB 泄漏——每断开一次连接不tcp_closeMEMP_NUM_TCP_PCB迟早耗尽。后来我改成跑一个带tcp_close的循环回环测试每小时打印memp_stat和mem_stat一个月下来内存曲线平的才算过。从那以后我每次上新板子都先用 60 秒 iperf 单向 24 小时 ping 加 TCP 长连接跑一遍再谈业务功能。这套动作省掉的返工比想象的多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑