news 2026/10/15 16:52:28

STM32F4驱动W5500硬协议栈芯片:SPI配置与TCP回环测试详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F4驱动W5500硬协议栈芯片:SPI配置与TCP回环测试详解

简介:面向STM32F4开发者与嵌入式产品工程师的W5500以太网控制器测试压缩包,主要解决芯片驱动、SPI通信与全硬件TCP/IP协议栈的快速集成问题,适用于物联网、工业控制及数据采集设备的原型验证与二次开发。包体共有1256个文件、约10.86MB,以378个C语言源文件、95个C头文件和433个脚本文件为主,辅以155张图片、67个动态示意、6个预编译库及PDF/CHM格式文档;C源码与头文件对应协议实现、外设驱动和接口定义,脚本与图片则适合状态展示或配置参考,Keil工程文件可直接打开。已有1055人浏览学习,内容涵盖Keil工程模板、启动汇编、烧录文件、网络参数配置说明等,可对照源码理解初始化流程、数据收发逻辑与移植要点,相当于一套可复用的W5500开发底座。包内整体结构清晰,能帮助工程师快速搭建STM32F4网络应用原型并验证功能,显著降低底层协议栈移植与调试的时间成本。

1. 认识 W5500 这个硬协议栈芯片:给 STM32F4 的可复现基础

做嵌入式网络开发的人,多半在 STM32F4 上被 ETH 外设和 LwIP 的组合折磨过:MAC 地址滤波、描述符链表、内存池对齐、中断回调,稍不留神就进 HardFault,调一晚上不改一行代码,只换来一个“数组越界”。W5500 这颗芯片的思路则完全不同:它把 MAC、PHY 和整个 TCP/IP 协议栈全塞进一颗芯片里,MCU 侧只通过 SPI 读写寄存器,TCP、UDP、ICMP 的状态机全部由硬件维护。换句话说,你在 STM32F4 上写的不是网络协议栈,只是一个 SPI 从机的读写器。

这份 W5500Test-20180314 资源包,就是围绕“STM32F4 主控 + W5500 从机”这套组合展开的测试工程。它解决的问题很具体:如何快速把 W5500 挂到 F4 的 SPI 上,如何完成上电初始化、MAC 与 IP 配置、Socket 的打开与收发,以及如何用 ping 和回环测试验证链路通不通。适合正在做串口转以太网、数据采集网关、远程控制模块的人,也适合想绕开 LwIP、直接体验硬协议栈方案的开发者。下面我会从硬件接线开始,一路拆到寄存器操作和排错经验。

2. 硬件上电:引脚映射、SPI 速率和 W5500 选型边界

2.1 先看 W5500 在电路里的位置

W5500 不是一个独立的网络接口芯片那么简单。它的内部集成了 10/100M 以太网 MAC 和 PHY,外部只需要再接一个带隔离变压器的 RJ45 座子,就能在硬件层面完成“以太网物理层”的全部工作。芯片通过 SPI 从机接口与主机 MCU 通信,支持最高 33.3 MHz 的 SPI 时钟,但实际项目中很少真的跑到这个上限,原因后面排错章会讲。

在 STM32F4 平台上,我一般分配 SPI1 或 SPI2 给 W5500,原因有两个:一是这两个 SPI 在 F4 上支持较高的时钟频率,二是引脚在板级上比较好走线。拿 SPI2 举例,典型映射是:

  • PA4 :SPI2_NSS(用 GPIO 控制,不用硬件 NSS)
  • PA5 :SPI2_SCK
  • PA6 :SPI2_MISO(接 W5500 的 MOSI)
  • PA7 :SPI2_MOSI(接 W5500 的 MISO)
  • PA2 或 PA3 :W5500 的 INT 引脚,配置为外部中断输入或 GPIO 轮询

很多现成模块把 W5500 的引脚做成 2.54mm 排针,直接对插杜邦线也能跑起来,但要注意 3.3V 电平的一致性。F4 的 GPIO 和 W5500 都是 3.3V 供电,理论上可以直接连,只是信号线不宜太长,我踩过 15cm 杜邦线在 20 MHz SPI 下直接读回全 0 的坑,后来把所有信号线压到 10cm 以内才恢复正常。

