ESP32 搭配 W5500 有线以太网:SPI 驱动详解与实战
2026/9/24 12:52:45 网站建设 项目流程

1. 为什么我要用 ESP32 搭配 W5500 做有线以太网

搞嵌入式网络通信的朋友大概率都经历过这样的纠结:ESP32 自带的 WiFi 确实方便,但真到了工业现场、长时间跑数据采集、或者对网络抖动特别敏感的场景,无线那点事儿就变得不太靠谱了。丢包、重连、信号衰减、路由器抽风,随便一个都能让你半夜爬起来改代码。我自己就踩过这个坑,一个跑了三天的数据采集终端,WiFi 突然掉线,结果后面两天的数据全断了,客户电话直接打过来。

所以后来我学乖了,凡是固定安装、对稳定性有要求的项目,一律上有线以太网。而 ESP32 加 W5500 这套组合,就是性价比和开发效率都很能打的一个方案。W5500 是 WIZnet 出的一款硬件 TCP/IP 协议栈芯片,内置了完整的 10/100M 以太网控制器和协议栈,通过 SPI 接口跟主控通信。它最大的好处是把 TCP/IP 协议栈用硬件实现了,主控只需要通过 SPI 收发数据就行,不用在软件层面跑一套 lwIP,CPU 占用低,实时性好,而且不容易被网络协议栈的各种 bug 拖累。

这篇文章我打算把 ESP32 的 SPI 驱动从例程层面逐行讲透,然后手把手带你把它跟 W5500 接起来,跑通有线以太网。不管你是刚接触 ESP-IDF 的新手,还是从 Arduino 转过来的老玩家,只要跟着走,基本都能一次点亮。我会把接线、配置、代码、踩坑点全部摊开讲,能直接抄作业的那种。

2. 先把 SPI 这件事彻底搞明白

2.1 SPI 到底是个什么东西,为什么 W5500 要用它

SPI 全称 Serial Peripheral Interface,串行外设接口,是 Motorola 早年搞出来的一种同步串行通信协议。它的核心特点就四个字:全双工、同步。所谓全双工,就是发送和接收可以同时进行;所谓同步,就是靠一根时钟线来对齐数据,不像 UART 那样需要双方约定波特率。

SPI 一共四根线,这是必须记牢的:

  • SCLK:时钟线,由主机产生,决定通信速率
  • MOSI:Master Out Slave In,主机发、从机收
  • MISO:Master In Slave Out,主机收、从机发
  • CS/SS:片选线,低电平有效,主机拉低哪根片选就选中哪个从机

W5500 之所以选 SPI 而不是并口或者 I2C,原因很直接。并口速度虽然快但引脚太多,W5500 这种小芯片不划算;I2C 速度太慢,标准模式才 100kHz,快速模式 400kHz,跑以太网数据吞吐根本不够看。SPI 在引脚数量和速度之间取得了很好的平衡,W5500 支持最高 80MHz 的 SPI 时钟,实际用 20MHz 到 30MHz 就能跑满 10/100M 以太网的带宽需求。

这里有个概念要澄清一下,很多人第一次看 W5500 手册会懵:它虽然叫“硬件协议栈”,但并不是说你完全不用管协议。它内部把 TCP、UDP、ICMP、IPv4、ARP、PPPoE 这些都做成了硬件逻辑,你通过 SPI 读写它的寄存器就能建立 socket、收发数据。但应用层的协议,比如 HTTP、MQTT,还是得你自己在 ESP32 上实现。所以它省的是网络协议栈那部分的 CPU 开销和内存占用,不是省掉你写应用的工作。

2.2 SPI 的四种模式,CPOL 和 CPHA 到底怎么配

SPI 有四种工作模式,由 CPOL(Clock Polarity,时钟极性)和 CPHA(Clock Phase,时钟相位)两个参数组合而成。这两个参数不理解清楚,通信大概率是收不到数据的。

CPOL 决定时钟空闲时是高电平还是低电平:CPOL=0 时空闲为低,CPOL=1 时空闲为高。CPHA 决定数据在时钟的哪个边沿采样:CPHA=0 时在第一个边沿采样,CPHA=1 时在第二个边沿采样。

