ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP32 SPI驱动W5500以太网控制器:从时序原理到稳定通信实战

ESP32 SPI驱动W5500以太网控制器:从时序原理到稳定通信实战 1. 为什么我劝你先搞懂 SPI 再碰 W5500很多人拿到 W5500 模块的第一反应是找现成库Ethernet.init()一调能 ping 通就收工。但真到了现场设备跑几天掉线、数据发不出去、初始化偶尔失败回头翻库代码才发现问题全卡在 SPI 时序上。W5500 本质就是一颗挂在 SPI 总线上的以太网控制器ESP32 通过 SPI 读写它的寄存器来收发网络数据。SPI 没吃透W5500 出问题你连从哪查都不知道。这篇内容面向的是已经会用 Arduino 或 ESP-IDF 点灯、但对 SPI 只停留在“知道有四根线”的开发者。我会把 ESP32 的 SPI 主机驱动逐行拆开讲再把它和 W5500 的寄存器操作串起来最后给出一套可以直接抄的接线和代码。读完你应该能做到三件事第一看懂 ESP32 SPI 初始化的每一个参数在干什么第二理解 W5500 的读写时序为什么必须那样写第三遇到通信失败时有一套自己的排查顺序而不是到处搜“W5500 连不上”。先说一个反直觉的结论大部分 W5500 通信问题不是 W5500 的锅是 SPI 模式、片选时序和电源这三件事没弄对。我见过太多人把模块换了一个又一个最后发现是 SPI 模式设成了 Mode 0 而 W5500 要 Mode 0 配合特定时序或者片选在传输间隙被拉高导致寄存器写入丢失。下面从 SPI 本身讲起。2. ESP32 SPI 主机驱动的参数逐个拆解2.1 SPI 到底是什么四根线背后的约定SPI 全称 Serial Peripheral Interface是一种同步串行通信协议。说人话就是一根时钟线SCLK负责打节拍一根数据线MOSI主机发给从机一根数据线MISO从机回给主机再加一根片选线CS决定跟哪个从机说话。它没有像 I2C 那样的地址机制靠片选线物理选中设备所以速度可以做得很快ESP32 的 SPI 主机最高能跑到 80MHz。关键点在于“同步”两个字。时钟的上升沿或下降沿决定数据什么时候被采样这就是所谓的 SPI 模式。模式由两个参数组合CPOL时钟极性和 CPHA时钟相位。CPOL0 表示空闲时时钟为低CPHA0 表示第一个边沿采样。W5500 支持 Mode 0 和 Mode 3实际用 Mode 0 最稳。很多人抄代码时看到SPI_MODE0就直接用但不知道如果设成 Mode 1 或 Mode 2数据会在错误的边沿被采样读回来的寄存器值全是乱的。2.2 ESP-IDF 里 spi_bus_config_t 的每个字段在 ESP-IDF 中初始化 SPI 主机分两步先配置总线再添加设备。总线配置结构体spi_bus_config_t里有几个字段必须搞清楚mosi_io_num/miso_io_num/sclk_io_num这三根线的 GPIO 编号。ESP32 的 SPI 可以映射到任意 GPIO因为内部有 GPIO 矩阵。但要注意某些 GPIO 在启动时有特殊功能比如 GPIO6-11 接的是内部 Flash不能随便用。quadwp_io_num/quadhd_io_num这两个是 Quad SPI 用的普通 SPI 设备设为 -1 即可。如果你不小心设了值总线会尝试以 Quad 模式初始化普通设备直接通信失败。max_transfer_sz单次传输最大字节数。默认是 4096如果你要一次发大包数据比如 W5500 的 socket 发送缓冲区这个值要调大否则传输会被截断。我一般设成 8192 留余量。flags可以设SPICOMMON_BUSFLAG_MASTER明确主机模式或者设SPICOMMON_BUSFLAG_IOMUX_PINS强制使用 IO_MUX 引脚以获得更高速度。普通应用不设也行驱动会自动判断。这里有个容易忽略的点max_transfer_sz设得太小W5500 发大包时会返回 ESP_ERR_INVALID_ARG但错误信息不会告诉你是因为尺寸超限。我第一次遇到时查了半天最后在驱动源码里看到这个限制才反应过来。2.3 设备配置里的时钟速度与模式选择添加设备用spi_device_interface_config_t核心字段是clock_speed_hz、mode、spics_io_num和queue_size。clock_speed_hz决定 SPI 时钟频率。W5500 数据手册标称最高支持 80MHz但实际用 ESP32 驱动时我建议先跑 10MHz 验证功能稳定后再往上加。原因有两个一是杜邦线飞线在高速下信号完整性差二是 W5500 模块上的晶振和布线质量参差不齐。我实测过某宝买的模块20MHz 时误码率明显上升降到 14MHz 就稳了。mode就是前面说的 SPI 模式W5500 用 Mode 0。spics_io_num是片选引脚这里有个大坑如果你用硬件片选指定了 spics_io_num驱动会在每次传输前后自动拉低拉高如果你设成 -1 用软件片选就得自己在传输前拉低、传输后拉高。两种方式都能用但混用会出问题。我见过有人既指定了 spics_io_num 又在代码里手动控制同一个引脚结果片选信号出现毛刺W5500 偶尔把一次传输拆成两次命令。queue_size是传输队列深度。设为 0 表示不使用队列每次传输阻塞等待设为大于 0 的值则使用中断驱动。对于 W5500 这种需要频繁读寄存器的场景我建议设成 1 到 3既能异步处理又不至于占太多内存。2.4 一次完整传输的事务结构ESP-IDF 用spi_transaction_t描述一次传输。关键字段length数据长度单位是比特不是字节。发 4 个字节要写 32。这个单位坑过无数人。tx_buffer/rx_buffer发送和接收缓冲区指针。如果只发不收rx_buffer 设 NULL如果只收不发tx_buffer 设 NULL 但 length 仍要设对。flags可以设SPI_TRANS_USE_TXDATA或SPI_TRANS_USE_RXDATA表示数据直接放在结构体的tx_data/rx_data数组里适合小数据量传输省去动态分配。W5500 的寄存器读写是典型的“发命令收数据”模式先发 3 字节地址段控制段再发或收 1 字节数据。用spi_device_transmit一次事务完成或者用spi_device_polling_transmit阻塞式完成。后者更简单适合初学者。3. W5500 的寄存器读写时序与 SPI 的配合3.1 W5500 的帧格式地址段和控制段W5500 的 SPI 帧不是简单的“发地址读数据”它有固定格式。每次操作先发 3 个字节前 2 字节是地址16 位第 3 字节是控制段。控制段里包含第 7 位读写标志1 表示读0 表示写。第 6-3 位块选择决定操作哪个寄存器区通用寄存器、socket 寄存器、发送缓冲区、接收缓冲区。第 2-0 位SPI 模式W5500 要求固定为 0可变长度模式。举个例子读通用寄存器MR地址 0x0000控制段是0x00 | 0x00 | 0x00 0x00但读操作要置第 7 位所以是0x80。写操作则是0x00。读 socket 0 的Sn_SR地址 0x0003块选择 1控制段是0x80 | (1 3) | 0 0x88。这些位运算看起来繁琐但理解了之后你会发现它设计得很紧凑3 个字节就能表达“读还是写、哪个区、哪个地址”。很多库把这些封装成宏但封装太深反而让人看不懂底层在干什么。我建议至少自己手写一遍读写函数把位运算搞清楚。3.2 片选时序为什么传输间隙必须拉高W5500 靠片选信号的下降沿开始一次 SPI 事务上升沿结束。在一次完整的 4 字节传输3 字节命令1 字节数据期间片选必须保持低电平。如果中间拉高W5500 会认为事务结束后续字节被当成新事务的命令寄存器操作就乱了。用 ESP-IDF 的硬件片选时驱动会自动处理这个时序你不用担心。但如果你用软件片选必须确保gpio_set_level(cs, 0)和gpio_set_level(cs, 1)之间没有其他 SPI 传输插入。多任务环境下尤其要注意两个任务同时操作 SPI 会导致片选交错。解决办法是用互斥锁保护整个“拉低-传输-拉高”过程或者干脆用硬件片选让驱动管理。我踩过的一个坑在 FreeRTOS 任务里用软件片选任务切换恰好发生在拉低之后、传输之前另一个任务也去操作 SPI结果两个事务的片选信号叠在一起W5500 直接不响应。后来改成硬件片选加队列传输问题消失。3.3 读写函数的逐行实现下面是一个读 W5500 寄存器的函数用 ESP-IDF 的 SPI 主机驱动#include driver/spi_master.h #include esp_log.h static spi_device_handle_t w5500_spi; // 读 W5500 寄存器 // block: 块选择 (0通用, 1socket0, ...) // addr: 16位地址 // data: 输出缓冲区 uint8_t w5500_read(uint8_t block, uint16_t addr, uint8_t *data, size_t len) { // 构造 3 字节命令 uint8_t cmd[3]; cmd[0] (addr 8) 0xFF; // 地址高字节 cmd[1] addr 0xFF; // 地址低字节 cmd[2] 0x80 | ((block 0x07) 3); // 读标志 块选择 // 分配传输描述符 spi_transaction_t trans { .length (3 len) * 8, // 总比特数 .tx_buffer cmd, // 发送命令 .rx_buffer NULL, // 先不发接收 }; // 第一次传输发命令同时接收 dummy 数据 // 这里用 tx_buffer 和 rx_buffer 分开处理更清晰 uint8_t dummy[3] {0}; trans.rx_buffer dummy; esp_err_t ret spi_device_polling_transmit(w5500_spi, trans); if (ret ! ESP_OK) return 0; // 第二次传输读数据 spi_transaction_t trans_rx { .length len * 8, .tx_buffer NULL, .rx_buffer data, }; ret spi_device_polling_transmit(w5500_spi, trans_rx); return (ret ESP_OK) ? 1 : 0; }这段代码有个细节第一次传输时length是(3len)*8但rx_buffer只给了 3 字节的 dummy。实际上 ESP-IDF 允许rx_buffer小于length对应的字节数多出来的部分会被丢弃。不过更规范的做法是分两次传输先发 3 字节命令再读 len 字节数据。W5500 支持连续读片选在两次传输之间保持低电平即可。但用spi_device_polling_transmit时每次调用都会拉高片选所以连续读需要特殊处理。正确做法是用一个事务完成“发命令读数据”把 tx_buffer 设成 3len 字节的缓冲区前 3 字节是命令后面填 0rx_buffer 同样大小接收到的数据从第 4 字节开始才是有效值。这样片选在整个事务期间保持低电平符合 W5500 时序。uint8_t w5500_read_fixed(uint8_t block, uint16_t addr, uint8_t *data, size_t len) { uint8_t tx_buf[3 len]; uint8_t rx_buf[3 len]; tx_buf[0] (addr 8) 0xFF; tx_buf[1] addr 0xFF; tx_buf[2] 0x80 | ((block 0x07) 3); memset(tx_buf 3, 0, len); // 后续字节填 0时钟照打 spi_transaction_t trans { .length (3 len) * 8, .tx_buffer tx_buf, .rx_buffer rx_buf, }; esp_err_t ret spi_device_polling_transmit(w5500_spi, trans); if (ret ! ESP_OK) return 0; memcpy(data, rx_buf 3, len); // 跳过命令段的回显 return 1; }写函数类似只是控制段第 7 位为 0且数据段直接放要写的值uint8_t w5500_write(uint8_t block, uint16_t addr, const uint8_t *data, size_t len) { uint8_t tx_buf[3 len]; tx_buf[0] (addr 8) 0xFF; tx_buf[1] addr 0xFF; tx_buf[2] 0x00 | ((block 0x07) 3); // 写标志 memcpy(tx_buf 3, data, len); spi_transaction_t trans { .length (3 len) * 8, .tx_buffer tx_buf, .rx_buffer NULL, }; return (spi_device_polling_transmit(w5500_spi, trans) ESP_OK) ? 1 : 0; }这两个函数就是 W5500 驱动的地基。所有上层操作——初始化、开 socket、发数据——都是对这两个函数的调用组合。4. 接线、供电与初始化顺序的实战细节4.1 接线表与 GPIO 选择W5500 模块和 ESP32 的接线不复杂但选错 GPIO 会带来莫名其妙的故障。下面是我常用的接线方案W5500 引脚ESP32 GPIO说明VCC3.3V必须 3.3V5V 会烧GNDGND共地SCLKGPIO18SPI 时钟MOSIGPIO23主机输出MISOGPIO19主机输入CSGPIO5片选INTGPIO4中断可选RSTGPIO2复位可选选 GPIO18/23/19 是因为它们是 ESP32 默认的 SPI 引脚VSPI用 IO_MUX 直连信号质量最好。如果你改用其他 GPIO驱动会走 GPIO 矩阵速度上限会降低。GPIO5 做片选是因为它启动时默认高电平不会误触发 W5500。GPIO2 做复位要注意它在启动时可能输出低电平如果 W5500 复位引脚对此敏感可以换 GPIO15 或直接接 RC 复位电路。4.2 供电最容易被忽视的故障源W5500 工作时电流峰值能到 150mA 左右ESP32 本身 Wi-Fi 工作时也有 200mA 以上的波动。如果你用电脑 USB 口供电5V 转 3.3V 的 LDO 如果容量不够电压会瞬间跌落W5500 直接复位或通信出错。我的做法是W5500 模块单独用一个 3.3V LDO 供电或者至少在电源引脚并一个 100uF 电解电容加 0.1uF 陶瓷电容。很多模块自带电容但容量偏小。我实测过一个模块不加电容时 ping 大包丢包率 5%加了 100uF 后丢包率降到 0。还有一个细节W5500 的 3.3V 和 ESP32 的 3.3V 最好来自同一个电源否则上电顺序不一致可能导致 SPI 引脚倒灌电流。如果必须分开供电确保先给 W5500 上电再给 ESP32 上电或者加电平转换。4.3 初始化顺序复位、配置、开 socketW5500 上电后需要一段稳定时间然后通过 SPI 写复位寄存器。完整初始化顺序拉低 RST 引脚至少 500us再拉高等待 1ms 让内部 PLL 锁定。读VERSIONR寄存器地址 0x0039应该是 0x04。如果不是说明 SPI 通信有问题后面不用继续了。写MR寄存器地址 0x0000的 RST 位为 1软复位等待 1ms。配置网络参数SHAR源 MAC、SIPR源 IP、SUBR子网掩码、GAR网关。配置 socket 缓冲区大小Sn_TXBUF_SIZE/Sn_RXBUF_SIZE默认每个 socket 2KB 发送、2KB 接收8 个 socket 共 16KB。如果只用 1 个 socket可以把其他 socket 的缓冲区设为 0给这个 socket 分 8KB。开 socket写Sn_MR设协议TCP 或 UDP写Sn_PORT设端口写Sn_CR发 OPEN 命令。第 2 步的版本寄存器读取是我强烈建议加的。如果 VERSIONR 读不到 0x04后面所有操作都是白费力气。这一步能快速区分“SPI 没通”和“W5500 配置错”。我见过有人跳过这步结果在 socket 配置上折腾半天最后发现是 MISO 线接错了。5. 通信失败时的排查链路5.1 从 VERSIONR 读不到 0x04 开始如果 VERSIONR 读回来是 0x00 或 0xFF说明 SPI 物理层或时序有问题。排查顺序先查接线MISO 和 MOSI 有没有接反这是最常见的错误。W5500 模块上通常标了引脚名但有些模块的丝印方向容易看错。再查片选用逻辑分析仪或示波器看 CS 引脚传输时应该有一个明显的低电平脉冲。如果没有检查 GPIO 配置和驱动初始化。查 SPI 模式确认设的是 Mode 0。有些库默认 Mode 0但如果你手动改了spi_device_interface_config_t的 mode 字段可能变成其他模式。查时钟速度降到 1MHz 试试。如果 1MHz 能读到 0x04 而 10MHz 不行说明信号完整性有问题检查线长和电源。我遇到过一次 VERSIONR 读回 0x04 但偶尔变成 0x00 的情况最后发现是 SPI 时钟线离电源线太近高速时串扰导致时钟抖动。把两根线分开走线后问题消失。5.2 能读到版本但 socket 连不上VERSIONR 正常说明 SPI 通了问题出在网络配置或 socket 操作。检查网络参数IP、网关、子网掩码是否和路由器在同一网段MAC 地址有没有和别的设备冲突socket 状态读Sn_SR寄存器看状态机走到哪一步。常见状态0x00 关闭、0x13 初始化、0x17 已连接、0x1C 等待关闭。如果一直停在 0x13说明 OPEN 命令没生效检查Sn_CR写入后有没有等待命令完成读Sn_CR直到为 0。端口占用W5500 的 socket 端口如果和服务器端口不匹配TCP 连接会被拒绝。用Sn_PORT设对端口Sn_DIPR和Sn_DPORT设对目标地址和端口。5.3 跑几天后掉线电源和散热W5500 长时间运行后掉线ping 断断续续大概率是电源问题。W5500 内部 LDO 在高温下输出能力下降如果模块散热不好芯片温度升高后 3.3V 电压跌落SPI 通信出错。解决办法给 W5500 芯片贴一个小散热片或者在模块下方留通风空间。电源方面确保 3.3V 在满载时不低于 3.2V。我实测过一个案例模块放在密闭盒子里夏天运行 3 天后开始丢包打开盒子散热后恢复正常。另一个可能原因是 SPI 时钟太快导致长期误码累积。把时钟从 30MHz 降到 20MHz稳定性明显提升。W5500 标称 80MHz但那是理想条件下的芯片能力实际模块受布线限制20-30MHz 是比较稳妥的范围。6. 可以直接抄的完整初始化代码6.1 SPI 总线与设备初始化#include driver/spi_master.h #include driver/gpio.h #include esp_log.h #include freertos/FreeRTOS.h #include freertos/task.h #define PIN_SCLK 18 #define PIN_MOSI 23 #define PIN_MISO 19 #define PIN_CS 5 #define PIN_RST 2 static spi_device_handle_t w5500_spi; void w5500_spi_init(void) { // 复位引脚配置 gpio_config_t rst_cfg { .pin_bit_mask (1ULL PIN_RST), .mode GPIO_MODE_OUTPUT, }; gpio_config(rst_cfg); gpio_set_level(PIN_RST, 0); vTaskDelay(pdMS_TO_TICKS(1)); gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(10)); // SPI 总线配置 spi_bus_config_t bus_cfg { .mosi_io_num PIN_MOSI, .miso_io_num PIN_MISO, .sclk_io_num PIN_SCLK, .quadwp_io_num -1, .quadhd_io_num -1, .max_transfer_sz 8192, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, bus_cfg, SPI_DMA_CH_AUTO)); // SPI 设备配置 spi_device_interface_config_t dev_cfg { .clock_speed_hz 20 * 1000 * 1000, // 20MHz .mode 0, // SPI Mode 0 .spics_io_num PIN_CS, // 硬件片选 .queue_size 3, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, dev_cfg, w5500_spi)); }这里用 SPI2_HOST也就是 HSPI因为 SPI1 通常留给内部 Flash。DMA 通道选自动分配max_transfer_sz设 8192 足够 W5500 的 2KB 缓冲区操作。6.2 验证通信与网络配置#define W5500_BLOCK_COMMON 0 #define W5500_BLOCK_SOCK0 1 uint8_t w5500_init_check(void) { uint8_t ver 0; w5500_read_fixed(W5500_BLOCK_COMMON, 0x0039, ver, 1); if (ver ! 0x04) { ESP_LOGE(W5500, VERSIONR 0x%02X, expected 0x04, ver); return 0; } ESP_LOGI(W5500, SPI communication OK, version 0x%02X, ver); return 1; } void w5500_config_network(void) { uint8_t mac[6] {0x02, 0x00, 0x00, 0x12, 0x34, 0x56}; uint8_t ip[4] {192, 168, 1, 100}; uint8_t sub[4] {255, 255, 255, 0}; uint8_t gw[4] {192, 168, 1, 1}; w5500_write(W5500_BLOCK_COMMON, 0x0009, mac, 6); // SHAR w5500_write(W5500_BLOCK_COMMON, 0x000F, ip, 4); // SIPR w5500_write(W5500_BLOCK_COMMON, 0x0005, sub, 4); // SUBR w5500_write(W5500_BLOCK_COMMON, 0x0001, gw, 4); // GAR }MAC 地址用02开头是本地管理地址避免和真实设备冲突。IP 根据你的路由器网段调整。6.3 开一个 TCP 客户端 socketvoid w5500_open_tcp_client(uint8_t sock, uint8_t *dest_ip, uint16_t dest_port) { uint8_t block W5500_BLOCK_SOCK0 sock; uint8_t val; // 设置 socket 缓冲区大小8KB 发送8KB 接收 val 8; w5500_write(block, 0x001E, val, 1); // Sn_TXBUF_SIZE w5500_write(block, 0x001F, val, 1); // Sn_RXBUF_SIZE // 设置协议为 TCP val 0x01; // Sn_MR TCP w5500_write(block, 0x0000, val, 1); // 设置目标端口和本地端口 uint8_t port[2] {dest_port 8, dest_port 0xFF}; w5500_write(block, 0x0004, port, 2); // Sn_DPORT uint8_t local_port[2] {0x1F, 0x90}; // 8080 w5500_write(block, 0x0001, local_port, 2); // Sn_PORT // 发送 OPEN 命令 val 0x01; // OPEN w5500_write(block, 0x0002, val, 1); // Sn_CR while (1) { w5500_read_fixed(block, 0x0002, val, 1); if (val 0) break; // 命令完成 vTaskDelay(pdMS_TO_TICKS(1)); } // 设置目标 IP w5500_write(block, 0x000C, dest_ip, 4); // Sn_DIPR // 发送 CONNECT 命令 val 0x04; // CONNECT w5500_write(block, 0x0002, val, 1); while (1) { w5500_read_fixed(block, 0x0002, val, 1); if (val 0) break; vTaskDelay(pdMS_TO_TICKS(1)); } // 等待连接建立 uint8_t sr; for (int i 0; i 100; i) { w5500_read_fixed(block, 0x0003, sr, 1); // Sn_SR if (sr 0x17) { // ESTABLISHED ESP_LOGI(W5500, Socket %d connected, sock); return; } vTaskDelay(pdMS_TO_TICKS(10)); } ESP_LOGE(W5500, Socket %d connect timeout, SR0x%02X, sock, sr); }这段代码里每个命令后都轮询Sn_CR直到为 0这是 W5500 的规矩命令寄存器写入后芯片需要时间执行必须等它自己清零才能发下一条命令。跳过这个等待是新手常犯的错误表现为“命令偶尔不生效”。7. 几个让我调试到凌晨的坑7.1 片选信号上的毛刺有一次用逻辑分析仪抓波形发现 CS 在两次传输之间有一个 100ns 的高电平毛刺。原因是spi_device_polling_transmit返回后驱动拉高 CS但下一个事务紧接着开始又拉低中间的高电平被 W5500 误判为事务结束。解决办法是在两次传输之间加一个esp_rom_delay_us(1)或者改用队列传输让驱动合并事务。更彻底的办法是把多个寄存器读写合并成一次 SPI 事务减少 CS 翻转次数。7.2 中断引脚悬空导致的随机复位W5500 的 INT 引脚是开漏输出如果不接上拉电阻浮空时可能被干扰触发。我一开始没接 INTW5500 偶尔自己复位。后来在 INT 和 3.3V 之间加了一个 10K 上拉电阻问题消失。如果你不用中断功能把 INT 引脚接 3.3V 或 GND 固定电平也行但不要悬空。7.3 多 socket 同时操作时的 SPI 冲突W5500 有 8 个 socket如果多个任务同时操作不同 socketSPI 总线会成为竞争资源。我试过两个任务分别读写 socket 0 和 socket 1结果寄存器值互相覆盖。解决办法是加一个全局互斥锁所有 W5500 读写函数先获取锁再操作。锁的粒度可以粗一点因为 SPI 传输本身很快不会成为性能瓶颈。static SemaphoreHandle_t w5500_mutex; // 在初始化时创建 w5500_mutex xSemaphoreCreateMutex(); // 读写函数开头加 xSemaphoreTake(w5500_mutex, portMAX_DELAY); // ... SPI 操作 ... xSemaphoreGive(w5500_mutex);这个锁我建议从第一天就加上不要等到出问题才补。多任务环境下SPI 共享资源的保护是必须的。7.4 时钟速度与线长的关系杜邦线超过 10cm 时20MHz 的 SPI 时钟开始出现明显振铃。用示波器看 SCLK 波形上升沿有过冲和振荡。解决办法缩短线长到 5cm 以内或者在 SCLK 线上串一个 33 欧姆电阻做阻抗匹配。我试过串 22 欧姆和 33 欧姆33 欧姆效果更好波形明显干净。如果必须用长线把时钟降到 10MHz 以下。8. 从能跑到跑得稳长期运行的建议W5500 跑通不难跑稳需要关注几个长期因素。第一是电源纹波用示波器交流耦合看 3.3V 引脚纹波应该小于 50mV。如果超过加电容或换 LDO。第二是温度W5500 芯片表面温度超过 60 度时通信误码率会上升加散热片或降低时钟频率。第三是看门狗ESP32 的任务看门狗如果超时复位W5500 需要重新初始化建议在复位后加一个完整的 W5500 初始化流程包括软复位和网络配置。我现在的做法是在 W5500 初始化后启动一个心跳任务每 30 秒读一次 VERSIONR如果连续 3 次读不到 0x04就执行一次软复位和重新配置。这个机制帮我省了很多现场排查的时间。代码不复杂但效果立竿见影。最后分享一个测量 SPI 实际速率的小技巧在w5500_read_fixed前后各翻转一个 GPIO用示波器测高电平时间再除以传输字节数就能算出实际吞吐。我实测 20MHz 时钟下读 1 字节寄存器4 字节事务耗时约 2.5us有效速率约 12.8Mbps。这个数据帮你判断 SPI 是不是瓶颈如果网络吞吐远低于这个值问题就不在 SPI 上去查网络配置或 socket 缓冲区。
返回列表