尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32F407+LWIP+DP83848以太网移植与排障指南
简介面向嵌入式开发者的STM32F407LWIPDP83848移植例程聚焦无操作系统环境下TCP/IP Server的搭建与数据收发适合已具备STM32基础、希望打通以太网通信链路的工程师与学生参考。压缩包共3458个文件以C源码与头文件为主体并含汇编启动文件、Keil工程文件、配置说明、PDF文档及少量网页与图片素材整体约22.92MB便于按工程结构查看移植细节。已有4769人学习说明该方案在同类硬件组合中具有一定参考价值。读者可据此了解DP83848在F407平台上的驱动适配、LWIP协议栈配置及无OS服务端收发流程资源不依赖外部内存只需一块407与DP83848即可复现并附具体说明文件辅助排错与二次开发。1. 从一块不亮灯的网口说起STM32F407LWIPDP83848 这套环境到底装什么手上这块 STM32F407 板子网口那头的 DP83848 焊得规规矩矩网线插上去两个灯就是不亮。这是很多人第一次做 STM32 LWIP DP83848 移植时的开局——例程拖进 Keil编译通过下载运行然后对着不亮的 LED 发呆。F407 自带以太网 MACDP83848 是 RMII 接口里资料比较全的 10/100M PHY两边凑一起成本低所以这套组合在工控板、采集器、教学板上到处都是。可它同时压在三条线上STM32 的 MAC 时钟与 RMII 引脚、DP83848 的自协商与 PHY 地址、LWIP 的内存池与网卡驱动接口任何一处错位网口就变成一个只进不出的盲区。这篇笔记按「装什么版本 → 引脚和 PHY 怎么接 → 驱动和 lwipopts.h 怎么改 → 坏了怎么救 → 装完怎么验」的顺序走一遍适合刚拿到板子、准备把裸机 TCP/IP 跑通的开发者。2. 环境准备与版本选择STM32F407 工程里 LWIP 和 DP83848 该配哪几样移植能不能一次跑通八成在前半天就定下来了。我的血泪经验是先把硬件、库、协议栈三者的版本对齐表格列出来再动手建工程版本对不上后面调三天也调不出结果。常见的翻车场景是拿着 1.4.1 的 ethernetif.c 去配 2.1.2 的源码树编译一通报sys_mutex_t未定义改了半小时才发现是两个时代的 API。2.1 硬件、库、工具链的版本对照先把这张表抄下来对不上就先别开 Keil。尤其是「标准库还是 HAL 库」这一行两个库的以太网收发包接口名字完全不同混着用一定会卡在链接阶段。组成选型关键参数 / 说明MCUSTM32F407VGT6 或 ZGT6Cortex-M4F168MHz192KB SRAM1MB FlashMAC片内 ETH需 25MHz 或 50MHz 参考时钟RMII 模式下 REF_CLK 为 50MHzPHYDP83848IVV / DP83848CRMII 模式PHY 地址常见为 0x01固件库STM32F4xx_HAL_Driver与 CubeMX 配套接口前缀HAL_ETH_固件库备选STM32F4xx_StdPeriph_Driver接口前缀ETH_老例程多用这套LWIP2.1.2 或 1.4.1两代 API 差异大选定一个就别换工具链Keil MDK 5.2xAC5 或 AC6AC6 编译 LWIP 需关掉部分警告调试器ST-Link V2用 SWD 即可不占用网口相关引脚我一般会固定「HAL 库 LWIP 2.1.2」这一组因为 CubeMX 能直接生成 ETH 初始化省掉手写 MAC 寄存器的那一截。如果手头例程是标准库版本把HAL_ETH_TransmitFrame之类的调用替换成ETH_TransmitFrame逻辑是同一个。2.2 LWIP 版本怎么选1.4.1 和 2.x 的差异点LWIP 1.4.1 是十多年前的稳定版本很多国内开发板的「网络实验」例程都基于它优点是配套的 ethernetif.c 满网都是、改起来心里有底缺点是sys_arch接口和 2.x 不兼容比如 1.4.1 里sys_sem_new返回的是信号量指针2.x 里返回err_t并把句柄通过出参传出去直接照抄会编译不过。2.1.2 是后来的长期维护版本lwipopts.h里的选项更细PBUF_POOL_BUFSIZE、MEMP_NUM_TCP_SEG这些能单独裁内存吃紧的 F407 上更可控。代价是网上能直接抄的完整工程少一些得自己对着ethernetif.c模板填空。我的取舍是如果是一块已有例程的板子先照例程的版本跑通再考虑升级如果是新画的板子直接上 2.1.2省得以后从 1.4.1 迁。两个版本在 DP83848 这一层没有区别PHY 寄存器读写都是标准 SMI 时序。2.3 工程骨架搭起来目录结构和第一次编译先把目录铺好再往里塞代码。下面这套结构在 HAL 和标准库下都适用arch、lwip、ethernet三个目录分开出问题的时候一眼能看出是哪一层。# 在工程根目录下执行建立 LWIP 移植的标准目录骨架 mkdir -p Drivers/STM32F4xx_HAL_Driver \ Middlewares/Third_Party/Lwip/src \ Middlewares/Third_Party/Lwip/system \ Middlewares/Third_Party/Lwip/apps \ BSP/ethernet \ BSP/arch # lwip/src 下放 api/ core/ netif/ 三个子目录直接从官方源码包拷 # BSP/ethernet 放 ethernetif.c、dp83848.c # BSP/arch 放 sys_arch.c、sys_arch.h、cc.h、perf.h 这几个头几个容易写错的路径点cc.h里要定义LWIP_PLATFORM_DIAG和字节序宏F407 是小端BYTE_ORDER要写成LITTLE_ENDIANperf.h是空实现也得有不然lwipopts.h里一开LWIP_PERF就链接失败Keil 的 Include Paths 里必须加上lwip/src/include、lwip/system、BSP/ethernet三处漏一处就是一堆No such file。第一次编译的目标不是跑通而是「零错误、只剩警告」。如果编译阶段就报pbuf未定义先查 Include Paths报sys_mutex_t类型冲突查NO_SYS是否和sys_arch.h里的定义一致。这一步过了再动硬件。3. DP83848 驱动落地RMII 引脚、MCO 时钟和 PHY 地址怎么配到了这一章才真正碰到引脚。STM32F407 的以太网 MAC 对外只出 RMII 或 MII 两组信号DP83848 在 RMII 模式下需要 50MHz 的 REF_CLK这个时钟要么由 STM32 的 MCO1 输出要么由 DP83848 外接的 25MHz 晶振经内部 PLL 倍频后反送给 MCU。两种接法对应两套引脚配置拿到板子第一件事是看原理图确认走的是哪一种。3.1 RMII 引脚分配与 MCO 时钟输出RMII 相比 MII 省了一半引脚一共 9 根信号线F407 上这组引脚的重映射是固定的没法随便挪。下面这张表我贴在工位墙上接错一根网口就是「灯亮但 ping 不通」或者「灯干脆不亮」。信号STM32F407 引脚说明ETH_RMII_REF_CLKPA150MHz 参考时钟输入ETH_MDIOPA2SMI 管理数据需 1.5k~10k 上拉ETH_RMII_CRS_DVPA7载波侦听 / 数据有效ETH_MDCPC1SMI 管理时钟ETH_RMII_RXD0PC4接收数据位 0ETH_RMII_RXD1PC5接收数据位 1ETH_RMII_TX_ENPB11发送使能ETH_RMII_TXD0PB12发送数据位 0ETH_RMII_TXD1PB13发送数据位 1MCO1可选PA8输出 25MHz 或 50MHz 给 PHY如果板子上 DP83848 用的是 25MHz 无源晶振它自己会在 REF_CLK 脚输出 50MHz 给 MCU 的 PA1此时 PA8 什么都不用做如果 PHY 没有晶振、靠 MCU 供时钟就要把 PA8 配成 MCO1源选 HSE分频到 50MHz 或者先出 25MHz 再让 PHY 内部倍频。配置 MCO 的代码如下/* 使能 GPIOA 时钟把 PA8 复用为 MCO1输出 HSE 25MHz 给 PHY */ __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_8; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF0_MCO; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); /* HSE 直接输出不分频HSE25MHz 时 MCO1 就是 25MHz */ HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);RCC_MCODIV_1表示不分频输出频率等于源频率。如果 PHY 需要 50MHz把源改成RCC_MCO1SOURCE_PLLCLK再把 PLL 的 Q 分频调到 50MHz这一步一旦配错PHY 的 REF_CLK 频率漂移链路自协商会反复失败串口上打出来的 PHY 状态寄存器一直是0x7849而 link 位不置位。用示波器量 PA1 上的频率是最快的确认办法25MHz 和 50MHz 差一倍肉眼可辨。3.2 PHY 地址探测与 SMI 读写DP83848 的 SMI 接口只有 MDC 和 MDIO 两根线所有 PHY 寄存器都从这里读写。PHY 地址由芯片上的 PHYAD[4:0] 引脚电平决定常见板子把 PHYAD0 拉高地址就是 0x01。HAL 库的HAL_ETH_ReadPHYRegister第二个参数传的是「地址 寄存器号」的组合值不是裸地址这点和标准库不一样。/* DP83848 的 PHY 地址按硬件 PHYAD 引脚决定常见为 0x01 */ #define DP83848_ADDR ((uint16_t)0x01) /* PHY 标准寄存器 */ #define PHY_BSR 0x01U /* 基本状态寄存器 */ #define PHY_BMCR 0x00U /* 基本控制寄存器 */ /* 读 BSR 判断链路是否 upbit2 为 Link Status */ uint16_t phy_bsr 0; uint32_t phyaddr (uint32_t)(DP83848_ADDR 5); /* HAL 要求 (addr5) */ if (HAL_ETH_ReadPHYRegister(heth, phyaddr | PHY_BSR, phy_bsr) ! HAL_OK) { /* 读失败一般是 MDIO 没上拉、MDC 频率过高或 PHY 没有供电 */ Error_Handler(); } if (phy_bsr 0x0004) { /* 链路已建立可以继续读自协商结果寄存器确认 100M 全双工 */ } else { /* 链路未建立先查网线再查 REF_CLK最后查 PHY 复位脚 */ }phyaddr | PHY_BSR这个写法看着别扭是因为 HAL 把(DevAddr 5) | RegAddr直接塞进了 MACMIIAR 寄存器分频系数另由HAL_ETH_SetMDIOClockRange()设置。MDC 频率上限是 2.5MHzHCLK 168MHz 时分频系数要取到 62 以上否则读回来的值是 0xFFFF——这个现象特别有迷惑性看起来像 PHY 没焊好其实是时钟太快。读 BSR 之前最好先做一次软件复位往 BMCR 写 0x8000延时 100ms 再读。DP83848 上电默认状态不一定可靠尤其是热插拔之后不自协商的残留状态会让 BSR 的 link 位卡在 0。3.3 网卡驱动 ethernetif.c 的四个回调LWIP 不关心底层是 DMA 还是寄存器搬运它只认netif结构体里挂的四个函数指针。HAL 库下的ethernetif.c模板可以直接拿来改核心是low_level_init、low_level_output、low_level_input和ethernetif_input。/* 网卡初始化挂 MAC 地址、开 DMA 收发描述符、启动 MAC */ static void low_level_init(struct netif *netif) { /* 48 位 MAC 地址前三个字节是 OUI后三个可以按板子编号自增 */ uint8_t macaddress[6] {0x00, 0x80, 0xE1, 0x00, 0x00, 0x01}; netif-hwaddr_len ETHARP_HWADDR_LEN; MEMCPY(netif-hwaddr, macaddress, ETHARP_HWADDR_LEN); netif-mtu 1500; /* 关键告诉 LWIP 这层需要 ARP且支持广播和多播 */ netif-flags NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; /* 初始化 ETH 外设和 DMA 描述符环形缓冲数量按 RAM 剩余情况给 */ HAL_ETH_Init(heth); /* 把接收描述符的所有权交给 DMA这一句漏了网口就是只发不收 */ HAL_ETH_Start(heth); } /* 发送一个 pbuf 链把 pbuf 里的数据按链式逐个拷进发送缓冲 */ static err_t low_level_output(struct netif *netif, struct pbuf *p) { struct pbuf *q; uint8_t *buffer (uint8_t *)ETH_TX_BUF_ADDR; for (q p; q ! NULL; q q-next) { MEMCPY(buffer, q-payload, q-len); /* 简化写法单缓冲拼包 */ buffer q-len; } /* 实际工程里应该用零拷贝把 pbuf 的 payload 指针直接挂到 DMA 描述符 */ return HAL_ETH_TransmitFrame(heth, p-tot_len) HAL_OK ? ERR_OK : ERR_IF; }low_level_init里最容易出问题的是netif-flags漏写NETIF_FLAG_LINK_UPDHCP 会一直超时因为 LWIP 认为链路没起来压根不发包漏写NETIF_FLAG_ETHARPARP 请求发不出去表现是同一网段都 ping 不通。这两个宏不占内存但决定协议栈敢不敢往下走。low_level_output里的MEMCPY循环是我图省事的写法每包多一次内存拷贝F407 主频 168MHz 下跑 10M 没问题跑 100M 全速时 CPU 占用会明显上升。要做零拷贝得把pbuf的payload转成物理地址开了 DCache 的 M7 才需要F407 没有 D-Cache直接把p-payload填进 DMA 描述符的 Buffer1Addr 即可。low_level_input反过来从接收描述符里取帧长再用pbuf_alloc(PBUF_RAW, len, PBUF_POOL)申请内存把数据拷进 pbuf最后交给ethernetif_input往上送。4. LWIP 协议栈裁剪lwipopts.h 里必须改动的十几个宏协议栈跑不跑得起来一半看驱动另一半看lwipopts.h。LWIP 默认配置是给「RAM 管够」的处理器准备的直接拖到 F407 上192KB SRAM 会被吃掉一大块编译能过运行到第二个 TCP 连接就 hardfault。我的原则是先按最小可用配置跑通 ping再按业务往上加别一上来就开 socket、开 DHCP、开 DNS。4.1 内存池与 pbufF407 上怎么把 RAM 控制在 30KB 内LWIP 的内存分两块MEM_SIZE是堆给应用层和 socket 用PBUF_POOL_SIZE × PBUF_POOL_BUFSIZE是池给网卡收包用。这两块加起来就是协议栈的常驻内存F407 的 192KB 里还得留给用户程序、FreeRTOS 堆栈、外设缓冲所以协议栈一般压在 30KB 以内比较安全。/* lwipopts.hSTM32F407 DP83848 FreeRTOS 的最小可用裁剪 */ #define NO_SYS 0 /* 1裸机0跑 RTOS先跑裸机可临时改 1 */ #define MEM_ALIGNMENT 4 /* Cortex-M4 按 4 字节对齐 */ #define MEM_SIZE (12 * 1024) /* 堆 12KB够一个 socket 连接用 */ #define PBUF_POOL_SIZE 8 /* 收包池个数8 个够跑 10M 流量 */ #define PBUF_POOL_BUFSIZE 1536 /* 单池大小含以太网头 14 1500 余量 */ #define MEMP_NUM_PBUF 16 /* pbuf 结构体个数包含 PBUF_ROM 类型 */ #define MEMP_NUM_TCP_SEG 24 /* TCP 分段控制块影响并发发送能力 */ #define MEMP_NUM_TCP_PCB 8 /* TCP 连接控制块按业务并发数给 */ #define TCP_MSS 1460 /* 以太网 MTU 1500 - IP 头 20 - TCP 头 20 */ #define TCP_SND_BUF (2 * TCP_MSS) /* 发送窗口 2920太小吞吐上不去 */ #define TCP_WND (2 * TCP_MSS) /* 接收窗口需 ≤ TCP_SND_BUF */ #define TCP_SND_QUEUELEN (4 * TCP_SND_BUF / TCP_MSS) #define LWIP_NETCONN 1 /* 开 netconn API比 raw API 好写 */ #define LWIP_SOCKET 0 /* socket 层吃 RAM裸机或小内存先关掉 */ #define LWIP_DHCP 0 /* 固定 IP 调试阶段先关通了再开 */ #define LWIP_UDP 1 /* ping 用 ICMPUDP 留着做广播发现 */ #define LWIP_ICMP 1 /* 必开否则 ping 不通别问我是怎么知道的 */PBUF_POOL_BUFSIZE取 1536 是留了余量以太网最大帧 1518 字节含 CRCLWIP 的 pbuf 头会额外占几个字节取到 1536 再按 4 字节对齐不会因为尾包多出 2 字节触发pbuf_alloc返回 NULL。这个值太小表现是「小包能通、大包丢」太小还容易让人误判成 MTU 问题。TCP_SND_BUF和TCP_WND这两个数直接决定吞吐。F407 的 ETH DMA 在 100M 全双工下理论能跑到 90Mbps 以上但如果窗口只给 1460一次只能发一个 MSS 再等 ACK实测能掉到 10M 以下。常见做法是给 2~4 个 MSS兼顾吞吐和 RAM。MEM_SIZE给 12KB 是个折中跑一个 netconn echo 服务足够如果要做 HTTP 服务器挂静态页面得加到 24KB 甚至 32KB那时候就要回头算 RAM 够不够。4.2 sys_arch裸机和 RTOS 两种跑法差在哪sys_arch.c是 LWIP 和操作系统之间的适配层负责信号量、互斥锁、邮箱和sys_now。裸机下NO_SYS1这个文件可以极简只实现sys_now返回毫秒计数跑 FreeRTOS 下NO_SYS0每个信号量都要包一层xSemaphoreCreateBinary邮箱包xQueueCreate偷懒不实现会编译到一半缺符号。/* sys_arch.c裸机模式下的最小实现只有 sys_now 是必须的 */ u32_t sys_now(void) { /* HAL_GetTick 由 SysTick 每 1ms 自增直接返回即可 */ return (u32_t)HAL_GetTick(); } /* 如果 NO_SYS0 且用 FreeRTOS上面这段要换成 */ /* 信号量创建返回 err_t句柄通过出参传出和 1.4.1 的返回值风格不同 */ err_t sys_sem_new(sys_sem_t *sem, u8_t count) { *sem xSemaphoreCreateBinary(); if (*sem NULL) return ERR_MEM; if (count 1) xSemaphoreGive(*sem); return ERR_OK; }裸机下还有一个坑NO_SYS1时协议栈不会自己跑定时器sys_check_timeouts()必须在主循环里周期调用ethernetif_input()也得主循环里轮询。有人把这两句写进while(1)却忘了喂看门狗跑几分钟复位一次日志里看不出原因查半天才发现是主循环被HAL_Delay卡住了。RTOS 下则相反ethernetif_input一般放进一个独立任务用信号量等 DMA 中断释放sys_check_timeouts由tcpip_thread内部处理不需要手动调。两种跑法的区别不在代码量而在「谁负责轮询」搞混了就会出现「裸机代码搬进 RTOS网口时通时不通」这种玄学问题。4.3 raw API 和 socket API 怎么取舍LWIP 提供三层接口最底层的raw API回调式、中间的netconn API阻塞式、最上层的socket API兼容 BSD。F407 这种资源我一般用netconn理由是它比 raw 好读netconn_accept、netconn_recv是顺序代码不用拆成回调又不像 socket 那样多占一层文件描述符表。/* netconn 写一个 TCP echo收到什么原样发回去 */ struct netconn *conn, *newconn; err_t err; conn netconn_new(NETCONN_TCP); netconn_bind(conn, IP_ADDR_ANY, 5000); /* 监听 5000 端口 */ netconn_listen(conn); while (1) { err netconn_accept(conn, newconn); /* 阻塞等连接需在 RTOS 任务里跑 */ if (err ERR_OK) { struct netbuf *buf; while (netconn_recv(newconn, buf) ERR_OK) { netconn_write(newconn, buf-p-payload, buf-p-tot_len, NETCONN_COPY); netbuf_delete(buf); } netconn_close(newconn); netconn_delete(newconn); } }netconn_write的第四个参数NETCONN_COPY表示协议栈会把数据拷进自己的发送缓冲再返回调用者可以直接复用原 buffer如果写成NETCONN_NOCOPY调用者必须保证 buffer 在发送完成前不被改写很多「发出去的数据被篡改」的事故就是这里用错。netconn_accept是阻塞调用裸机主循环里调它会把整个系统卡死这也是为什么裸机阶段建议先用 raw API 的tcp_accept回调跑通后再上 RTOS 换 netconn。5. 移植避坑与常见问题排查网口不亮、ping 不通、跑一会儿就挂这一章是我这几年踩过的坑里挑出来的高频项按「现象 → 原因 → 解决」记。网口问题有个特点现象都长得很像原因却分散在硬件、驱动、协议栈三层只看现象猜不出层得用示波器、寄存器读值和抓包三条线交叉定位。5.1 链路层排查灯不亮、自协商降速、CRC 错包现象一网线插上DP83848 的两个 LED 都不亮MCU 侧读 BSR 的 link 位始终为 0。原因通常是 REF_CLK 没有送到或者频率不对。DP83848 在 RMII 模式下没有 50MHz 参考时钟就不工作LED 也不会亮另一种情况是 PHY 的复位脚RESET_N没有被拉高芯片一直处于复位态。解决先用示波器量 PA1REF_CLK是否有 50MHz、幅度是否到 3.3V再量 PHY 的 RESET_N 在上电 10ms 后是否为高。如果是 MCU 供时钟的接法检查 MCO1 是否真的使能__HAL_RCC_MCO1_CONFIG的宏在部分 HAL 版本里名字有差异编译过了不代表寄存器写了。现象二灯亮了但 PHY 自协商结果一直是 10M 半双工ping 延迟忽高忽低。原因多半是 RMII 的 TXD/RXD 有引脚虚焊或者ETH_MACMIIAR的 MDC 分频过大导致读写 PHY 寄存器偶发失败自协商没跑完就退到了最低速率。解决读 PHY 的第二个状态寄存器地址 0x11 附近的厂商寄存器看自协商完成位同时用万用表通断档量 PC4、PC5、PB12、PB13 四根数据线到 PHY 焊盘的通断。我遇到过一块板子 PC5 虚焊现象就是只能 10M补焊后立刻变 100M 全双工。现象三ping 通但大包丢、小包正常Wireshark 里能看到 CRC 错误帧。原因是PBUF_POOL_BUFSIZE给小了或者 DMA 接收缓冲没按 4 字节对齐。F407 的 ETH DMA 要求描述符地址 4 字节对齐缓冲区如果按uint8_t buf[1524]定义在结构体里编译器可能只保证 1 字节对齐DMA 搬过来的数据就会错位。解决缓冲数组加__attribute__((aligned(4)))PBUF_POOL_BUFSIZE给到 1536同时确认ETH_RXBUFNB至少 4 个单缓冲在突发流量下必然溢出。5.2 协议栈层排查ping 不通、DHCP 拿不到地址、跑几分钟死机现象四链路灯正常、PHY 自协商 100M 全双工但同网段 PC ping 不通板子。原因按概率排第一low_level_init里漏了NETIF_FLAG_ETHARP板子不回 ARP第二MAC 地址全 0 或与 PC 冲突第三netif_set_up和netif_set_link_up只调了一个。解决在ethernetif_input入口加一句串口打印看有没有收到 ARP 请求帧收到但没回就是 flags 的问题一帧都没收到回头查 DMA 接收描述符的所有权位有没有被重新置位。现象五跑十几分钟到半小时不等系统死机或网口彻底失联复位后又能跑。这是内存耗尽不是玄学。常见原因有三个pbuf申请后异常分支没pbuf_freenetconn_recv拿到的netbuf没netbuf_deleteMEMP_NUM_TCP_SEG给小了高并发下tcp_write返回ERR_MEM但代码没处理一直重试把堆吃空。解决把MEMP_NUM_PBUF、MEMP_NUM_TCP_SEG各加 8 试试同时用mem_perf或者简单的mem_malloc前后计数打印确认是不是只增不减。LWIP 自带的stats显示LWIP_STATS1在串口上打mem和memp两行跑一小时看曲线最直观。现象六DHCP 一直超时静态 IP 却能通。原因通常是LWIP_DHCP1但没调dhcp_start或者 DHCP 的 UDP 端口 67/68 被防火墙挡了还有一种少见情况是sys_now返回的不是毫秒DHCP 的重传定时器算出来是几万秒看起来就是永远不重发。解决确认dhcp_start(netif)在netif_add之后调用sys_now用HAL_GetTick返回路由器侧放行 UDP 68。调试期我一般先用静态 IP 把链路和协议栈跑通最后再开 DHCP能省掉一大段排查。6. 装完怎么验证从 PHY 寄存器到 TCP echo 的三级验收环境装完不等于跑通我习惯按三级验收走一遍每一级都有明确的通过标准任何一级不过就停在那一级查不带着问题往上叠。这套流程在几块不同板子上都验证过照着做基本能定位到层。验收级别检查项通过标准一级PHY读 BSR看 link 位和自协商结果bit21速度为 100M 全双工二级网络层PC 上 ping 板子固定 IP4 包全通延迟 2ms无丢包三级传输层netconn echo 收发 1KB 数据内容一致连续 100 次无错第一级用串口打印加几行代码就够/* 上电后打印 PHY 状态确认链路和速率别等 ping 不通再回头查 */ uint16_t bsr 0, phyaddr (uint16_t)(DP83848_ADDR 5); HAL_ETH_ReadPHYRegister(heth, phyaddr | PHY_BSR, bsr); printf(PHY BSR 0x%04X, Link %s\r\n, bsr, (bsr 0x0004) ? UP : DOWN); /* 再读自协商结果寄存器bit13 表示 100M 全双工 */ uint16_t anlpar 0; HAL_ETH_ReadPHYRegister(heth, phyaddr | 0x05, anlpar); printf(ANLPAR 0x%04X\r\n, anlpar);第二级在 PC 上执行注意先确认 PC 和板子在同一网段且 PC 的防火墙允许 ICMP 入站否则会把防火墙当成板子的问题查半天。# -c 4 只发 4 包-W 1 单包超时 1 秒避免无限等 ping -c 4 -W 1 192.168.1.100 # 大包验证-s 1400 接近 MTU能过说明 pbuf 和 DMA 缓冲够大 ping -c 4 -s 1400 192.168.1.100第三级用netconn的 echo 服务配合 PC 端的nc发一条长字符串看回显是否完整。这一步过了说明low_level_output、pbuf链、TCP 窗口三者都正常。如果要压吞吐可以在板子上跑 iperf 的 lwip 移植版但 F407 上 CPU 会先到瓶颈实测数值参考意义有限不如看连续 100 次 echo 有没有丢。从那以后我每次移植这套组合都强制先跑「读 BSR → ping 小包 → ping 大包 → echo 100 次」这四步任何一步不过就不往下写业务代码。血泪经验是带着链路问题往上叠协议最后出问题的位置和真正的原因隔着三层排查成本翻十倍。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Transformer核心逻辑与魔改实践:注意力、残差与位置编码的细节