模式CPOLCPHA空闲电平采样边沿
Mode 000上升沿
Mode 101下降沿
Mode 210下降沿
Mode 311上升沿

W5500 支持 Mode 0 和 Mode 3,这两个模式在实际项目里用得最多。我一般默认用 Mode 0,因为大部分 SPI 设备都兼容它,调试起来也最省心。如果你发现数据读出来全是 0xFF 或者 0x00,第一件事就是检查模式配对了没有。

注意:SPI 模式配错是新手最常见的坑,症状是读寄存器永远返回固定值。遇到这种情况先别怀疑硬件,先把模式换成另一个试试。

2.3 ESP32 的 SPI 外设架构,别被“多总线”绕晕

ESP32 内部有好几组 SPI,新手看手册容易晕。我帮你理一下:

  • SPI0:内部专用,接 Flash,用户碰不到
  • SPI1:内部专用,也接 Flash 相关,用户碰不到
  • SPI2(也叫 HSPI):用户可用
  • SPI3(也叫 VSPI):用户可用

在 ESP-IDF 里,你用的是spi_host这个概念,通过SPI2_HOSTSPI3_HOST来指定。默认情况下,GPIO 矩阵可以把 SPI 信号映射到任意引脚,这给了布线很大的灵活性。但要注意,有些引脚是 strapping 引脚或者接了 Flash,不能随便用,比如 GPIO6 到 GPIO11 通常被内部 Flash 占用,GPIO0、GPIO2、GPIO12 这些是启动模式相关的,用之前要确认。

ESP-IDF 的 SPI 驱动分两层:SPI Master 驱动SPI Slave 驱动。我们驱动 W5500 用的是 Master 模式。Master 驱动又分两种用法,一种是spi_bus_initializespi_bus_add_device的通用方式,另一种是spi_master的老接口。新项目建议直接用spi_bus_initialize这套,更规范,也方便一条总线上挂多个设备。

3. 硬件接线与 W5500 模块选型

3.1 W5500 模块怎么挑,别买到坑货

市面上 W5500 模块价格从十几块到几十块不等,差别主要在晶振、网络变压器和 PCB 布线。我踩过的坑是买过一批便宜模块,晶振用的是劣质 25MHz 无源晶振,跑几天就开始丢包,后来换成带温补的才稳定。选模块的时候注意几点:

  • 晶振必须是 25MHz,负载电容匹配,最好选带金属屏蔽壳的
  • 网络变压器要选正规厂家的,RJ45 座子带不带变压器都行,但一定要有隔离
  • 板子上最好有 3.3V 稳压,有些模块直接吃 5V 再降压,发热大
  • 看有没有引出所有 SPI 引脚和中断引脚,INT 引脚在做中断接收时很有用

我目前用的是 WIZnet 官方的 W5500 Ethernet Shield 兼容板,或者国产的“W5500 以太网模块”带排针那种,实测都还行。关键是别贪便宜买那种连丝印都模糊的。

3.2 ESP32 与 W5500 的接线表

接线这块我直接给你一张表,照着接就行。我用的是 ESP32 DevKitC,SPI 用 SPI2_HOST(HSPI),引脚可以按下面来:

ESP32 引脚W5500 引脚说明
GPIO18SCLKSPI 时钟
GPIO23MOSI主机输出
GPIO19MISO主机输入
GPIO5CS/SS片选,低有效
GPIO4INT中断(可选)
GPIO21RST复位(可选)
3.3VVCC电源
GNDGND共地

这里要说明一下,ESP32 的 GPIO 矩阵允许你把 SPI 信号映射到几乎任意引脚,所以上面这张表不是死的。但 GPIO18、23、19 是 VSPI 的默认引脚,用它们性能最好,因为走的是 IO_MUX 直连,延迟低。如果你换成别的引脚,会走 GPIO 矩阵,速度上限会低一些,跑 20MHz 以上可能会不稳。

提示:CS 引脚一定要用普通 GPIO,不要用 strapping 引脚。INT 和 RST 如果不用可以不接,但建议至少把 RST 接上,方便硬件复位。

3.3 电源和地线的处理,别小看