2.2 SPI 参数配置:这条总被人抄错

STM32 的 SPI 外设初始化里有几个参数经常被抄错,导致 W5500 读回的数据全是 0xFF 或 0x00。常见做法是主机模式、8 位数据、MSB 先行,关键在时钟极性和相位:

SPI 参数W5500 要求典型错误
CPOL低电平空闲(0)误配为高电平空闲
CPHA第二个边沿采样(1)误配为第一个边沿采样
数据宽度8 位配成 16 位后读 ID 全错
NSS软件管理用硬件 NSS 导致多设备冲突
SPI 时钟建议 ≤ 10 MHz 起步一上来跑 33.3 MHz 丢字节

这里给出一份可直接抄的 SPI 初始化代码,在 STM32F4 的 HAL 库下配置 SPI2:

void W5500_SPI_Init(void) { SPI_HandleTypeDef hspi; hspi.Instance = SPI2; hspi.Init.Mode = SPI_MODE_MASTER; hspi.Init.Direction = SPI_DIRECTION_2LINES; hspi.Init.DataSize = SPI_DATASIZE_8BIT; hspi.Init.CLKPolarity = SPI_POLARITY_LOW; hspi.Init.CLKPhase = SPI_PHASE_2EDGE; hspi.Init.NSS = SPI_NSS_SOFT; hspi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi.Init.TIMode = SPI_TIMODE_DISABLE; hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; if (HAL_SPI_Init(&hspi) != HAL_OK) { Error_Handler(); } }

这段代码里最值得注意的两个点:一是 CPOL=0、CPHA=1 的组合,W5500 手册里把它定义为“模式 0 与模式 3 之间的变体”,实际上相当于 SPI Mode 0 的数据格式配合 Mode 3 的采样沿,很多人按 STM32 默认的 Mode 0 去配也能偶尔工作,但长数据帧下必出错;二是分频系数 16,在 168 MHz 的系统时钟下 SPI2 挂在 APB1 上,总线时钟为 42 MHz,分频 16 后得到 2.625 MHz,这个速度作为调试起步足够稳定。等链路验证通过后,再把分频系数逐步调大到 4 或 2。

2.3 硬件复位时序:上电后必须做的第一步

W5500 的复位脚是低电平有效,数据手册要求的复位脉冲宽度是至少 500ns。我通常的做法是:上电后先把复位脚拉低 10ms,再拉高,然后延时 100ms 再开始写寄存器,而不是上电立即操作。这个延时的作用是等 W5500 内部的 PLL 锁定和 PHY 上电稳定。下面的代码展示了一个带超时判断的复位流程:

void W5500_Hardware_Reset(void) { HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET); HAL_Delay(100); uint8_t id_ver = 0; id_ver = W5500_Read_Byte(0x0000); // 读取版本寄存器 VERSIONR if (id_ver != 0x04) { // 版本号应为 0x04,读到其他值说明 SPI 时序或接线有问题 while (1); } }

这段代码把版本校验放在了复位之后、初始化之前,是一个很实用的自检手段。W5500 的版本寄存器地址是 0x0000,固定返回 0x04,如果 SPI 时序不对、引脚接错或电平不匹配,读回来的值不会是 0x04,程序直接停在 while(1) 里,比后面初始化到一半才发现问题要容易定位得多。

3. 寄存器堆与收发流程:把 TCP/IP 状态机留给芯片

3.1 三种寄存器访问方式:直接寻址、间接寻址和 Socket 寻址

W5500 的寄存器空间分为三大块:基础寄存器(Common Register)、Socket 寄存器和发送/接收缓冲存储器。用一句话概括访问规则:读写普通寄存器用 3 字节地址,读写 Socket 寄存器要带上 Socket 号,而收发缓冲区则通过寄存器的指针间接操作。理清这三者的区别,整个驱动就完成了一半。

这里给出读写单字节的基础函数,注意 W5500 的 SPI 帧格式是“地址段 + 控制段 + 数据段”,控制段的 bit4 表示读或写,bit2:0 表示要访问的区块:

uint8_t W5500_Read_Byte(uint16_t addr) { uint8_t tx_buf[3]; uint8_t rx_buf[1]; tx_buf[0] = (addr >> 8) & 0xFF; // 地址高 8 位 tx_buf[1] = addr & 0xFF; // 地址低 8 位 tx_buf[2] = 0x00; // 控制段:读操作,访问 Common Register W5500_CS_LOW(); HAL_SPI_Transmit(&hspi2, tx_buf, 3, 100); HAL_SPI_Receive(&hspi2, rx_buf, 1, 100); W5500_CS_HIGH(); return rx_buf[0]; } void W5500_Write_Byte(uint16_t addr, uint8_t val) { uint8_t tx_buf[4]; tx_buf[0] = (addr >> 8) & 0xFF; tx_buf[1] = addr & 0xFF; tx_buf[2] = 0x04; // 控制段:写操作,访问 Common Register tx_buf[3] = val; W5500_CS_LOW(); HAL_SPI_Transmit(&hspi2, tx_buf, 4, 100); W5500_CS_HIGH(); }

控制段的 bit4(即 0x10)是读/写标志位,置 1 为读,置 0 为写;低三位选择区块,0 表示 Common Register,1 表示 Socket 0 寄存器,依此类推。很多人的驱动在“读”时忘了把 bit4 置 1,结果读回来的永远是上一次写进去的数据或全 0,这一类错误最迷惑人,因为 bug 完全不在 SPI 时序上。

3.2 Socket 缓冲区的读写:绕过一家伙的经典误区

W5500 的收发缓冲区每个 Socket 分配了独立的发送和接收区域,默认配置下每个方向 2KB。MCU 要发数据时,不是直接把数据“推”给 W5500,而是先把数据写到自己的发送缓冲区,再调整写指针,最后触发 SEND 命令,让芯片内部的 TCP 状态机去把数据打包发出去。这一套流程在代码里体现为:读空闲大小、写入缓冲、更新写指针、发送命令。

uint8_t W5500_Send_Buffer(uint8_t sock, uint8_t *data, uint16_t len) { uint16_t free_size = 0; uint16_t ptr = 0; uint16_t offset = 0; free_size = W5500_Read_16Bit(sock, Sn_TX_FSR_REG); // 读取发送空闲大小 if (free_size < len) { return 0; // 空闲空间不足,稍后重试 } ptr = W5500_Read_16Bit(sock, Sn_TX_RD_REG); // 读取写指针 offset = ptr & (TX_BUF_MASK); // 2KB 缓冲区的掩码 // 分两段写入,处理缓冲区绕回 if (offset + len > TX_BUF_SIZE) { uint16_t part1 = TX_BUF_SIZE - offset; W5500_Write_Bytes(sock, TX_BUF_BASE + offset, data, part1); W5500_Write_Bytes(sock, TX_BUF_BASE, data + part1, len - part1); } else { W5500_Write_Bytes(sock, TX_BUF_BASE + offset, data, len); } // 更新写指针 W5500_Write_16Bit(sock, Sn_TX_WR_REG, (ptr + len) & 0xFFFF); // 触发 SEND 命令 W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_SEND); return 1; }

这段代码里的缓冲区绕回处理是个高频翻车点。W5500 的收发缓冲区在地址空间上是连续的,但物理上被分成了两块,当写入位置接近末尾时,数据需要拆成两段写入,否则会把尾部数据写到缓冲区之外,覆盖掉其他 Socket 的数据。我在一个实际项目里就遇到过:Socket 0 发包偶尔失败,排查了很久才发现是 Socket 0 的发送缓冲区尾部越界写到了 Socket 1 的缓冲区,把 Socket 1 的接收数据全搅乱了。

3.3 Socket 命令状态机:打开、连接、收发的完整时序

W5500 的每个 Socket 都有一组命令寄存器,通过写入特定命令字驱动状态机迁移。TCP 客户端模式下的典型流程是:打开 Socket → 设置为 TCP 模式 → 连接远端 → 等待建立 → 收发数据。每一个命令发出后,不能立即执行下一个命令,而是需要轮询 Sn_SR 寄存器确认状态已经迁移到位。

uint8_t W5500_Socket_Connect(uint8_t sock, uint8_t *ip, uint16_t port) { uint16_t status = 0; // 关闭可能残留的旧连接 W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_CLOSE); while (W5500_Read_16Bit(sock, Sn_SR_REG) != SOCK_CLOSED); // 打开 Socket,设置为 TCP 模式 W5500_Write_Byte(sock, Sn_MR_REG, Sn_MR_TCP); W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_OPEN); while (W5500_Read_16Bit(sock, Sn_SR_REG) != SOCK_INIT); // 写入远端 IP 和端口 W5500_Write_IP(sock, Sn_DIPR_REG, ip); W5500_Write_16Bit(sock, Sn_DPORT_REG, port); // 发起连接 W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_CONNECT); status = W5500_Read_16Bit(sock, Sn_SR_REG); while (status != SOCK_ESTABLISHED) { if (status == SOCK_CLOSED) { return 0; // 连接失败,目标主机不可达或端口被拒 } HAL_Delay(1); status = W5500_Read_16Bit(sock, Sn_SR_REG); } return 1; }

这段代码体现了 W5500 驱动与 LwIP 驱动的本质差异:LwIP 里你写 socket 调用,底层协议栈在 MCU 上跑,你需要关心超时重传、滑动窗口这些机制;W5500 则把这些全部屏蔽了,你只需要盯着 Sn_SR 的状态字段,等待它从 SOCK_INIT 跳到 SOCK_ESTABLISHED。这个“等待状态迁移”的轮询逻辑是整个驱动性能的关键,轮询间隔太密会浪费 SPI 带宽,太疏则响应变慢。我一般用 HAL_Delay(1) 做基础轮询间隔,实测在 10 MHz SPI 下,TCP 连接建立时间在 5ms 以内。

4. 回环测试:用 Ping 延时和 TCP 回环验证资源包能否正常工作

4.1 构建最小测试工程:验证前期准备是否到位

拿到 W5500Test 这套测试代码后,我不建议直接往自己的板子上烧录,而是先做一个最小验证:把 W5500 模块、STM32F4 最小系统板、USB 转串口工具这三样拼在一起,跑一个最基础的回环测试。先看串口打印的初始化日志是否正常,再用 PC 端 ping 这个模块的 IP,一步步确认 SPI 通信、PHY 链路和协议栈状态是否都正常。

在这个阶段,我们需要配置一个静态 IP,这一段代码是必须的。注意 W5500 的 IP 配置在 Common Register 区,与 Socket 无关:

void W5500_Network_Config(uint8_t *ip, uint8_t *gw, uint8_t *mask, uint8_t *mac) { // 写入 MAC 地址 W5500_Write_IP(0x0009, mac); // SHAR 寄存器,起始地址 0x0009 // 写入网关地址 W5500_Write_IP(0x0001, gw); // GAR 寄存器,起始地址 0x0001 // 写入子网掩码 W5500_Write_IP(0x0005, mask); // SUBR 寄存器,起始地址 0x0005 // 写入本机 IP W5500_Write_IP(0x000F, ip); // SIPR 寄存器,起始地址 0x000F }

这段代码里用到的地址偏移是 W5500 手册里的标准地址,GAR(网关地址寄存器)在 0x0001,SUBR(子网掩码寄存器)在 0x0005,SHAR(MAC 地址寄存器)在 0x0009,SIPR(源 IP 寄存器)在 0x000F。要注意的是,MAC 地址必须是全局唯一的,建议从自己网卡的 MAC 段里挑一个没用过的,或者用 STM32 的 UID 做哈希生成,而不是随便填一个 FF:FF:FF:FF:FF:FF,那会直接导致链路层通信异常。

4.2 实测 Ping 延时的判断标准

配置好 IP 后,把 STM32F4 通过 USB 线连到电脑,W5500 的 RJ45 口用网线直连电脑或接到交换机上。在电脑上执行 ping 命令,观察返回值。正常的判断标准是:ping 的通,延迟在 1ms 以内,丢包率为 0。如果你的延迟在 10ms 以上甚至上百 ms,多半不是网络拥塞,而是 SPI 配置有性能瓶颈,或者代码里的轮询逻辑有阻塞。

下面列出我在调试中维护的一份“Ping 结果速查表”,供你对照参考:

Ping 现象可能原因处理方向
请求超时 100%PHY 链路未建立,RJ45 灯不亮检查网络变压器接线、复位时序、供电
延迟稳定在 1~3msSPI 分频系数过大导致带宽不足调小分频系数,观察是否有丢包
延迟波动超过 20ms代码中轮询逻辑被中断阻塞检查中断优先级、临界区保护是否过度
前几个包通,后面全丢发送缓冲区写指针溢出检查 Sn_TX_WR 指针是否按掩码正确回绕
第一个 ping 通延迟特别大ARP 缓存未命中,属正常现象确认第二次 ping 延迟恢复正常即无问题

这里的“第一个 ping 通延迟特别大”是很多人误判的一个点。第一次 ping 时,目的主机的 MAC 地址需要先经过 ARP 请求解析,整个过程可能消耗几十 ms,这是以太网机制的正常现象,不代表 W5500 配置有问题。我见过有人在群里因为第一次 ping 是 30ms 就反复重刷固件,其实完全没必要,只要后续 ping 稳在 1ms 以内就说明链路是通的。

4.3 TCP 回环测试的代码骨架

Ping 只能证明 IP 层是通的,证明传输层(TCP 或 UDP)还需要更进一步的测试。一个简单有效的做法是:在 W5500 上开一个 TCP Server,监听一个端口(比如 5000),PC 端用网络调试助手连接后往里发什么,就原样返回什么。回环测试通过后,再往自己的业务逻辑上叠加。

uint8_t W5500_TCPServer_Test(uint8_t sock, uint16_t port) { uint8_t buf[256]; uint16_t recv_len = 0; uint16_t status = 0; // 配置为 TCP Server 模式 W5500_Write_Byte(sock, Sn_MR_REG, Sn_MR_TCP); W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_OPEN); W5500_Write_16Bit(sock, Sn_PORT_REG, port); W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_LISTEN); while (1) { status = W5500_Read_16Bit(sock, Sn_SR_REG); if (status == SOCK_ESTABLISHED) { recv_len = W5500_Recv_Buffer(sock, buf, sizeof(buf)); if (recv_len > 0) { // 原样返回接收到的数据 W5500_Send_Buffer(sock, buf, recv_len); } } else if (status == SOCK_CLOSED) { // 客户端断开连接,回到监听状态 W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_LISTEN); } } }

这段代码是一个死循环等待,适合在测试阶段验证收发链路的完整性。Sn_MR_TCP 是 Socket 模式寄存器里 PT 位的设置,打开 Socket 后写入端口号,再执行 LISTEN 命令,芯片内部的 TCP 状态机就会开始监听。当客户端连上来后,状态变成 SOCK_ESTABLISHED,代码就进入接收-回发的循环,这是最直观的“通了没通”的判断方式。

5. STM32F4 上最常遇到的几个坑:现象、原因与解决思路

5.1 读版本号永远是 0xFF,SPI 时序没问题但就是读不到

现象:执行 W5500_Hardware_Reset 里的版本读取,返回的值是 0xFF,不是期望的 0x04。用示波器看 SPI 波形,CLK、MOSI 都有,MISO 却一直处于高电平。

原因:这个现象我遇到过两次,第一次是 CS 引脚根本没有拉低,第二次是 W5500 芯片供电电压不足,导致内部数字逻辑没有复位完成。CS 在 SPI 通信的整个帧期间必须保持低电平,很多人用 GPIO 控制 CS,但在 HAL_SPI_Transmit 和 HAL_SPI_Receive 之间把 CS 拉高了,W5500 会把一帧数据拆成两次传输,第二段的数据全部丢失。

解决:把 CS 的控制改成一个函数封装,在连续传输的整个过程中只拉低一次。同时用万用表量一下 W5500 的 VCC 引脚,低于 3.0V 就必须检查供电电路,W5500 内部的 PHY 部分电流消耗不小,用 STM32 板载的 3.3V LDO 直供可能导致电压跌落。

5.2 Ping 通但 TCP 连接建立不了,Sn_SR 一直卡在 SOCK_INIT

现象:模块能 ping 通,电脑端用网络调试助手开 TCP 客户端去连接 W5500 监听的端口,状态一直停留在 SYN_SENT,W5500 侧的 Sn_SR 寄存器也一直没有跳变成 SOCK_ESTABLISHED。

原因:我排查过的一个实际案例,问题出在端口号的字节序上。W5500 的寄存器空间是大端字节序存储,即高字节在前,低字节在后。如果你的端口号是 5000(十进制),十六进制是 0x1388,写入 Sn_PORT_REG 时应该先写 0x13,再写 0x88。如果按小端序写入了 0x88 0x13,W5500 实际监听的端口就变成了 0x8813,也就是 34835,与电脑端连接的 5000 端口对不上,自然建不了连接。

解决:排查时先打印 Sn_PORT_REG 读回来的值,确认端口号的高低位。这也是我建议所有写 W5500 驱动的人在封装读写函数时直接把高低字节顺序固定死的原因,不要在业务代码里一遍遍手动调整字节序。

5.3 发送数据偶尔失败,失败间隔没有规律

现象:同一套代码,发送大量小包时偶尔有数据发不出去,串口打印的失败日志没有固定周期。用抓包工具看,发现 W5500 确实把数据发出来了,但包的 Ethernet Type 字段偶尔会变成错误的值,抓包的解析直接失败。

原因:这是发送缓冲区的写入指针与芯片内部的 DMA 读指针产生了竞争。W5500 内部有硬件 DMA,会在你写数据的同时从发送缓冲区读取要发送的数据,如果你的代码更新 Sn_TX_WR 指针的时机不对,DMA 可能读到半包数据。

解决:在写入数据之前,先读 Sn_TX_FSR(发送空闲大小)确认缓冲区的空闲空间足够。这个寄存器返回的是已经可用的字节数,不是缓冲区总量,有些驱动只做了一次掩码计算就认为空间够用,忽略了 FSR 本身的含义。在写 Sn_TX_WR 和触发 SEND 命令之间增加一个 SPI 读写屏障,确保寄存器更新顺序不会被编译优化或指令重排打乱。

5.4 复位后第一次初始化成功,第二次初始化失败

现象:代码里执行完一次完整的网络收发后,把 Socket 关闭、重新初始化,程序的第二个连接再怎么也建不起来,必须重新上电才能恢复。

原因:这是典型的缓冲区指针没有归零问题。W5500 的 Sn_RX_RD 和 Sn_TX_WR 指针在一次完整的通信后移动到了某个位置,但你没有把它们复位到缓冲区起始地址,就直接执行了 OPEN 命令。芯片认为缓冲区还是满的或脏的,拒绝写入新的数据。

解决:在 Socket 关闭后,强制把 Sn_RX_RD 和 Sn_TX_WR 都写回与其对应的缓冲区首地址,同时调一次 Sn_CR_CLOSE 命令让芯片内部的状态机完全复位。我一般会在 W5500_Socket_Close 函数里追加一个指针复位操作,代码像这样:

void W5500_Socket_Close(uint8_t sock) { W5500_Write_Byte(sock, Sn_CR_REG, Sn_CR_CLOSE); W5500_Write_16Bit(sock, Sn_RX_RD_REG, RX_BUF_BASE); W5500_Write_16Bit(sock, Sn_TX_WR_REG, TX_BUF_BASE); HAL_Delay(1); }

这个坑最隐蔽的地方在于:你不一定会立刻翻车,可能连续运行一两个小时才暴露一次。因为它只有在接收或发送指针恰好停在缓冲区末尾附近时才会触发,而大部分时间指针都在缓冲区中间区域,看起来一切正常。

6. 调试技巧:从日志到抓包,把 W5500 驱动问题定位缩小到一层

调试 W5500 驱动时,我的习惯是强制自己遵循一个流程:先看物理层(PHY 是否建立链接),再看 SPI 传输层(寄存器读写是否正常),再看 Socket 状态机,最后才看业务逻辑。很多人一上来就用网络调试助手发包,一旦不通就直接改业务代码,结果绕了大半天,最后发现是 SPI 的 CPHA 配错了。

串口日志是 W5500 调试里最廉价的工具,但需要控制好输出量。我见过有人在中断服务函数里打印日志,直接把 SPI 时序打乱了,因为打印一个字符的时间比传输一帧 SPI 数据还长。要打印就放在主循环里,而且只打印状态变化,不打印每一包数据的内容。

抓包工具方面,推荐用 Wireshark 在 PC 端抓包,观察三个关键点:ARP 请求/响应是否正常、TCP 三次握手的 SYN/SYN-ACK/ACK 是否成对出现、数据包的窗口大小是否符合预期。如果 TCP 握手的 SYN 包发出后对方没有回应,问题多半在路由或防火墙;如果 SYN-ACK 回来了但 W5500 没有发出最后的 ACK,问题多半在 W5500 侧的状态机处理上,重点检查 Sn_CR 是否有残留的命令字没有清掉。

网络调试助手测试时,我建议每次更改代码后都重启 W5500 的电源,而不只是按一下复位键。W5500 的 PHY 在热复位时偶尔会进入未知状态,导致端口指示灯亮了但链路层不通,断电重新上电通常能解决。这个习惯帮我避免了很多“代码没变但行为变了”的玄学问题。

从那以后,我每次拿到一块新的 W5500 模组,都会先跑一遍版本读取和回环测试,确认芯片本身没坏、SPI 通路是干净的,再往自己的业务代码上叠加。这套流程看起来很慢,但省下来的排查时间往往以小时计。希望这份拆解对你上手 W5500 有帮助,遇到奇怪问题的时候,记得先拿示波器看一眼 CS 信号。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:52:27

AT24C02进阶实战:I2C时序、页写回卷与掉电保存避坑指南

1. 这个存储器为什么让很多人翻车做蓝桥杯嵌入式的同学&#xff0c;基本都会在某个晚上跟AT24C02杠上。这个芯片看着不起眼&#xff0c;价格便宜、容量也只有256字节&#xff0c;但它在竞赛里的出镜率极高&#xff0c;尤其到了“像掉电保存、参数设置、运行状态记录”这类题目要…

作者头像 李华
网站建设 2026/10/15 16:52:26

从裸数据到驾驶舱:MQTT+WebSocket遥测可视化系统实战

1. 一个副驾驶视角&#xff1a;Apollo-11想解决的真实问题能做 Apollo-11 这个项目&#xff0c;纯粹源于我对飞行仪表盘的执念&#xff1a;那一排排动态跳动的数字、渐变色的燃料条、末端急剧跳变的高度值&#xff0c;画面对我来说有不可抗拒的吸引力。市面上的装饰品很多&…

作者头像 李华
网站建设 2026/10/15 16:51:24

基于SSM的JavaWeb在线订餐系统:架构、事务与二次开发拆解

简介&#xff1a;面向JavaWeb课程设计与毕业设计的必胜客在线订餐系统项目&#xff0c;完整模拟从用户注册登录、菜品浏览、购物车到下单支付的在线订餐流程&#xff0c;同时提供后台菜品管理、订单处理与用户信息管理功能&#xff0c;适合学习者对照系统需求练习完整开发链路。…

作者头像 李华
网站建设 2026/10/15 16:50:32

3步搞定C盘瘦身:WindowsClear C盘清理工具快速上手教程

桌面应用系统工具 【免费下载链接】WindowsClear Windows C盘清理工具/C盘瘦身工具&#xff0c;还你Appdata的大量硬盘空间 | Windows System disk cleanup, returning a large amount of hard drive space from your Appdata 项目地址&#xff1a; https://gitcode.com/gh_mir…

作者头像 李华
网站建设 2026/10/15 16:50:00

从仿真到实车:ROS2+Autoware自动驾驶小车完整实战记录

做ROS2小车最难的不是跑通仿真&#xff0c;而是仿真里一切完美&#xff0c;一上车就露馅。这句话我这两年听同行说过无数次&#xff0c;自己也在ROS2Autoware这套组合上反复踩过坑。ROS2解决了机器人通信的实时性和分布式问题&#xff0c;Autoware则把感知、定位、规划、控制这…

作者头像 李华