Transformer核心逻辑与魔改实践:注意力、残差与位置编码的细节

一个经常魔改神经网络架构的人,自然免不了反复拆解和重组各种模型组件。Transformer这个架构,可以说是绕不开的“工作对象”。说实话,拆过的网络多了,你会发现很多结构都是纸老虎,换几个数据集就现原形。但Transformer…

📅 2026/10/26 12:47:01
信息学奥赛排名题解析:Dense Rank与Ordinal Rank双解法

信息学奥赛排名题解析:Dense Rank与Ordinal Rank双解法

1. 题目本质拆解:这根本不是一道“排名题”,而是一道隐性排序规则识别题 看到标题“东方博宜 1542 - 小X算排名 共2种答案”,很多刚接触信息学奥赛模拟题的同学第一反应是:“哦,排序题,套个sort函数完事”。…

📅 2026/10/26 12:42:00
GitHub Copilot 实战避坑指南:从补全工具到靠谱结对伙伴

GitHub Copilot 实战避坑指南:从补全工具到靠谱结对伙伴

AI编程工具这两年从"尝鲜玩具"变成了不少团队日常开发流程里绕不开的一环,GitHub Copilot 算是其中被讨论最多、也最容易被误用的一个。我前后在几个不同规模的项目里用它写业务代码、补测试、啃陌生框架,踩过的坑和攒下的经验都不少。这篇不打…

