1. 为什么我劝你先别急着写W5500的驱动代码
很多人拿到ESP32和W5500模块的第一反应,是直接去GitHub上找一个现成的库,把线一插,烧进去,能ping通就完事了。我以前也这么干过,结果项目做到一半,遇到网络偶尔断流、大数据量传输丢包、DHCP拿不到地址这些问题,翻遍库的源码也找不到原因,最后只能推倒重来。
问题的根源在于:你跳过了SPI本身。W5500本质上就是一颗挂在SPI总线上的外设芯片,你对它的所有操作——读版本号、写寄存器、收发以太网帧——全部要通过SPI时序来完成。如果你不理解ESP32的SPI主机控制器是怎么工作的,不理解W5500的帧格式和寄存器寻址方式,那你就是在盲人摸象。
这篇文章的目标很明确:把ESP32的SPI例程逐行拆开讲清楚,然后在此基础上,把W5500的驱动从零搭起来,实现有线以太网通信。适合有C语言基础、用过ESP32但没深入碰过SPI的开发者,也适合之前用Arduino库但想搞清楚底层原理的人。读完你至少能自己写一个W5500的初始化流程,遇到问题知道从哪个寄存器开始查。
注意:本文基于ESP-IDF v5.x的SPI Master驱动来讲解,Arduino框架下的SPI库封装层次不同,但底层原理一致。
2. ESP32的SPI主机控制器到底长什么样
2.1 四个SPI外设,别选错了
ESP32芯片内部有四个SPI控制器:SPI0、SPI1、SPI2、SPI3。很多人不知道这件事,以为只有一个SPI。实际情况是:
- SPI0和SPI1:内部专用,SPI0用于Cache访问外部Flash,SPI1用于访问外部PSRAM(如果模组带了的话)。这两个你碰不到,也不应该碰。
- SPI2(也叫HSPI):通用SPI主机,可以自由映射到GPIO矩阵。
- SPI3(也叫VSPI):同样是通用SPI主机,默认引脚和SPI2不同。
在ESP-IDF中,你用spi_bus_config_t配置总线时,通过host字段指定用哪个。通常W5500接在SPI2上就够了,SPI3可以留给其他外设或者不用。
这里有个容易踩的坑:ESP32的SPI引脚是可以通过GPIO矩阵任意映射的,不像STM32那样固定死了。这意味着你在spi_bus_config_t里填的mosi_io_num、miso_io_num、sclk_io_num可以是几乎任意GPIO。但要注意,有些GPIO是仅输入的(比如34-39),不能用作MOSI或SCLK输出。我一般习惯用默认的VSPI引脚:GPIO23(MOSI)、GPIO19(MISO)、GPIO18(SCLK)、GPIO5(CS)。
2.2 时钟极性和相位:CPOL和CPHA
SPI有四种模式,由CPOL(时钟极性)和CPHA(时钟相位)组合决定。W5500支持SPI模式0和模式3,也就是CPOL=0/CPHA=0或者CPOL=1/CPHA=1。这两种模式的区别在于空闲时SCLK是高还是低,以及数据在时钟的哪个边沿采样。
在ESP-IDF中,这两个参数不在总线配置里,而在设备配置spi_device_interface_config_t中:
spi_device_interface_config_t devcfg = { .clock_speed_hz = 30 * 1000 * 1000, // 30MHz .mode = 0, // CPOL=0, CPHA=0 .spics_io_num = PIN_CS, .queue_size = 7, };W5500的数据手册明确写了支持模式0和模式3。我实测下来模式0最稳,模式3在高速时钟下偶尔会有采样错误。如果你不确定,先用模式0。
2.3 时钟频率:不是越高越好
W5500的SPI接口最高支持80MHz(在3.3V供电下),但实际能跑多快取决于你的PCB走线、线缆长度和上拉电阻。我见过太多人直接把时钟设成40MHz甚至80MHz,结果通信不稳定,然后花几天时间怀疑是芯片坏了。
我的建议是:先用10MHz跑通,确认功能正常后再逐步提高。每次提高后跑一遍大数据量收发测试(比如连续发送1000个ARP请求),观察有没有丢包或超时。一般杜邦线连接的情况下,20-30MHz是比较稳妥的上限。PCB走线短且阻抗控制好的话,可以到40MHz以上。
另外注意,ESP32的SPI时钟是从APB时钟(默认80MHz)分频得到的,所以实际时钟频率是80MHz除以某个分频系数。你设30MHz,实际可能是26.67MHz(80/3)或40MHz(80/2),不会精确等于30MHz。这个细节在计算W5500的超时参数时要注意。
2.4 事务与队列:ESP-IDF的SPI传输模型
ESP-IDF的SPI Master驱动采用**事务(transaction)**模型。你不需要手动拉CS、发时钟、移位数据,而是把要发送和接收的数据打包成一个spi_transaction_t,然后提交给驱动,驱动会自动完成整个SPI时序。
一个典型的事务长这样:
spi_transaction_t t = { .length = 8 * 4, // 数据长度,单位是bit .tx_buffer = tx_data, // 发送缓冲区 .rx_buffer = rx_data, // 接收缓冲区 }; spi_device_transmit(spi_handle, &t);这里有几个关键点:
length的单位是比特,不是字节。发4个字节要写32。- 如果只发不收,
rx_buffer可以设为NULL。 - 如果只收不发,
tx_buffer设为NULL,驱动会发送0xFF(或者你可以配置成其他值)。 queue_size决定了可以排队多少个事务。如果你在中断里提交事务,队列大小要留够,否则会阻塞。
W5500的读写操作都是"命令+地址+数据"的格式,所以每次操作至少需要两个事务:一个发命令和地址,一个收发数据。或者你可以把命令、地址、数据拼在一个事务里,但要注意W5500的时序要求——地址阶段和数据阶段之间CS不能拉高。
3. W5500的SPI帧格式:读懂这颗芯片的"语言"
3.1 偏移地址与B SB位
W5500的SPI帧不是简单的"发地址读数据"。它的每个操作都包含一个偏移地址(Offset Address),这个地址是相对于某个块选择位(Block Select Bits,简称BSB)的。
W5500内部把寄存器空间分成了几个区块:
| BSB值 | 区块名称 | 用途 |
|---|---|---|
| 00000 | Common Register | 通用寄存器(模式、中断等) |
| 00001 | Socket 0 Register | Socket 0的配置寄存器 |
| 00010 | Socket 0 TX Buffer | Socket 0发送缓冲区 |
| 00011 | Socket 0 RX Buffer | Socket 0接收缓冲区 |
| 00101 | Socket 1 Register | Socket 1的配置寄存器 |
| ... | ... | ... |
| 11111 | Socket 7 RX Buffer | Socket 7接收缓冲区 |
所以你要读Socket 0的某个寄存器,实际发送的地址是(BSB << 8) | offset。比如读Socket 0的状态寄存器(偏移0x0003),BSB=0x01,实际地址就是0x0103。
3.2 读写操作码
W5500的SPI帧第一个字节的高3位是操作码:
0b000:读操作0b001:写操作0b010:读缓冲区(带自动地址递增)0b011:写缓冲区(带自动地址递增)
后面5位是BSB的高5位?不对,这里要仔细看数据手册。实际上第一个字节的格式是:
Bit 7-5: 操作码 (OP) Bit 4-0: BSB的高5位?等等,我重新查一下W5500数据手册。正确的格式是:
第一个字节:
- Bit 7-5:操作码(OP)
- Bit 4-0:BSB的bit 4到bit 0?不对。
让我重新理清楚。W5500的SPI帧格式是:
第一阶段(地址阶段):发送2个字节
- 第1字节:操作码(3位)+ BSB(5位)
- 第2字节:偏移地址(8位)
第二阶段(数据阶段):发送或接收N个字节的数据
所以BSB是5位,正好放在第1字节的低5位。偏移地址是8位,放在第2字节。总共16位地址空间,每个区块256字节。
举个例子:读Socket 0的状态寄存器(偏移0x0003):
- 操作码:0b000(读)
- BSB:0b00001(Socket 0 Register)
- 第1字节:0b000_00001 = 0x01
- 第2字节:0x03
- 然后接收1个字节的数据
写操作类似,只是操作码变成0b001,数据阶段变成发送。
3.3 缓冲区读写与自动递增
W5500的TX/RX缓冲区读写支持自动地址递增。当你使用操作码0b010(读缓冲区)或0b011(写缓冲区)时,每读写一个字节,内部地址指针自动加1。这样你就不需要每次发地址了,适合连续读写大量数据。
但要注意:自动递增只在同一个BSB区块内有效。如果你要跨区块读写,必须重新发地址阶段。
4. 从零搭建W5500驱动:逐行代码拆解
4.1 硬件连接与GPIO分配
先确定接线。我用的ESP32 DevKitC和W5500模块(常见的那个带RJ45的蓝色板子):
| ESP32引脚 | W5500引脚 | 说明 |
|---|---|---|
| GPIO23 | MOSI | 主机输出从机输入 |
| GPIO19 | MISO | 主机输入从机输出 |
| GPIO18 | SCLK | 时钟 |
| GPIO5 | CS | 片选 |
| GPIO4 | INT | 中断(可选) |
| GPIO2 | RST | 复位(可选) |
| 3.3V | VCC | 供电 |
| GND | GND | 共地 |
提示:W5500模块上一般有3.3V稳压芯片,但如果你用的是裸芯片,注意供电必须是3.3V,5V会烧。
4.2 SPI总线初始化
#include "driver/spi_master.h" #define PIN_MOSI 23 #define PIN_MISO 19 #define PIN_SCLK 18 #define PIN_CS 5 spi_device_handle_t spi; void spi_init(void) { spi_bus_config_t buscfg = { .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 = 2048, // 最大传输字节数 }; spi_device_interface_config_t devcfg = { .clock_speed_hz = 20 * 1000 * 1000, // 先跑20MHz .mode = 0, .spics_io_num = PIN_CS, .queue_size = 7, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &devcfg, &spi)); }这里max_transfer_sz设成2048,是因为W5500的Socket缓冲区默认是2KB。如果你要一次读写整个缓冲区,这个值要够大。SPI_DMA_CH_AUTO让驱动自动选择DMA通道,大数据量传输时DMA能显著降低CPU占用。
4.3 W5500的读写函数
先写最基础的读写:
void w5500_read(uint8_t bsb, uint8_t offset, uint8_t *buf, uint16_t len) { uint8_t cmd[2]; cmd[0] = (0x00 << 5) | (bsb & 0x1F); // 读操作码0b000 cmd[1] = offset; spi_transaction_t t; memset(&t, 0, sizeof(t)); t.length = 16; // 2字节 = 16位 t.tx_buffer = cmd; spi_device_transmit(spi, &t); if (len > 0) { memset(&t, 0, sizeof(t)); t.length = 8 * len; t.rx_buffer = buf; spi_device_transmit(spi, &t); } } void w5500_write(uint8_t bsb, uint8_t offset, const uint8_t *buf, uint16_t len) { uint8_t cmd[2]; cmd[0] = (0x01 << 5) | (bsb & 0x1F); // 写操作码0b001 cmd[1] = offset; spi_transaction_t t; memset(&t, 0, sizeof(t)); t.length = 16; t.tx_buffer = cmd; spi_device_transmit(spi, &t); if (len > 0) { memset(&t, 0, sizeof(t)); t.length = 8 * len; t.tx_buffer = buf; spi_device_transmit(spi, &t); } }注意这里我把地址阶段和数据阶段分成了两个事务。这样做的好处是代码清晰,坏处是CS会在两个事务之间拉高。W5500的数据手册要求CS在整个帧期间保持低电平,所以严格来说这样是不对的。
正确做法是把地址和数据拼在一个事务里:
void w5500_read_single(uint8_t bsb, uint8_t offset, uint8_t *data) { uint8_t tx[3] = { (0x00 << 5) | (bsb & 0x1F), offset, 0x00 }; uint8_t rx[3]; spi_transaction_t t; memset(&t, 0, sizeof(t)); t.length = 24; // 3字节 t.tx_buffer = tx; t.rx_buffer = rx; spi_device_transmit(spi, &t); *data = rx[2]; }这样CS在整个24位期间保持低电平,符合W5500的时序要求。对于多字节读,可以动态分配缓冲区:
void w5500_read_buf(uint8_t bsb, uint8_t offset, uint8_t *buf, uint16_t len) { uint8_t *tx = heap_caps_malloc(len + 2, MALLOC_CAP_DMA); uint8_t *rx = heap_caps_malloc(len + 2, MALLOC_CAP_DMA); tx[0] = (0x00 << 5) | (bsb & 0x1F); tx[1] = offset; memset(tx + 2, 0x00, len); spi_transaction_t t; memset(&t, 0, sizeof(t)); t.length = 8 * (len + 2); t.tx_buffer = tx; t.rx_buffer = rx; spi_device_transmit(spi, &t); memcpy(buf, rx + 2, len); free(tx); free(rx); }注意:DMA缓冲区必须用
heap_caps_malloc并指定MALLOC_CAP_DMA,否则DMA传输会失败。这是ESP-IDF的一个硬性要求,很多人在这里踩坑。
4.4 复位与版本号验证
W5500上电后需要复位。如果你接了RST引脚,拉低再拉高即可。如果没有接,可以通过SPI写复位寄存器:
void w5500_reset(void) { w5500_write_single(BSB_COMMON, 0x0000, 0x80); // MR寄存器,RST位 vTaskDelay(pdMS_TO_TICKS(10)); // 等待复位完成 uint8_t mr; do { w5500_read_single(BSB_COMMON, 0x0000, &mr); } while (mr & 0x80); }复位完成后,读版本号寄存器(偏移0x0039)验证通信是否正常:
uint8_t version; w5500_read_single(BSB_COMMON, 0x0039, &version); ESP_LOGI(TAG, "W5500 Version: 0x%02X", version); // 正常应该读到0x04如果读到0x00或0xFF,说明SPI通信有问题。先检查接线,再降低时钟频率,最后用逻辑分析仪抓波形。
5. 那些年我踩过的W5500坑
5.1 版本号读出来是0xFF
这是最常见的问题。原因通常有三个:
第一,MISO引脚没有上拉。W5500的MISO在CS未选中时是高阻态,如果ESP32的MISO没有内部或外部上拉,读到的就是浮空电平,表现为0xFF或随机值。解决办法是在MISO上加一个10K上拉电阻到3.3V,或者启用ESP32的内部上拉:
gpio_set_pull_mode(PIN_MISO, GPIO_PULLUP_ONLY);第二,CS引脚没有正确控制。如果你用的是硬件CS(spics_io_num指定了引脚),驱动会自动控制。但如果你手动控制CS,要注意在事务前后正确拉低拉高。我见过有人把CS接在GPIO上但配置成了普通输出,然后在代码里忘了拉低,结果SPI时钟在跑但W5500根本没被选中。
第三,SPI模式不对。W5500只支持模式0和模式3。如果你设成了模式1或模式2,数据采样边沿错了,读出来全是乱的。
5.2 大数据量传输时丢包
当你用W5500发送大量数据时,如果SPI时钟太快或者DMA缓冲区没对齐,会出现丢包。我的经验是:
- SPI时钟先降到10MHz测试,确认功能正常后再逐步提高。
- DMA缓冲区用
heap_caps_malloc分配,确保4字节对齐。 - 每次传输不要超过
max_transfer_sz,否则驱动会报错。 - 如果用了FreeRTOS,SPI事务提交函数不要在中断里调用阻塞版本,用
spi_device_queue_trans和spi_device_get_trans_result。
5.3 DHCP拿不到地址
W5500的DHCP流程需要正确配置Socket的寄存器。常见问题包括:
- 网关地址没设对:W5500的网关寄存器(GAR)必须和路由器在同一网段。
- 子网掩码错误:如果掩码设成了255.255.255.0但实际网络是255.255.0.0,DHCP广播包可能发不出去。
- Socket没打开:DHCP用的是Socket 0,必须先把Socket 0设为UDP模式并打开。
我一般会在初始化时打印所有网络参数,方便排查:
ESP_LOGI(TAG, "IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); ESP_LOGI(TAG, "GW: %d.%d.%d.%d", gw[0], gw[1], gw[2], gw[3]); ESP_LOGI(TAG, "SN: %d.%d.%d.%d", sn[0], sn[1], sn[2], sn[3]);5.4 运行几天后连不上
这个问题我遇到过两次,都是因为W5500的Socket没有正确关闭和重开。W5500有8个Socket,每个Socket都有独立的状态机。如果连接异常断开后没有正确关闭Socket,它会一直卡在某个状态,导致后续连接失败。
解决办法是加一个看门狗机制:定期检查Socket状态,如果发现异常(比如CLOSED但应该有连接),就强制关闭再重开。另外,W5500的TX/RX缓冲区指针在异常情况下可能会错乱,需要在重开Socket时复位。
6. 跑通之后:从Ping到TCP客户端
6.1 先Ping通再说
W5500初始化完成后,第一步是Ping通。如果Ping不通,后面什么TCP、UDP都是白搭。Ping通需要:
- 物理层连接正常(网线插好,Link灯亮)
- 网络参数配置正确(IP、网关、掩码)
- Socket 0配置为IPRAW模式或者用硬件Ping(W5500支持硬件Ping)
我一般先用硬件Ping测试:
// 设置Socket 0为IPRAW模式 w5500_write_single(BSB_SOCK0, 0x0000, 0x03); // Sn_MR = IPRAW // 设置目标IP uint8_t target_ip[4] = {192, 168, 1, 1}; w5500_write_buf(BSB_SOCK0, 0x000C, target_ip, 4); // Sn_DIPR // 发送Ping请求 w5500_write_single(BSB_SOCK0, 0x0001, 0x08); // Sn_CR = OPEN // ... 等待响应如果硬件Ping通了,说明SPI通信、网络参数、物理连接都没问题。接下来就可以跑TCP客户端了。
6.2 TCP客户端的Socket配置
W5500的TCP客户端配置流程:
- 设置Socket模式为TCP(Sn_MR = 0x01)
- 设置本地端口(Sn_PORT)
- 打开Socket(Sn_CR = OPEN)
- 设置目标IP和端口(Sn_DIPR, Sn_DPORT)
- 发起连接(Sn_CR = CONNECT)
- 等待连接建立(Sn_SR = SOCK_ESTABLISHED)
- 发送/接收数据
- 关闭连接(Sn_CR = DISCON)
每一步都要检查Sn_SR寄存器的状态,确保上一步完成了再执行下一步。我见过有人一口气把所有命令都发了,结果W5500根本来不及处理,状态机乱了。
6.3 数据收发的缓冲区管理
W5500每个Socket有2KB的TX缓冲区和2KB的RX缓冲区(可配置)。发送数据时,先读Sn_TX_FSR(空闲发送缓冲区大小),确认有足够空间后再写入。接收数据时,先读Sn_RX_RSR(接收缓冲区中已接收的数据大小),然后读取。
这里有个细节:Sn_TX_FSR和Sn_RX_RSR是16位寄存器,但SPI读的时候要读两次。第一次读高字节,第二次读低字节。而且读的时候要确保两次读之间没有数据变化,否则可能读到不一致的值。W5500的数据手册建议连续读两次,如果两次结果相同才认为有效。
7. 关于SPI硬件片选和软件片选的选择
ESP-IDF的SPI驱动支持硬件片选和软件片选。硬件片选就是spics_io_num指定一个GPIO,驱动自动控制。软件片选是spics_io_num = -1,你自己在事务前后拉GPIO。
我推荐用硬件片选,原因很简单:时序更准。驱动会在SPI时钟开始前拉低CS,在时钟结束后拉高CS,中间不会有额外的GPIO操作延迟。软件片选的话,你在代码里拉低CS和提交事务之间会有几微秒的延迟,高速时钟下可能导致W5500采样错误。
但硬件片选有个限制:一个SPI总线上的每个设备必须用不同的CS引脚。如果你要挂多个W5500或者其他SPI设备,每个设备都要分配一个独立的CS。ESP32的GPIO数量有限,这时候可以考虑用74HC138之类的译码器扩展片选。
8. 调试工具与手段:没有逻辑分析仪怎么办
逻辑分析仪是调试SPI的利器,但不是每个人都有。没有逻辑分析仪的情况下,我一般用这几招:
第一,降低时钟频率。把SPI时钟降到1MHz甚至100KHz,用万用表或者LED就能看出CS和SCLK有没有动作。
第二,用ESP32自己抓波形。ESP32的GPIO可以配置成边沿中断,在CS或SCLK变化时记录时间戳。虽然精度不高,但能看出有没有时钟输出。
第三,读ID寄存器。W5500的版本号寄存器是最简单的验证手段。如果版本号读不对,其他都不用试了。
第四,分段测试。先只发地址阶段,用示波器看MOSI上有没有正确的字节。确认地址阶段没问题后,再加数据阶段。
我个人的经验是:SPI问题90%是接线问题,9%是模式配置问题,1%是芯片坏了。所以遇到问题先检查接线,特别是MISO和CS。
9. 从W5500延伸到其他SPI以太网方案
W5500不是唯一的SPI以太网芯片。市面上常见的还有ENC28J60、LAN8720(RMII接口,不是SPI)、DM9051等。W5500的优势是硬件TCP/IP协议栈,芯片内部实现了TCP、UDP、ICMP、DHCP等协议,ESP32只需要通过SPI读写寄存器和缓冲区即可,CPU占用极低。
相比之下,ENC28J60只有MAC和PHY,TCP/IP协议栈要在ESP32上跑,占用大量CPU和内存。LAN8720是RMII接口,需要ESP32的以太网MAC支持,引脚占用多但速度快(100Mbps)。
选型建议:
| 芯片 | 接口 | 协议栈 | 速度 | CPU占用 | 适用场景 |
|---|---|---|---|---|---|
| W5500 | SPI | 硬件 | 10/100M | 低 | 多Socket、低功耗 |
| ENC28J60 | SPI | 软件 | 10M | 高 | 低成本、简单应用 |
| LAN8720 | RMII | 软件 | 10/100M | 中 | 高速、ESP32原生支持 |
| DM9051 | SPI | 硬件 | 10/100M | 低 | W5500替代品 |
如果你要做Modbus TCP网关、MQTT客户端、HTTP服务器这类应用,W5500是省心的选择。如果你要做高速数据采集或者视频流,LAN8720更合适。
10. 最后分享几个实战中总结的小技巧
技巧一:SPI事务的队列大小要留够。如果你在多个任务里同时使用SPI,queue_size至少设为任务数乘以2。否则一个任务提交事务时队列满了,会阻塞等待,可能导致其他任务超时。
技巧二:W5500的INT引脚很有用。把INT引脚接到ESP32的GPIO上,配置成下降沿中断。当W5500收到数据或者连接状态变化时,INT会拉低,ESP32在中断里处理,不用轮询Sn_SR寄存器。这样能显著降低CPU占用。
技巧三:定期读取Sn_SR寄存器。即使你用了INT中断,也建议每隔几秒读一次Sn_SR,确认Socket状态正常。我遇到过INT引脚虚焊导致中断丢失的情况,Socket断了但ESP32不知道。
技巧四:W5500的TX/RX缓冲区可以配置。通过Sn_TXBUF_SIZE和Sn_RXBUF_SIZE寄存器,你可以把8KB的缓冲区分配给不同的Socket。如果某个Socket需要大量发送数据,可以给它分配4KB甚至8KB。
技巧五:注意SPI的CS建立时间和保持时间。W5500要求CS拉低后至少等待几个纳秒才能开始SCLK,SCLK结束后至少等待几个纳秒才能拉高CS。ESP-IDF的驱动默认满足这些时序,但如果你手动控制CS,要加延时。
技巧六:用spi_device_acquire_bus和spi_device_release_bus。如果你有多个SPI设备共享总线,在操作W5500之前先获取总线,操作完成后释放。这样能避免多个设备同时操作总线导致的冲突。
技巧七:W5500的复位时间要够。上电后W5500需要至少1ms的复位时间,复位完成后还要等待PLL锁定(约1ms)。如果你复位后立刻读寄存器,可能读到无效值。我一般复位后延时10ms再操作。
技巧八:注意电源噪声。W5500在发送数据时电流会突然增大,如果电源滤波不好,可能导致SPI通信出错。建议在W5500的VCC引脚附近加一个10uF和一个0.1uF的电容。
这些经验都是我在实际项目中一点点积累的,有些是踩了坑才明白的。SPI和W5500本身不复杂,但细节很多,任何一个细节没注意到都可能导致通信失败。希望这篇内容能帮你少走一些弯路。