STM32与W5500以太网UDP通信:工业数据采集的稳定实现方案

STM32与W5500以太网UDP通信:工业数据采集的稳定实现方案 简介本资源是一套面向物联网嵌入式开发初学者与项目实践者的STM32以太网通信实战代码聚焦UDP协议在W5500硬件模块上的完整实现。适用于基于STM32F103系列如C8T6的单片机开发者解决从物理层连接、网络配置到应用层通信的全流程问题涵盖DHCP自动获取IP、UDP Socket创建、数据收发及连接管理等关键环节。压缩包共182个文件含44个头文件.h、42个源码文件.c、27个编译中间文件.o/.d/.crf及KEIL工程配置文件.uvprojx/.uvoptx/.axf/.hex等总大小5.88MB结构完整可直接编译下载运行。已有1436人学习下载提供成熟可复用的底层驱动SPI对接W5500、外设初始化RCC/TIM/USART/FLASH等、网络协议栈封装及调试配置说明显著降低物联网终端联网开发门槛。1. 项目缘起为什么是STM32W5500UDP最近在做一个工业数据采集终端的项目需要把分布在车间不同位置的传感器数据实时汇总到一个中控服务器上。传感器节点数量不少布线环境复杂对实时性有一定要求但数据包不大丢一两个也能接受。最开始考虑过RS485总线但布线麻烦扩展性也差也想过直接用Wi-Fi模块但车间里电磁干扰大稳定性心里没底。最后和硬件同事一合计决定上以太网用最经典的STM32F4系列做主控搭配W5500这颗硬核网络芯片通信协议就选简单粗暴的UDP。这个组合听起来有点“复古”毕竟现在ESP32这类自带Wi-Fi和蓝牙的SoC满天飞。但做过工业项目的朋友都知道稳定和可靠永远是第一位的。W5500是一颗全硬件TCP/IP协议栈的以太网控制器这意味着网络协议处理ARP, IP, ICMP, UDP, TCP不占用MCU的CPU资源STM32只需要通过SPI接口读写它的寄存器就能完成所有网络操作。对于STM32这种资源不算特别宽裕的MCU来说这简直是福音——你可以把宝贵的CPU算力留给业务逻辑、信号处理或者复杂的控制算法而不是去折腾那些繁琐的协议栈解析和状态维护。UDP协议的选择也是基于实际场景。我们的数据是周期性的传感器读数每秒发几次每个包就几十个字节。TCP的握手、重传、确认机制带来的延迟和开销在这个场景下完全是负担。UDP无连接、尽最大努力交付的特性正好匹配我发了你收到最好收不到拉倒反正下一秒新的数据又来了。这种“轻装上阵”的方式在局域网内配合简单的应用层重传或校验逻辑完全能满足要求而且代码复杂度直线下降。网上关于STM32和W5500的资料很多但要么是官方例程的简单翻译要么只讲TCP要么代码耦合度高很难直接移植到自己的项目里。我把自己从零搭建、调试到稳定运行的过程梳理了一遍重点不是贴代码而是讲清楚为什么这么选型、配置时有哪些坑、以及如何写出既稳定又易于维护的驱动层代码。如果你也在为嵌入式设备增加以太网通信能力特别是对实时性有要求的场景这篇内容应该能帮你省下不少调试时间。2. 硬件选型与电路设计要点硬件是稳定通信的基石。STM32和W5500的搭配非常成熟但硬件设计上的几个细节不注意后期调试会让你抓狂。2.1 MCU选型为什么是STM32F4主控我选择了STM32F407VET6。理由很直接充足的SPI接口W5500通过SPI通信F407有多个SPI外设我可以专门分一个SPI如SPI1给W5500避免与其他外设如Flash、屏幕冲突。主频够高168MHz的主频在处理网络数据包、打包应用层协议时游刃有余。虽然W5500分担了协议栈压力但应用层的数据组包、解析、校验还是需要MCU来做的。内存足够192KB的RAM可以开辟足够大的缓冲区来收发网络数据。UDP包虽然不大但为了避免丢包双缓冲区一个收、一个发的设计是必要的这需要内存支持。生态完善Hal库、标准库资料都极多调试工具ST-Link也便宜好用。遇到问题基本上都能在网上找到线索。当然如果你的项目成本敏感用STM32F1系列如F103C8T6也完全可行只是主频和内存低一些在组包和处理密集数据时需要更精细地优化代码。2.2 W5500模块 vs 自主设计对于快速验证直接购买现成的W5500模块是最佳选择。模块通常已经集成了网络变压器HR911105A这类、RJ45座子以及所有必要的阻容元件你只需要接上电源、SPI线和复位、中断引脚即可。这能极大降低硬件调试风险。如果你需要将以太网功能集成到自己的主板上自主设计时务必注意网络变压器Magnetics是必须的它负责信号耦合、阻抗匹配和电气隔离。绝对不能省略。通常选用带集成变压器的RJ45插座如HR911105A、J0011D21B等这样最省事。如果分开设计变压器型号、中心抽头接法要严格参照W5500官方参考设计。电源与去耦W5500的模拟部分AVDD和数字部分DVDD供电要分开最好都用LC滤波。每个电源引脚附近放置一个0.1uF的陶瓷电容去耦这是保证芯片稳定工作的基本操作。时钟电路W5500需要外部25MHz晶振。晶振要尽量靠近芯片负载电容的取值要根据晶振规格书调整这是网络通信稳定的时钟基础。复位与中断引脚RSTn复位引脚建议通过一个RC电路如10k电阻上拉0.1uF电容对地做上电复位同时MCU最好也能控制它方便软件复位。INTn中断引脚连接到MCU的外部中断引脚用于事件通知如数据收到、发送完成。注意原理图设计阶段一定要找到WIZnetW5500厂商官方的参考设计原理图Reference Schematic对照着画。自己凭空想象很容易遗漏关键滤波电容或电阻导致通信不稳定。2.3 SPI接口配置速度与模式的坑W5500支持标准SPI模式0和模式3。我强烈建议使用模式3CPOL1 CPHA1这也是官方例程常用的模式。接线方面SCSn片选 MCU的任意GPIO。SCLK时钟 MCU的SPI时钟引脚。MOSI主出从入 MCU的MOSI接W5500的SI。MISO主入从出 MCU的MISO接W5500的SO。最大的一个坑是SPI时钟速度。W5500的最高SPI时钟可以到80MHz但并不是越快越好。过高的速度在长线连接或布线不理想时容易导致数据出错。我个人的经验是在初期调试时先将SPI时钟分频设置得低一些比如先降到1MHz或更低确保基础读写功能正常。待通信稳定后再逐步提高速度如到20-40MHz并长时间进行大数据量传输测试观察是否出现偶发性通信失败。STM32的SPI时钟配置在Hal库中很容易调整这是一个重要的调试手段。3. 软件驱动层抽象与封装的艺术直接操作W5500的寄存器非常繁琐。一个好的驱动层应该向上提供清晰、简单的接口向下封装硬件的复杂性。我的驱动层主要分为几个部分硬件抽象层HAL、W5500基础驱动、Socket管理、以及应用层回调机制。3.1 硬件抽象层SPI与GPIO这一层的目的是将MCU特定的操作SPI收发、GPIO控制抽象成统一的函数方便未来更换MCU或SPI外设。我定义了以下接口// spi_hal.h typedef struct { void (*Init)(void); uint8_t (*ReadWriteByte)(uint8_t data); void (*Select)(void); void (*Deselect)(void); } SPI_Device_t; // gpio_hal.h void W5500_RST_Set(uint8_t state); // 控制复位引脚 void W5500_CS_Set(uint8_t state); // 控制片选引脚 uint8_t W5500_INT_Read(void); // 读取中断引脚状态在实现文件里再将这些函数具体化为STM32 Hal库的调用。例如SPI_ReadWriteByte就是调用HAL_SPI_TransmitReceive。这样做的好处是驱动核心代码w5500.c完全不关心用的是STM32还是别的单片机它只调用spiHal.ReadWriteByte()这样的函数。3.2 W5500基础驱动寄存器读写与初始化这是驱动层的核心主要完成两件事寄存器读写函数和芯片初始化流程。寄存器读写W5500的寄存器分为通用寄存器和Socket寄存器通过地址段区分。读写函数需要指定块Block、地址偏移Offset和数据。void W5500_WriteReg(uint8_t block, uint16_t offset, uint8_t data) { W5500_CS_Low(); // 发送写操作码、地址高位、地址低位 spiHal.ReadWriteByte((offset 8) 0xFF); // 地址高字节 spiHal.ReadWriteByte(offset 0xFF); // 地址低字节 spiHal.ReadWriteByte((block 3) | 0x04); // 块选择 写命令 spiHal.ReadWriteByte(data); // 写入数据 W5500_CS_High(); }读操作类似只是命令码不同。这里的关键是理解W5500的地址编排方式官方数据手册有详细说明。初始化流程这是一个标准动作必须按顺序进行。硬件复位拉低RSTn引脚至少2ms然后拉高等待至少100ms让芯片内部稳定。软件上可以加个HAL_Delay(150)。配置网关、子网掩码、物理地址MAC、本地IP这些是网络身份信息。MAC地址要确保在局域网内唯一通常可以烧录一个到Flash或者用芯片唯一ID生成。配置中断掩码我们通常关心SOCKET事件中断所以先使能对应中断。配置Socket对于UDP我们需要初始化一个Socket比如Socket 0。设置其端口号Port、模式UDP模式、并打开Open它。初始化代码看似简单但最容易出错的地方是时序。复位后等待时间不够可能导致后续配置写入不成功。我的经验是在初始化函数里在关键步骤如写IP地址后增加一个读取验证的步骤如果读回来的值和写进去的不一样就打印错误日志这能快速定位是SPI通信问题还是芯片未就绪。3.3 Socket管理与UDP数据收发W5500支持8个独立的硬件Socket我们可以把每个Socket看作一个独立的网络端点。对于简单的UDP通信使用一个Socket就够了。发送UDP数据检查Socket的发送缓冲区是否空闲通过查询S0_TX_FSR寄存器。如果空闲空间大于待发送数据长度将目标IP和端口号写入Socket的目标IP地址寄存器S0_DIPR和端口寄存器S0_DPORT。将应用层数据通过SPI写入Socket的发送缓冲区S0_TX_BUFF。发送发送命令S0_CR SEND。接收UDP数据通常通过中断方式。当有数据到达时W5500的INTn引脚会产生低电平中断。MCU进入中断服务程序查询中断寄存器IR和Socket中断寄存器S0_IR判断是哪个Socket的什么事件这里是RECV事件。读取接收到的数据长度S0_RX_RSR寄存器。从Socket的接收缓冲区S0_RX_BUFF读取数据。这里要注意读出来的数据前8个字节是W5500自动附加的UDP头部信息包含了源IP地址、源端口、数据长度和校验和。这是非常关键的信息你需要解析这8个字节才能知道数据是谁发来的然后才是真正的应用层数据。读取完成后发送接收完成命令S0_CR RECV释放缓冲区。踩坑实录忘记处理UDP头部信息。我第一次调试时直接把读到的数据当应用数据发给了上层结果全是乱码。后来用Wireshark抓包对比才发现W5500在接收UDP数据时会在应用数据前插入8字节的头部。这个细节在数据手册里有写但很容易被忽略。正确的做法是定义一个结构体来解析这8个字节或者手动偏移读取指针。3.4 非阻塞与超时机制虽然我们可以用中断来通知数据到达但发送和接收函数本身应该是非阻塞的并且要有超时机制。例如发送函数在等待发送缓冲区空闲时不应该死循环等待而应该设置一个超时计数器比如循环检查100次每次延时1ms超时则返回错误。这能防止因为网络异常或配置错误导致整个程序卡死。int32_t W5500_UDP_Send_Timeout(uint8_t *data, uint16_t len, uint32_t timeout_ms) { uint32_t tick_start HAL_GetTick(); while (W5500_GetTxFreeSize() len) { if ((HAL_GetTick() - tick_start) timeout_ms) { return -1; // 超时错误 } // 可以在这里执行一次任务调度如果用了RTOS } // ... 执行发送操作 return 0; // 成功 }4. 网络协议栈集成与数据包处理有了稳定的驱动接下来要让数据在网络上有意义地流动。对于UDP虽然协议栈简单但依然需要一些设计。4.1 IP与MAC地址管理设备上电后IP地址如何获取对于嵌入式设备常见有三种方式静态IP最简单代码里写死。适用于网络拓扑固定的小型场景。在初始化时直接配置到W5500的寄存器。DHCP动态获取。W5500硬件支持DHCP客户端你只需要使能DHCP功能它就会自动完成IP申请、续租等过程。这对于设备需要即插即用的场景非常友好。你需要定期轮询DHCP状态并在成功获取IP后更新Socket的本地IP信息。链路本地地址APIPA当DHCP失败时可以自动配置一个169.254.x.x的地址。这需要自己在应用层实现。我的项目采用了DHCP为主静态IP为辅的策略。设备先尝试DHCP如果超过一定时间比如30秒没获取到就回退到一个预设的静态IP并点亮一个“网络异常”的指示灯。这样既保证了灵活性又避免了设备因网络问题而“失联”。4.2 应用层协议设计UDP传输的是原始数据报你需要自己定义应用层协议让接收方知道这个数据包是干什么的。一个最简单的帧结构可以这样设计[帧头2字节如0xAA55] [数据长度2字节] [命令字1字节] [序列号1字节] [数据载荷N字节] [CRC16校验2字节]帧头用于在数据流中识别一个帧的开始。数据长度指明载荷的长度方便接收方解析。命令字标识这个包的类型如传感器数据上报、参数设置请求、心跳包等。序列号用于匹配请求与响应或检测丢包。CRC校验确保数据在传输过程中没有出错。UDP有校验和但应用层再加一道CRC更保险。在STM32端你需要编写组帧和解析帧的函数。发送时将业务数据按这个格式打包调用驱动层的发送函数。接收时从驱动层拿到原始数据后先寻找帧头然后根据长度字段截取一帧校验CRC最后根据命令字分发给不同的处理函数。4.3 心跳与重传机制UDP不保证可靠但我们的业务可能要求一定的可靠性。一个常见的做法是加入应用层的心跳和确认重传机制。心跳包设备定时如每5秒向服务器发送一个特定的UDP包命令字为心跳。服务器收到后回复一个确认包。如果设备连续多次如3次没收到心跳回复就认为网络连接异常可以尝试重新初始化Socket或重启网络模块。关键数据重传对于重要的控制指令或参数设置请求采用“发送-等待确认”的模式。发送方在发出数据包后启动一个定时器等待确认包。如果在超时时间内收到确认则流程结束如果超时则重发数据包可设置最大重发次数。这就在UDP的基础上实现了一个简单的可靠传输。这些逻辑都需要在应用层实现会稍微增加代码复杂度但换来了更高的可靠性。对于非关键性的传感器数据流则可以直接采用“发了就不管”的模式以追求最高的实时性。5. 实战调试从灯都不亮到稳定通信理论说再多不如一次实际的调试。下面是我总结的调试步骤和常见问题排查表。5.1 上电自检与基础测试电源与复位首先确保W5500的供电电压3.3V稳定。用万用表量一下纹波不能太大。复位引脚在上电后的波形要用示波器看一下确保有一个从低到高的跳变。SPI通信测试这是第一步。写一个最简单的测试函数去读写W5500的通用寄存器比如版本号寄存器VERSIONR地址0x0000它应该固定返回0x04。如果读不出来或者读出来是0xFF/0x00说明SPI通信有问题。检查方向接线是否正确SPI模式是否匹配片选时序时钟极性相位可以先把SPI速度降到最低确保能正确读写寄存器。网络连接指示灯连接网线后W5500的PHY寄存器可以反映链路状态。读取PHYCFGR寄存器检查链路状态位Link。同时观察RJ45接口上的绿色链路和黄色活动指示灯是否正常亮起和闪烁。灯不亮大概率是硬件问题变压器、网线、对端设备。5.2 UDP通信功能调试当基础通信和链路都正常后开始调试UDP。配置Socket正确设置本地端口、目标IP和端口如果是固定通信对象并打开Socket。使用网络调试工具在电脑上打开网络调试助手如NetAssist、SocketTool或直接使用命令行工具ncnetcat。创建一个UDP服务器监听设备设置的本地端口。同时在设备端将目标IP和端口设置为电脑的IP和调试工具监听的端口。发送测试在设备端程序里固定发送一个字符串如“Hello UDP”。在网络调试助手中观察是否收到。如果收不到依次排查电脑防火墙是否关闭或添加了规则设备IP和电脑IP是否在同一网段W5500的网关GAR设置是否正确通常就是路由器地址发送函数是否真的执行了可以在SPI读写函数中加入调试打印看是否触发了发送命令。接收测试在网络调试助手中向设备的IP和端口发送数据。在设备端检查中断是否触发接收缓冲区是否有数据解析是否正确。这里务必用Wireshark抓包。在电脑端抓取所有经过网卡的数据包过滤出设备的IP。你可以清晰地看到设备发出的UDP包源IP、源端口、目标IP、目标端口、数据内容是否正确。电脑发出的UDP包设备是否收到了。如果设备没收到但Wireshark显示包已经到达电脑网卡那问题很可能出在设备接收代码如中断未使能、缓冲区处理错误。如果Wireshark都没看到设备发出的包那问题出在设备发送端或更底层的网络配置IP、MAC、网关。5.3 稳定性与压力测试基本通信调通后需要进行长时间、大流量的压力测试暴露潜在问题。大数据量连续发送让设备以最高速率连续发送UDP包持续数小时。观察是否会出现丢包接收端统计收到的包数是否少于发送端。UDP本身允许丢包但在局域网内丢包率应该极低0.1%。如果丢包严重检查STM32是否因为处理不过来而堵塞或者W5500的发送缓冲区是否设置得太小可以通过寄存器调整每个Socket的缓冲区大小。死机或复位看设备是否会跑飞。这可能是堆栈溢出、中断嵌套问题或者驱动层有未处理的异常状态。使用iperf3进行UDP打流这是一个专业的网络性能测试工具。在电脑上运行iperf3 -s作为服务器在设备端如果支持或另一台电脑上运行iperf3 -c 设备IP -u -b 100M进行UDP带宽测试。这能非常客观地评估网络的吞吐量和丢包率。异常情况测试热插拔网线在通信过程中拔掉网线等待几秒再插上。观察设备是否能自动检测到链路断开和恢复并重新开始正常通信。这需要你的代码能处理PHY链路状态变化的中断。对端异常关闭设备正在向某个端口发送数据但该端口上的应用程序突然关闭。设备应能正常处理发送操作可能会触发错误但不能崩溃并通过心跳机制检测到对端无响应。5.4 常见问题排查速查表现象可能原因排查步骤SPI读出版本号错误1. SPI接线错误MOSI/MISO接反2. SPI模式CPOL/CPHA设置错误3. 片选CS时序不对4. 电源或复位不正常1. 用逻辑分析仪或示波器抓取SPI波形对照W5500时序图检查。2. 降低SPI时钟频率至1MHz以下测试。3. 检查RSTn引脚复位波形。网口指示灯不亮1. 网线故障或对端设备未上电2. 网络变压器损坏或未正确连接3. W5500的PHY未正常工作1. 更换网线确认对端设备如交换机电源和端口正常。2. 检查变压器型号和原理图测量相关引脚电压。3. 读取PHYCFGR寄存器检查PHY状态和配置。能Ping通但UDP不通1. 本地/目标端口号设置错误2. 防火墙拦截3. Socket未正确打开Open4. 目标IP地址设置错误1. 使用Wireshark抓包确认UDP包是否发出端口号是否正确。2. 暂时关闭电脑防火墙测试。3. 检查Socket模式寄存器S0_MR是否为UDP模式并发送了OPEN命令。4. 核对代码中的目标IP地址。接收数据乱码或不对1. 未跳过UDP接收头部8字节2. 数据长度解析错误3. 字节序大小端问题4. 接收缓冲区溢出1.重点检查确认接收数据处理时指针是否向后偏移了8字节。2. 打印接收到的原始字节与Wireshark抓包内容逐字节对比。3. 检查应用层协议定义的长度字段是主机序还是网络序。通信一段时间后死机1. 中断服务程序处理时间过长或未清除中断标志2. 内存泄漏如动态分配未释放3. 堆栈溢出4. 看门狗未喂狗1. 确保中断函数精简快速并正确读取S0_IR寄存器以清除中断源。2. 检查所有malloc是否有对应的free。3. 增大堆栈大小或使用RTOS的任务栈分析功能。4. 检查看门狗配置和喂狗逻辑。丢包率过高1. 网络拥堵或物理链路质量差2. STM32处理不过来接收缓冲区满3. 应用层处理太慢未及时取走数据1. 更换网线、交换机端口或降低发送速率测试。2. 增大W5500 Socket的接收缓冲区大小Sn_RXBUF_SIZE。3. 优化应用层代码提高数据处理速度或采用生产者-消费者模型用队列缓冲。6. 进阶优化从“能用”到“好用”当基本功能稳定后可以考虑一些优化提升代码的健壮性和可维护性。6.1 使用RTOS管理网络任务如果你的项目业务复杂强烈建议引入RTOS如FreeRTOS。你可以创建一个独立的网络任务Network_Task在这个任务中阻塞在一个信号量或队列上等待网络事件如接收中断释放的信号量。事件到来时集中处理数据收发、协议解析、状态机维护。通过消息队列与其他任务如传感器采集任务、显示任务通信。这样做的好处是解耦。网络通信变成了一个独立的、可调度的模块不会因为等待SPI操作或处理数据而阻塞整个系统。中断服务函数ISR只做最少的操作如给出信号量将耗时处理移到任务中系统响应更及时。6.2 连接状态管理与自动恢复工业设备要求7x24小时运行网络闪断、路由器重启是常事。设备必须具备自动检测和恢复的能力。链路状态监测使能W5500的PHY状态变化中断。当网线被拔插时会产生中断。在中断服务中可以设置一个标志位。在主循环或网络任务中检测到这个标志就进行链路断开或连接的处理如停止发送、尝试重新初始化PHY。心跳超时与重连如前所述心跳包是检测对端是否存活的好方法。如果连续心跳超时除了报警还可以尝试“软重启”网络部分关闭Socket重新初始化W5500甚至包括重新DHCP获取IP再重新打开Socket。这一套流程应该自动化无需人工干预。参数掉电保存获取到的IP、网关等DHCP信息如果设备意外重启最好能保存到Flash或EEPROM中。下次启动时可以先使用上次的IP尝试通信同时重新发起DHCP请求。这可以缩短网络中断的时间。6.3 功耗考量对于电池供电的设备功耗至关重要。W5500本身功耗不高但在不通信时可以将其设置为低功耗模式。软件断电通过写PHYCFGR寄存器可以关闭PHY物理层大幅降低功耗。需要通信时再唤醒。注意唤醒后需要重新建立链路有一定延迟。网络唤醒WOLW5500支持魔术包唤醒。可以让设备在低功耗模式下监听特定的魔术包收到后唤醒整个系统。这对于远程唤醒设备非常有用。实现低功耗需要软硬件配合比如MCU本身也要进入低功耗模式通过W5500的中断引脚来唤醒MCU。这是一个系统工程需要仔细设计。7. 项目总结与代码框架建议回过头看STM32W5500实现UDP通信技术本身并不高深难点在于对硬件细节的把握、对协议栈的理解以及构建一个鲁棒的软件框架。它不像用ESP32的Arduino库那样几行代码就能联网但正是这种“底层”的控制让你对网络通信的每一个环节都了然于胸出了问题也知道从哪里下手。对于代码框架我建议分层次组织/Project ├── Drivers/ │ ├── STM32F4xx_HAL_Driver/ # HAL库文件 │ └── BSP/ # 板级支持包 │ ├── spi_hal.c/.h # SPI硬件抽象层 │ ├── gpio_hal.c/.h # GPIO硬件抽象层 │ └── w5500_bsp.c/.h # W5500硬件初始化引脚、SPI配置 ├── Middlewares/ │ └── W5500/ │ ├── w5500.c/.h # W5500基础驱动寄存器读写 │ ├── socket.c/.h # Socket管理封装 │ ├── udp_client.c/.h # UDP客户端应用封装 │ └── dhcp.c/.h # DHCP客户端实现可选 ├── Application/ │ ├── app_network.c/.h # 网络应用层心跳、协议解析、状态机 │ ├── app_sensor.c/.h # 传感器业务逻辑 │ └── main.c # 主循环调度各任务 └── Utilities/ ├── crc16.c/.h # 工具函数如CRC校验 └── debug_log.c/.h # 调试日志输出在main.c中初始化顺序至关重要先初始化MCU时钟、GPIO、SPI再初始化W5500硬件抽象层接着初始化W5500驱动并配置网络最后创建应用层任务如果用了RTOS或进入主循环。最后分享一个调试心得善用指示灯LED。我在代码里为不同的状态设置了不同的LED闪烁模式慢闪表示正在DHCP快闪表示正在发送数据双闪表示收到数据常亮表示网络就绪常灭表示网络故障。在调试初期这比串口打印更直观尤其是在没有连接调试器的时候看一眼灯就知道设备大概处在什么状态能快速缩小问题范围。本文还有配套的精品资源点击获取