W5500 工作电流典型值 130mA 左右,峰值能到 180mA。ESP32 本身 WiFi 工作时峰值也有 500mA。如果你用 USB 供电,一定要保证 USB 口能提供足够电流,最好用带外部供电的 USB Hub 或者直接上 5V 2A 的电源适配器。

地线方面,SPI 是高速信号,地线一定要短而粗。我一般会在 W5500 的 VCC 和 GND 之间并一个 10uF 钽电容加一个 0.1uF 陶瓷电容,滤高频噪声。如果通信不稳定,先检查电源纹波,用示波器看一下 3.3V 上有没有超过 100mV 的纹波。

4. ESP-IDF 环境搭建与工程配置

4.1 装 ESP-IDF,版本选择有讲究

ESP-IDF 的版本更新挺快,我建议用稳定版,比如 v5.1 或 v5.2。太老的版本对 W5500 的 SPI 驱动支持不够完善,太新的又可能有 API 变动。安装方式我推荐用官方安装器或者 git clone 加 install.sh,Windows 上也可以用 VS Code 的 ESP-IDF 插件,那个最省事。

装完之后,在终端里跑一下idf.py --version,能输出版本号就说明环境 OK。然后idf.py set-target esp32设置目标芯片。如果你用的是 ESP32-S3 或者 C3,SPI 的 host 编号和引脚会不一样,要注意区分。

4.2 工程结构,从例程改起最快

别从零建工程,直接拿 ESP-IDF 自带的 SPI master 例程改。路径在examples/peripherals/spi_master。复制一份出来,改个名字,然后idf.py menuconfig进去配置。

menuconfig 里要改几个地方:

  • Component config -> SPI Master:确认 SPI 驱动使能
  • Component config -> Ethernet:使能 Ethernet 支持,选 W5500 作为 PHY
  • Example Configuration:设置 SPI 引脚和时钟频率

ESP-IDF 从 v4.1 开始就内置了 W5500 的驱动,在components/ethernet里,叫esp_eth_phy_w5500。这个驱动封装好了 W5500 的初始化和收发逻辑,你只需要配置 SPI 总线和引脚就行,非常方便。

4.3 关键配置参数怎么定

SPI 时钟频率是个关键参数。W5500 手册标称最高 80MHz,但实际用的时候要考虑 PCB 走线和线缆长度。我一般从 20MHz 开始调,稳定的话再往上加。如果线比较长或者没有屏蔽,建议降到 10MHz。

spi_device_interface_config_t devcfg = { .clock_speed_hz = 20 * 1000 * 1000, // 20MHz .mode = 0, // SPI Mode 0 .spics_io_num = 5, // CS 引脚 .queue_size = 20, // 事务队列深度 .command_bits = 0, .address_bits = 0, .dummy_bits = 0, };

queue_size这个参数容易被忽略,它决定 SPI 事务队列能排多少个请求。W5500 收发数据时会有多个 SPI 事务,队列太小会导致阻塞。我一般设 20,够用了。

5. 逐行拆解 SPI 驱动代码

5.1 总线初始化,第一步别搞错

spi_bus_config_t buscfg = { .miso_io_num = 19, .mosi_io_num = 23, .sclk_io_num = 18, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 4096, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));

这段代码做了几件事。miso_io_nummosi_io_numsclk_io_num分别指定三根信号线。quadwp_io_numquadhd_io_num是 Quad SPI 用的,我们普通 SPI 用不到,设 -1。max_transfer_sz是单次传输最大字节数,W5500 一帧以太网数据最大 1500 多字节,设 4096 足够。

spi_bus_initialize的第三个参数是 DMA 通道,SPI_DMA_CH_AUTO让驱动自动选。用 DMA 的好处是数据搬运不占 CPU,尤其是大块数据传输时差别明显。W5500 收发以太网帧动辄上千字节,DMA 几乎是必须的。

注意:如果spi_bus_initialize返回ESP_ERR_INVALID_STATE,说明这个 host 已经被初始化过了,检查是不是重复调用。

5.2 添加设备,片选和模式在这里定

spi_device_handle_t spi_handle; ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &devcfg, &spi_handle));

spi_bus_add_device把 W5500 挂到总线上,返回一个句柄。后面所有跟 W5500 的通信都通过这个句柄。devcfg里的spics_io_num指定片选引脚,驱动会自动管理片选的拉低拉高,你不用手动操作。

