ARTICLE DETAIL

资讯详情

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

FPGA网络通信从零到通:UDP/ARP最小系统实战指南

FPGA网络通信从零到通:UDP/ARP最小系统实战指南 很多学FPGA的朋友到了某个阶段都会冒出同一个念头能不能让我做的板子直接连网线电脑Ping通它甚至通过局域网发个指令控制它这事儿听着挺唬人但拆开了看其实就是MAC控制器加物理层收发芯片再加一套握手协议的事。这篇内容果然按原计划走到了第七篇网络通信。先给结论FPGA做网络通信并不是要求你从零写出一个TCP/IP协议栈也不是非得啃完几百页的协议文档才能动手。在真实项目里多数场景要的只是把“网口”这个东西跑通——能收包、能发包、能跟电脑或者别的设备对上话。把这个链路拆成物理层PHY芯片、数据链路层MAC、网络层IP/ICMP/UDP三个层次你会发现每一层都有成熟的方案和固定的套路。这篇文章我就按这个思路把一个近似0基础的FPGA开发者也能源码级搞明白的FPGA网络通信设计从方案选型到实际调通的过程完整捋一遍。1. 先搞清楚方向FPGA网络通信到底在做什么1.1 三个层次的分工和边界网络通信里有个很朴素的分层物理层、数据链路层、网络层。FPGA开发里我们接触到的最常见组合是外部一颗PHY芯片加FPGA内部逻辑实现MAC然后往上跑IP、UDP或者ICMP这种轻量级协议。物理层说白了就是网口和PHY芯片。“PHY”这名字可能吓人你把它当成一个把数字信号变成模拟差分信号、再把模拟信号变回数字信号的“翻译器”就行。它负责把MAC层送过来的并行数据按照千兆或者百兆的速率串行化之后怼到网线上同时负责载波监听、自动协商这些事。数据链路层是FPGA开发者的主战场。这一层做的事情包括拼装以太网帧、插入MAC地址、处理CRC校验、填充帧间隙、处理冲突CSMA/CD已经很少用了现在全双工模式基本忽略它。如果你用的是Xilinx的Vivado这里可以选择自带的三速以太网MAC IP核也可以自己用状态机一行一行写。网络层的活儿就相对“软件化”了。FPGA里通常用轻量级的UDP代替TCP因为UDP无状态、实现简单、适合FPGA这种硬件逻辑。ICMP的Ping响应则是网络通没通最直观的判断方式调试网络通信基本先把这个做出来。1.2 FPGA和单片机的网络方案到底差在哪很多人问过为什么不用STM32H743这类自带MAC的单片机来做网络通信非得绕一大圈用FPGA这个问题得分场景。如果只是做个设备上报数据单片机加W5500或者LAN8720这种方案上手快、资料多吃掉现有大部分MCU开发者的需求。但单片机方案有几个天花板很致命逐包处理的吞吐量上不去、中断响应有延迟、每路连接的协议处理占用大量CPU时间。FPGA的价值在网络数据的处理加速和确定性延迟上。举例来说在测量仪器中做波形数据的实时网络回传要求每秒钟持续输出几十万包且延迟抖动在微秒级单片机在这个量级基本扛不住。FPGA则可以用纯粹的组合逻辑和流水线结构让数据从MAC接收进来、经过简单处理、再从MAC发出去这个过程几乎零CPU介入。我现在做的项目里用FPGA加RGMII接口的PHY芯片整体跑千兆UDP收发数据通路完全不经过CPUFPGA里跑的是一个自由运行的收发状态机。整个数据通路的延迟用示波器实测只有几个微秒。这个数在单片机方案里想都不敢想。1.3 明确本篇的能力边界为了让从近似0基础开始的读者不被劝退这篇内容对“FPGA的网络通信设计”划一条明确边界本篇目标是搭建一个能跑通ARP响应和UDP收发的最小系统覆盖PHY芯片驱动、RGMII接口时序、MAC帧收发、ARP处理和UDP收发这几大块。至于TCP这种有状态协议、多端口路由查找、DMA卸载到DDR这类进阶内容放到后续篇章展开。为什么定这个边界因为UDP加ARP这套组合已经是FPGA网络开发里性价比最高的起步方案了——它能证明你整个链路是通的又不像TCP那样要求实现超时重传、滑动窗口这些复杂逻辑。工程上新项目评估网络方案时我也习惯先让硬件把UDP收发跑通再谈别的这一步通了其他都只是加模块的事。2. 硬件层的关键决策PHY芯片选型与RGMII接口2.1 常见PHY芯片对比与选型心得FPGA开发板常见的PHY芯片就那几颗瑞昱的RTL8211系列、美满的88E1512国产还有裕太微的YT8512系列。板子用的是什么芯片直接决定你在Vivado里怎么配置MDIO管理接口、GMII还是RGMII的数据接口、时钟怎么接。从新手角度分优先级的话第一优先是资料数量第二是接口类型第三才是芯片价格。开发板上最常见的是RTL8211特别是RTL8211E这颗千兆PHY、支持RGMII、市面上几乎所有FPGA开发板资料都拿它举例。88E1512我在好几块板子上用过性能和RTL8211相当但寄存器和MDIO时序有些差异别直接抄RTL8211的驱动代码。数据接口的选择很关键。GMII用8位数据并行传输125MHz时钟电平标准3.3VRGMII用4位数据双沿采样125MHz时钟下等价速率还是千兆电平标准2.5V。RGMII的引脚数量少一半布线压力小很多现在主流千兆方案基本都走RGMII。我做实际项目选型时有一条经验看PHY芯片有几路电源和电平标准匹配情况。有些PHY需要独立的1.0V核心电压加2.5V IO电压如果板子上没有对应电源轨得额外加LDO这件事在选型阶段必须确认清楚不然原理图画到一半发现电源不够各种难受。2.2 RGMII时序新手最容易翻车的点RGMII接口说穿了就是4根数据线加一个时钟线发送方向TXD[3:0]加GTXCLK接收方向RXD[3:0]加RXCLK再加上TX_CTL和RX_CTL两根控制线。数据在时钟的上升沿和下降沿各采一次上升沿送低4位下降沿送高4位。这里有个很细节的问题TXD[3:0]数据在GTXCLK的两个沿都有效对应的建立保持时间要求非常严格。Vivado里做约束的时候RGMII的时钟和数据之间通常要加2ns左右的引脚延迟约束这个值来源于PHY芯片数据手册里对setup/hold时间的定义。实际调试中如果你发现Ping不通、或者偶尔通偶尔不通很大概率就是这里出了问题。我自己调试RGMII时吃过一次亏FPGA端用ODDR原语输出数据但数据切换点和时钟沿的偏移没控制好导致近端回环测试全对、接入PHY后收包全错。后来在约束里把时钟相对数据的延迟调整了1.5ns问题立刻消失。接收方向的时序更复杂。PHY送给FPGA的RXCLK和RXD是同步关系但时钟和数据之间的相位差每次上电可能有微小变化这属于正常的时钟数据偏斜。新手的做法是直接用IDDR在RXCLK两个沿采样RXD但工业级设计通常会用MMCM/PLL对RXCLK做相位调整或者用IODELAY做数据延迟校准才能在温度电压变化时保持稳定。2.3 PHY芯片复位与配置的常见误区PHY芯片的初始化是很多人拿到板子后第一道坎。硬件上PHY有个复位引脚低电平复位正常工作要拉高。这听起来简单但要注意复位信号不能跟FPGA配置完成信号直接连否则PHY可能在上电瞬间进入未知状态。我踩过的坑是这样的PHY的复位引脚接的是FPGA的一个普通IOFPGA配置完成后程序里给它拉高。看上去没问题但实际上PHY还没完成上电初始化这时候的复位脉冲根本没起作用。正确做法是硬件上用一个RC延时电路拉低PHY复位引脚让它在上电后有几十毫秒的稳定复位期之后再释放FPGA软件上的复位控制只是辅助手段。PHY的MDIO管理接口是用来读写PHY内部寄存器的比如自动协商状态、连接状态、速度模式这些信息。FPGA端写一个简单的MDIO控制器并不难难点在于时序要符合MDIO标准——MDC时钟最高2.5MHzMDIO数据在MDC上升沿采样。很多人的FPGA代码里MDC给的太快PHY根本不响应读回来的寄存器全是0xFF表现就是啥都读不到。3. MAC层的实现路径自己写还是用IP核3.1 两种主流方案的一次彻底对比MAC层实现有两条路线完全自己写Verilog或者用Vivado里自带的三速以太网MAC IP核加DMA。这两条路我都走过各自的优缺点很明显。自己写MAC的好处是可控性极高。你可以完全理解每一个状态跳转、每一个计数值的含义出问题的时候能看波形直接定位。坏处是验证工作量巨大——以太网帧格式虽然不复杂但各种边界条件短帧、超长帧、CRC错误、半字节对齐错位如果不在设计之初就覆盖到后期调起来极其痛苦。用IP核的好处是省心。Xilinx的三速以太网MAC核在千兆模式下可以配置成RGMII接口内部自带FIFO和AXI4-Stream接口上层的用户逻辑只需要处理AXI4-Stream的数据流就行。坏处是IP核配置选项多有些选项不理解的时候容易配出一个“看似正常但实际不能用”的核。我的建议很直接从近似0基础的读者早期学习阶段一定自己写一遍简易MAC。不求功能完整哪怕只支持千兆全双工模式、只处理标准以太网帧也要亲手把前导码、帧起始定界符、MAC地址、类型长度字段、数据、CRC这一整条链路写完。写完这一遍后面不管用什么IP核心里都有底。3.2 自写MAC的模块划分与关键技术点如果决定自己写MAC模块划分建议按收发两个方向分开发送方向MAC接收上层的数据拼装以太网帧加上7字节前导码0x55交替和1字节帧起始定界符0xD5然后按字节把数据送出去最后计算并附加4字节CRC32。这里有个小细节前导码在很多PHY芯片里是自动生成的你用RGMII发送数据时不需要自己加前导码PHY会帮你加上。如果自己加了可能出现帧间隔过大或者PHY识别不了的情况。接收方向MAC从RGMII接口收到数据后要去掉前导码和FCS校验字段提取出目标MAC地址、源MAC地址、类型字段把有效载荷交给上层逻辑。这里最容易出问题的是CRC校验。以太网用的CRC32多项式是0xEDB88320反转后的多项式计算时初始值是0xFFFFFFFF算完以后结果要取反。很多人自己写的CRC模块和PHY芯片算出来的结果对不上往往是初始化值或者字节序搞错了。还有一点很多人容易忽略帧间隙IFG。千兆以太网规定两个帧之间的最小间隔是96比特时间也就是12个字节时间。如果你的MAC发送状态机太“勤奋”上一帧发完立刻发下一帧没有等够96比特对端的MAC会把这些帧当作冲突帧或者直接丢弃。这个字段在调试抓包时特别明显Wireshark里如果看到一堆CRC错误和短帧先检查IFG有没有做对。3.3 Vivado IP核方案的关键配置经验用Vivado里的Ethernet IP核时配置界面上有几个关键选项要特别注意。PHY接口选择RGMII之后会要求你选是否开启MDIO管理接口、是否开启Statistics统计接口。新手建议把MDIO打开这样可以通过MDIO读写PHY寄存器确认链路状态。Statistics接口可以先不开它只是提供一些计数器不影响收发功能。时钟部分要特别注意。MAC核的client时钟是用户逻辑的工作时钟千兆模式下是125MHz。PHY侧的GTXCLK由FPGA内部逻辑产生通常用ODDR原语把125MHz时钟送给PHY。板子上如果没有独立的125MHz参考时钟可以用一个PLL从PHY提供的RXCLK反推或者用板载的任意适当频率时钟倍频出来。IP核的用户接口是AXI4-Stream。发送侧需要你把数据拼成AXI4-Stream的包格式用tlast信号标识包尾tvalid/tready做握手。接收侧解析出tdata、tkeep、tlast再往下游分发。这里有个容易坑的地方Ethernet核的tkeep含义发送时如果最后一拍不是完整4字节tkeep要对应设置不然后端PHY发出的数据会多出几个脏字节导致CRC校验失败。4. 从零做通UDP收发一个最小系统的完整实操4.1 系统架构与文件组织我现在做的这个UDP收发最小工程整体架构分五块RGMII接口收发模块、MAC层收发模块、MAC地址过滤模块、ARP请求响应模块、UDP数据分发模块。如果你跟着这个架构来做文件组织建议这样建rtl/phy_interface/放RGMII接口的ODDR/IDDR原语封装和时钟处理rtl/mac/放发送MAC和接收MAC的状态机代码rtl/arp/放ARP请求检测和响应生成模块rtl/udp/放UDP包头分析、校验和计算、数据分发逻辑rtl/top/放顶层文件和时钟约束文件这样按功能分目录的好处是后面加功能时不用在层层include里找文件。我做项目时习惯每个模块一个文件文件命名跟模块名一致仿真时单独跑每个模块的testbench也会方便很多。4.2 UDP收发的核心状态机设计接收方向RGMII进来的数据先经过MAC接收模块把以太网帧头解析完判断目标MAC是否等于本机MAC或者广播地址不是就丢。确认是本机帧之后解析以太网类型字段。如果是0x0806就是ARP包转给ARP模块处理如果是0x0800就是IPv4包再往IP层送。IP层解析比很多人想的简单。偏移量从以太网头之后开始第0字节是版本号加IHL第9字节是协议号UDP是17第12到15字节是源IP地址第16到19字节是目的IP地址。FPGA里做这个解析就是字节计数器加组合逻辑判断注意IP头不是定长的首部长度字段IHL表示的是32位字的数量最小是5。UDP层再往后解析端口号和长度字段。我的实现里做了一个简单的查找表目的端口号等于约定的端口就收下数据不等于就丢弃。数据往用户逻辑送的时候用一个FIFO缓存解决MAC层数据速率和用户逻辑消化速率不匹配的问题。发送方向用户逻辑把要发送的数据写入发送FIFOUDP发送模块从FIFO读出数据依次拼出UDP头源端口、目的端口、长度、校验和、IP头版本、IHL、总长度、协议、源IP、目的IP、MAC头目的MAC、源MAC、类型。跟PHY_MAC那边握手发完一帧再发下一帧。4.3 让我把“Ping通”的过程一步步拆给你看上面这个工程下载进FPGA之后你第一个要验证的就是电脑能不能Ping通FPGA。而这背后其实是一个ARP请求和响应的过程。电脑要Ping板子时先检查自己的ARP缓存表看板子的IP对应的MAC地址在不在。如果不在先发一个ARP广播包目标MAC是全F类型字段0x0806操作类型1表示请求问“谁的IP是192.168.1.10请告诉我你的MAC地址”。FPGA收到这个广播包后MAC层先做地址过滤判断——广播地址放行然后交给ARP模块。ARP模块检查操作类型是不是请求、目标IP是不是本机如果是就组织一个ARP响应包源MAC等于本机MAC目的MAC等于请求方的MAC操作类型2把自己的IP和MAC填进回复里发回去。电脑收到ARP响应后更新ARP缓存表这时候它才知道FPGA的MAC地址然后正式发出ICMP Echo Request。FPGA收到这个ICMP包后解析到协议号是1ICMP就把ICMP类型字段改成0Echo Reply重新计算IP头和ICMP的校验和把原数据回发出去电脑这边就显示Ping通了。这个过程中有个容易出bug的点是校验和计算。IP头校验和是16位累加和的反码算法本身不难但要注意累加的时候每16位做一次进位回卷carry wrap-around否则结果差1。ICMP校验和还要把ICMP头和数据内容一起算进去。很多新手写的校验和模块只算了IP头没算ICMP部分导致Ping包能到FPGA但发出的响应包电脑不认Wireshark上看到一堆校验和错误。4.4 约束文件里这几个值建议直接抄约束这事最容易劝退新人我直接把我试验能用的几条关键约束列在这里供参考。RGMII接口的时钟约束使用create_clock把PHY提供给FPGA的RXCLK定义为时钟使用set_input_delay把数据相对时钟的延迟范围约束在0到2ns之间。发送方向需要约束ODDR输出数据和GTXCLK的时序关系把输出延迟约束在1.5ns到2.5ns之间具体数值根据PHY数据手册调整。MDIO接口的约束相对简单MDC时钟频率设置2.5MHz只需要约束它和MDIO数据之间的关系数据延迟约束在几十纳秒级别就够。我自己踩过的一个坑用Vivado综合后忘记检查“Property”里的I/O标准配置。RGMII接口电平标准应该配成LVCMOS25或者LVCMOS33具体看PHY芯片的IO电源电压。如果配成默认的LVCMOS18PHY接口上电平不匹配信号完整性完全崩溃表现是链路协商正常但收发数据全乱。5. 调试实录我从“Link Up但收发失败”到彻底跑通的全过程5.1 四个典型问题的定位思路调试FPGA网络通信核心是靠两层东西一是FPGA内部的ILA逻辑分析仪抓信号二是外部的Wireshark配合tcpdump在电脑侧抓包。两边对着看能很快定位问题出在哪一端。问题一PHY链路状态始终Link Down。先查PHY寄存器通过MDIO读寄存器偏移0x01的链路状态位。如果读出来不对先检查PHY的复位时序和电源。如果寄存器能读但状态不对检查原理图上PHY的时钟电路很多PHY芯片需要一个25MHz的有源晶振才能工作晶振没起振PHY自然无法协商。问题二Link Up了但电脑Ping不通。先用Wireshark看电脑有没有发出ARP请求。如果发出了但FPGA没回复用ILA抓FPGA内部的MAC接收模块看前导码是否被识别、目标MAC过滤是否放行。最常见的是MAC过滤逻辑没考虑广播地址直接把ARP请求丢了。问题三FPGA能收到ARP请求但回复的包电脑不认。用Wireshark看回复包的源MAC地址和源IP地址是否对得上。很多人的代码里ARP响应填的源MAC是从请求包里解出来就直接用结果把请求方的MAC填回去了这会导致电脑收到后更新ARP缓存为空甚至报错。问题四UDP数据能收到但内容不对。先用循环发送测试命令方式电脑向FPGA发送固定长度的数据FPGA收到后原样回传在Wireshark里逐字节比对。如果字节错位八成是MAC层的tkeep信号处理错了最后一个字节的使能没弄对数据整体错位了几个字节。5.2 用ILA和Wireshark交叉定位的完整实例我调试一个具体项目时FPGA板子Link状态正常电脑能Ping通但用网络调试助手发一个UDP报文FPGA完全没有反应。我先用Wireshark看电脑的发送记录确认报文确实发出来了目标端口是3000。然后在FPGA工程里加上ILA触发条件设置为以太网帧类型字段等于0x0800结果发现ILA根本没有触发——说明MAC接收模块压根没发出完整的以太网帧头数据。进一步抓接收数据发现前导码后面紧跟着的SFD没被识别再查代码原因是SFD判断条件写成了连续8字节的0x55且第九字节是0xD5但实际RGMII接口在双沿采样的逻辑里每一拍数据是4bitSFD的完整判断应该分两拍处理。代码把低4位和高4位的SFD判断顺序写反了。这个问题如果在纯仿真的testbench里可能发现不了因为仿真时你给的数据是理想对齐的但实际PHY芯片给出的数据流里SFD的第二拍紧跟着第一拍时序非常紧凑稍微有点偏斜就出错。用ILA抓波形后一目了然0xD5这个值被拆成了两个4位先来的是高4位0xD然后才是低4位0x5而代码在判断时把两个半字节组合的顺序搞反了SFD永远匹配不上。5.3 一套可复用的“先通链路、再通数据、后上层”调试法这套方法论在我做过的所有带网口的FPGA项目里都实用严格按步骤来可以少走很多弯路。第一步先确认物理层链路通没通。读PHY寄存器看连接状态位、速度和双工模式。这里要特别注意自动协商结果是不是你期望的千兆全双工如果协商出来是百兆或者半双工要回去查PHY的时钟和配置引脚。第二步做回环验证。RGMII接口有个近端回环功能可以在PHY里配置寄存器开启。开启后数据从FPGA发出PHY直接把它绕回给FPGA接收不需要经过网线对端。这能确认FPGA和PHY之间的RGMII数据通路本身没问题是排查问题最有效的手段之一。第三步做电脑和板子的互通。电脑固定IP和板子同一网段先Ping通再说。Ping通说明ARP和ICMP都通了MAC层没问题。第四步再测UDP。先用最简单的回环方式板子收到UDP就原样发回确认双向通路都通。然后再加业务逻辑比如用户模块对数据做特定处理后发送这时候出问题就是业务逻辑的事收发通路已经排除了。每次加一层验证一层问题绝不会堆到最后一起爆发。5.4 常见问题速查表现象可能原因排查方法PHY Link灯不亮PHY复位时序问题/电源异常/晶振未起振示波器查复位电平和25MHz时钟Link灯亮但Ping不通MAC接收SFD判断错误/RGMII延迟约束不对ILA抓MAC接收核对SFD和数据偏移Ping偶尔通偶尔不通RGMII数据时钟偏斜/MDC时钟太快调整RGMII延迟约束MDC降到2.5MHz以下收包CRC校验错误MAC发送IFG不够/CRC字节序不对Wireshark看错误帧核对CRC算法收到ARP请求但无响应MAC地址过滤丢弃了广播包ILA检查过滤逻辑是否放行0xFFFFFFFFFFFFUDP数据错位tkeep信号处理错误比较回传数据和原数据核对字节偏移上电偶发Link失败PHY复位时间太短调整RC延时电路确保复位脉宽稳定6. 从能通到好用关于性能和稳定性的一点提升思路FPGA网络通信做到“能通”只是第一个里程碑工程上真正交付级的要求还有性能、稳定性、资源开销这几关。这里分享几个后期优化方向都是我自己走过的路对入门者有很好的方向指引。第一是缓存和时序优化。现在只是简单的FIFO暂存数据量大了之后FIFO深度要按最坏情况设计避免在MAC和用户处理速度不匹配时产生FIFO溢出。可以把用户逻辑按事务型处理发一次请求收一次响应这样不会瞬间把FIFO打爆。第二是协议栈的扩展方向。如果在UDP基础上增加TCP工作量会翻好几倍。TCP的状态机很复杂建议直接评估现有的开源TCP栈比如Alveo板卡上的XDMA TCP方案或者Xilinx的TCP/IP核而不是自己从零写。工程上成本最高的不是代码量而是状态机各种边角情况的验证。第三是时钟域划分。完整系统的时钟域比我上面写的要复杂PHY给的RXCLK是一个域用户逻辑工作时钟是另一个域MAC核的client时钟又是一个域。跨时钟域处理建议用FIFO做缓冲不要用全局时钟网络强行拉平不同频率的时钟。我一般在项目结构上会专门建一个clk_rst模块统一管理所有时钟的产生、复位同步和时钟监控这样系统上电后每个时钟域有没有跑起来、复位是否释放都能用一个状态寄存器一览无余。这个习惯在板子上同时有DDR、PCIe、网络、图像接口的时候尤其重要——多个时钟域交互没有统一管理就是灾难。7. 最后分享几个亲手踩过才懂的关键经验第一PHY芯片的MDIO调试能力非常宝贵。很多PHY芯片自带寄存器可以控制LED指示、强制百兆/千兆、环回模式开关。调试的时候先用强制模式把速度固定不要上来就依赖自动协商。我调试时踩过自动协商正常但数据不通的怪坑反复查了半天最后把PHY切到强制千兆模式就好了。自动协商结果正常并不意味着PHY内部的所有配置都正确有时候它会通过协商把对方的能力集抓过来但如果跟FPGA的MAC配置没有对应上去就会出现链路正常但收发一塌糊涂的诡异现象。第二CRC相关的坑永远值得多说一遍。以太网CRC32的算法实现需要注意字节序和初始值。我之前做过一次自写MACCRC校验模块在仿真里全对但板子上就是几乎每一帧都报CRC错误。后来逐字节对比发现PHY芯片把数据按小端方式送到RGMII总线上跟我仿真时假设的大端字节序完全相反。这种问题仿真里很难发现最好在编码前就跟硬件工程师确认清楚PHY的字节序处理方式。第三有条件的话一定要用Wireshark配合调试。Wireshark能直接看到收到的包有没有校验错误、有没有TCP乱序、有没有广播风暴。电脑上开着WiresharkFPGA这边的代码只要改了一跑Ping就知道问题还在不在。比起盲目改代码然后用逻辑分析仪漫天抓信号Wireshark提供的信息量能帮你省下好几个小时的调试时间。这次把FPGA网络通信的最小闭环讲完了。从PHY芯片选型、RGMII接口时序到自写MAC或者用IP核再到ARP和UDP收发调通一条链路下来你已经跨过了FPGA网络开发最陡峭的一道坎。下一步建议先自己动手把这篇内容里的每个模块都看懂然后在板上跑通再考虑往TCP、DMA、多端口方向扩展。中间有任何步骤卡住了欢迎在评论区带着现象描述和抓包截图来聊这比自己埋头啃文档快得多。
返回列表