📅 2026/10/26 12:42:00
MORE NEWS

更多资讯

📰

大模型与AIGC技术发展现状及厂商选型指南

1. 大模型与AIGC技术发展现状 2023年被称为"生成式AI元年",大模型和AIGC技术已经从实验室走向产业化应用。根据最新行业报告显示,全球AI市场规模预计在2025年将达到1900亿美元,年复合增长率超过30%。在这个快速发展的赛道上&#x…

📰

MEA与双层BP神经网络混合优化方案解析

1. 项目概述:当群智能遇上神经网络在机器学习领域,BP神经网络作为经典算法已经服役三十余年,但其固有的局部最优和收敛速度问题始终困扰着从业者。去年我在一个工业预测项目中,就曾因为传统BP网络反复陷入局部最优而连续加班两周。…

📰

西门子1200PLC随动程序实战:从架构设计到避坑验收

简介:这份资源面向已具备一定S7-1200基础的自动化工程师与运动控制学习者,围绕编码器反馈下的随动控制展开,解决电机如何实时跟随参考信号、实现位置与速度精确校正的问题。内容涉及高速计数器采集编码器信号、脉冲输出驱动伺服或步进电机、P…

📰

AI工程师必备:从理论到实践的算法实现四阶训练法

1. 项目概述:当AI遇上动手实践三年前我在一次技术沙龙上听到一个观点:"AI工程师的代码能力正在退化"。当时觉得这个说法有些危言耸听,直到去年review团队项目时发现,许多同事的解决方案清一色都是"调API拼prompt&q…

📰

Token工厂生死局:大模型时代数据标注行业如何突围

开头 前一阵子我带着"Token工厂到底是门什么生意"这个问题,陆陆续续和十几个数据服务团队的创始人、项目经理聊了一圈。说实话,聊完之后我心里很不舒服——这个行业在过去两年被大模型带上了风口,无数人冲进去想分一杯羹&#xff0…

📰

AI编码时代程序员价值重构:从写代码到定义问题、编排Agent工作流与质量兜底

1. AI 编码时代,程序员的价值锚点到底在哪1.1 先搞清楚一个事实:AI 写代码这件事,到底走到了哪一步这两年我身边不少同行都在焦虑同一件事:AI 都能写代码了,我还要不要继续干这行?尤其是各种编码助手、Agen…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