这里有个细节,如果你总线上挂了多个 SPI 设备,每个设备都要单独spi_bus_add_device,各自有独立的devcfg。片选引脚不能重复,否则会冲突。

5.3 W5500 的 SPI 帧格式,跟普通 SPI 不一样

W5500 的 SPI 通信不是简单的读写,它有一套自己的帧格式。每次操作分三个阶段:

  1. 地址阶段:2 字节,包含地址和读写控制位
  2. 控制阶段:1 字节,包含 BSB(Block Select Bits)和 RWB(Read/Write Bit)
  3. 数据阶段:N 字节,实际读写的数据

地址阶段的第一个字节的最高位是读写标志,1 表示读,0 表示写。后面 15 位是地址。控制阶段的 BSB 选择 W5500 内部的哪个块,比如通用寄存器、socket 寄存器、发送缓冲区、接收缓冲区。

这套格式意味着你不能直接用spi_device_transmit发一个简单的事务,得把地址和控制字节拼在数据前面。ESP-IDF 的 W5500 驱动已经帮你封装好了这些,你调esp_eth_phy_w5500的接口就行。但如果你想自己写底层驱动,就得按这个格式来。

// 读 W5500 寄存器的底层实现示意 uint8_t addr_phase[2]; addr_phase[0] = (reg_addr >> 8) & 0xFF; addr_phase[1] = reg_addr & 0xFF; addr_phase[0] |= 0x80; // 读操作,最高位置1 uint8_t ctrl_phase = (bsb << 3); // BSB 在 bit3-7 // 拼接后通过 SPI 发送

5.4 中断方式接收,比轮询省 CPU

W5500 的 INT 引脚在收到数据或者 socket 状态变化时会拉低。用中断方式接收,ESP32 不用一直轮询,CPU 占用低很多。

gpio_config_t io_conf = { .intr_type = GPIO_INTR_NEGEDGE, .mode = GPIO_MODE_INPUT, .pin_bit_mask = (1ULL << 4), .pull_up_en = 1, }; gpio_config(&io_conf); gpio_install_isr_service(0); gpio_isr_handler_add(4, w5500_int_handler, NULL);

中断处理函数里不要做太多事,一般就是发个信号量或者任务通知,让专门的任务去处理数据。我见过有人在中断里直接读 SPI,结果各种超时和丢数据,这是大忌。

6. 跑通以太网,从 DHCP 到 ping 通

6.1 初始化以太网,驱动帮你搞定大部分

ESP-IDF 的以太网初始化流程大致是这样:

esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); esp_eth_handle_t eth_handle = NULL; esp_eth_driver_install(&eth_config, &eth_handle);

esp_eth_mac_new_w5500创建 MAC 层实例,esp_eth_phy_new_w5500创建 PHY 层实例。W5500 把 MAC 和 PHY 集成在一起了,所以这两个都是针对同一颗芯片。ETH_DEFAULT_CONFIG是个宏,帮你填好默认配置。

6.2 接上 DHCP,自动拿 IP

esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); esp_netif_t *eth_netif = esp_netif_new(&netif_cfg); esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle); esp_netif_attach(eth_netif, glue); esp_eth_start(eth_handle);

这段代码把以太网驱动和 ESP-IDF 的网络接口层对接起来。ESP_NETIF_DEFAULT_ETH默认启用 DHCP 客户端,启动后会自动向路由器请求 IP。如果你要静态 IP,就在这之后调esp_netif_set_ip_info

启动后等几秒,用esp_netif_get_ip_info查一下拿到的 IP。如果一直是 0.0.0.0,检查网线插好没有、路由器 DHCP 开了没有、W5500 的 link 灯亮不亮。

6.3 ping 通测试,验证链路

拿到 IP 后,在电脑上 ping 一下 ESP32 的 IP。能 ping 通说明链路层和网络层都 OK。如果 ping 不通,按这个顺序排查:

  1. 看 W5500 的 LINK 灯和 ACT 灯,LINK 常亮说明物理连接正常
  2. 看 ESP32 串口日志,有没有报 PHY 初始化失败
  3. 检查 IP 是不是跟电脑在同一网段
  4. arp -a看电脑有没有学到 ESP32 的 MAC 地址

我遇到过一种情况,ping 的时候断断续续,最后发现是网线质量太差,换根六类线就好了。还有一次是 W5500 的复位引脚没接,上电时序不对,芯片没正常工作。

7. 常见问题与避坑指南

7.1 SPI 通信不生效,读出来全是 0xFF

这是最高频的问题。排查顺序:

  • 先确认 SPI 模式,W5500 用 Mode 0 或 Mode 3,试试换一个
  • 检查 CS 引脚有没有接对,片选没拉低的话从机根本不响应
  • 用逻辑分析仪抓一下 SCLK、MOSI、CS 的波形,看时序对不对
  • 确认 W5500 的复位引脚状态,有些模块 RST 悬空会导致芯片一直复位

我自己的经验是,八成问题出在模式或者片选上。逻辑分析仪是神器,几百块的投资能省你几天时间。

7.2 跑几天后连不上,ping 断断续续

这个问题的根源通常是电源或者散热。W5500 长时间工作会发热,如果模块没有良好的散热,芯片温度过高会导致内部逻辑出错。解决办法:

  • 给 W5500 加个小散热片
  • 检查 3.3V 电源的稳定性,用示波器看纹波
  • 在软件里加看门狗,定期检查 link 状态,掉线了自动复位 W5500

我有个项目跑了半年多,中间偶尔掉线,后来加了每 30 秒检查一次 link 状态,掉了就硬件复位,再也没出过问题。

7.3 以太网和 WiFi 同时用,路由冲突

ESP32 可以同时跑 WiFi 和以太网,但两个网络接口都有 IP 时,路由表可能会冲突。默认路由只能有一个,你得决定数据从哪个接口出去。用esp_netif_set_default_netif指定默认接口,或者用setsockopt绑定特定接口。

我一般建议,如果上了有线以太网,就把 WiFi 关掉或者只做备用。两个都开的话,网络调试会复杂很多。

7.4 常见问题速查表

现象可能原因解决办法
读寄存器全 0xFFSPI 模式错、CS 没接换 Mode 0/3,检查 CS
ping 不通IP 不对、网线问题查 DHCP、换网线
跑几天掉线电源纹波、发热加电容、加散热片
数据丢包SPI 时钟太高降到 10MHz 试试
初始化失败复位时序不对检查 RST 引脚、加上电延时

8. 几个实操心得,都是踩坑换来的

第一个心得,SPI 时钟不要一上来就拉满。我见过有人直接设 80MHz,结果各种诡异问题。从 10MHz 开始,稳定了再往上加,找到稳定上限后留 20% 余量。工业环境温度变化大,留余量很重要。

第二个心得,W5500 的发送缓冲区要等空了再写下一帧。W5500 每个 socket 有 2KB 发送缓冲区,如果你写太快,缓冲区满了数据就丢了。正确做法是查Sn_TX_FSR寄存器,确认有足够空间再写。这个细节很多例程里没讲,但不处理的话高负载下必丢包。

第三个心得,网线别用太长的。我试过用 20 米的网线,结果 link 都建立不起来。后来查资料才知道,W5500 的驱动能力有限,超过 15 米就可能有问题。工业现场如果距离远,中间加个交换机中继一下。

第四个心得,调试的时候串口日志要打全。ESP-IDF 的日志级别调到 Debug,能看到 PHY 状态变化、link up/down 事件、DHCP 过程。这些信息对定位问题非常关键。我一般会在menuconfig里把 Ethernet 和 SPI 的日志级别都调到 Verbose。

第五个心得,硬件复位比软件复位靠谱。W5500 有时候会进入奇怪的状态,软件复位命令发下去没反应。这时候拉低 RST 引脚 10ms 再拉高,基本都能救回来。所以 RST 引脚一定要接到 ESP32 的 GPIO 上,别省这一根线。

这套方案我前后用在三个项目里,最长的跑了两年多,除了偶尔因为路由器重启需要重新 DHCP,其他时间都很稳。ESP32 加 W5500 的组合,在需要有线网络的嵌入式场景里,确实是个省心又省钱的选择。如果你也在做类似的东西,希望这篇能帮你少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询