简介:本资源是一套面向嵌入式开发工程师与STM32进阶学习者的完整以太网通信实践方案,聚焦STM32F4系列(尤其F429)驱动LAN8720A PHY芯片并实现TCP数据通信的核心技术链。涵盖硬件RMII接口连接、HAL库级ETH/MAC初始化、MDIO寄存器配置、lwIP协议栈移植、TCP客户端连接及收发逻辑等关键环节,适用于IoT终端、工业网关等需稳定有线网络接入的场景。压缩包含240个文件,主体为125个.h头文件与111个.c源码文件,支撑底层驱动、中间件适配与应用层通信逻辑;另有Keil工程文件(uvprojx/uvoptx)、可执行hex固件及汇编启动文件,总大小1.74MB。已有1820人学习下载,代码结构清晰,直接基于stm32f4xx_hal_eth.c等官方驱动模块扩展,集成PHY状态轮询、中断响应、DMA收发及错误日志输出等实用机制,具备即编译、可调试、易迁移的工程化特性。 做嵌入式以太网通信,STM32F4系列里自带MAC的型号不少,但真正把成本、功耗和稳定性权衡下来,F429配LAN8720A是我目前用过最舒服的组合。前阵子要给一个工业采集设备加网口,板子上主控是STM32F429IGT6,外设资源不算紧张,项目要求能通过标准网线接入局域网,和上位机做TCP数据交互。这个方案不用外扩MAC芯片,STM32F429内置的以太网MAC加上一颗便宜的PHY芯片LAN8720A就能跑起来,成本低、调试资料多,整体落地很快。整个项目做完,硬件连线和软件驱动都踩了不少坑,趁热记录下来,希望能帮到正在搞F4系列以太网的朋友。不管是刚接触以太网的新手,还是手头有成熟项目想换PHY方案的老人,这篇文章里的连接方式、寄存器配置和调试思路都能直接拿过去参考。
1. 方案选型与整体思路
1.1 为什么是STM32F429加LAN8720A
STM32F4系列里面带以太网MAC的单片机其实就那几款,F407、F417、F429、F439这些。F429相比F407多了LCD控制器和SDRAM控制器,很多时候选它不是因为要跑界面,而是因为它货量充足、主频能到180MHz,拿来跑以太网应用绰绰有余。以太网通信的完整链路是MAC层加PHY层,STM32F429内部已经集成了MAC控制器,负责数据帧的封装、校验、流控这些脏活,但物理层的信号编解码、载波侦听、脉冲变换这些事必须交给PHY芯片来完成。LAN8720A就是一颗非常典型的10/100M以太网PHY芯片,通过SMI接口用MDIO和MDC两根线就能跟STM32F429的MAC对接,数据通路走RMII接口,整体占用的引脚非常少。
我最开始也考虑过STM32F429搭配DP83848的方案,那款PHY芯片同样经典,TI的资料也很全,但算了一下成本,LAN8720A的单价大概是DP83848的一半还不到,而且封装更小,QFN24封装只有4mm乘4mm,在空间受限的板子上特别友好。不过LAN8720A有个要注意的地方,它的供电是3.3V单电源,但I/O电平可以兼容1.8V到3.3V,跟STM32F429直接连完全没问题。这颗芯片平时工作电流只有几十毫安,对电源的要求也不苛刻,很适合单片机系统。
还需要考虑的一点是供货稳定性。LAN8720A在市面上流通了很多年,引脚定义没变过,国产替代型号也出了不少,万一原厂缺货,换一个兼容芯片也能快速顶上。做产品选型的时间越长越明白,芯片不一定要最新最强的,关键是生命周期够长、用的人够多、踩坑资料够丰富,LAN8720A恰好都满足。
1.2 用STM32F429标准库,还是HAL库?
这个项目我选用的是ST标准库,而不是HAL库。有人说都202X年了还抱着标准库不放,但说实话,对于以太网这种对外设寄存器操作要求精细的场景,标准库的逻辑更直白。你想看某个寄存器的赋值,直接进库文件里搜就行,不像HAL那样绕了好几层;而且网上大量现成的STM32F429以太网工程模板都是标准库写的,我这次正好接手一套延续多年的老代码,技术栈定的就是标准库,整个工程顺势就沿用下来了。
标准库驱动以太网的思路就是三步:配置GPIO复用、配置MAC控制器、配置DMA描述符。ETH外设的库函数并不多,关键的初始化函数是ETH_Init,里面要填充一个ETH_InitTypeDef结构体,把MAC地址、双工模式、速度、自动协商这些参数都定义好。相比HAL库,标准库少了多层抽象,遇到问题直接对照参考手册查寄存器,排错效率高不少。HAL库的以太网部分也不是不能用,但它引入了LinkState回调、MspInit这种机制,新手容易被底层细节绕晕。
标准库也有一个不算缺点的缺点:官方已经停止更新了。但以太网这部分代码非常稳定,多年没变过,所以不存在库太老导致用不了的问题。反而是一些HAL库版本升级后API频繁变化,老代码迁移起来更麻烦。如果你纠结选哪个,我就一句话:别纠结,手里有哪个顺手就先用哪个,以太网通信卡住进度的一般都不是库的版本,而是原理图和寄存器配置。
2. 硬件设计与LAN8720A原理图核心模块
2.1 LAN8720A原理图核心模块拆解
先说LAN8720A原理图,很多人第一次画这个芯片的电路会觉得引脚多,实际上拆开看就四块:供电和滤波、时钟电路、网络变压器接口、LED状态指示。
供电部分,LAN8720A有VDDCR和VDD两个供电域,VDD是3.3V主电源,VDDCR是内部数字核心电源。原理图上通常会用磁珠或0欧电阻把3.3V引到VDDCR,再加一个1uF和一个0.1uF去耦电容。数据手册明确要求VDDCR必须有电压,接法上很多参考设计是直接跟VDD连在一起,但我个人习惯串一个磁珠,方便调试时测量核心功耗,还能滤掉一些高频噪声。LED指示灯的电源也要单独滤波,避免LED开关瞬间的电流波动影响到PHY内部。
时钟电路是重点。LAN8720A需要一个50MHz的参考时钟,来源有两种:一种是用外部50MHz有源晶振,直接接到XI引脚;另一种是用25MHz无源晶振配合内部PLL倍频到50MHz。大多数参考设计用的是25MHz晶振加两个18pF负载电容的方案,因为这样时钟可以从XCLKOUT引脚再引出来给MAC用,省一颗有源晶振。我用的就是25MHz无源晶振加两个18pF电容,配合内部PLL把50MHz参考时钟输出到XCLKOUT引脚给STM32F429。这个方案成本低,但晶振的负载电容必须根据芯片手册选准,偏差太大会导致起振困难或者频率不准。
网络变压器接口,LAN8720A的TXP、TXN和RXP、RXN引脚要通过网络变压器接到RJ45座。变压器的主要作用是隔离,防止共模干扰和浪涌损坏PHY芯片,同时实现电平转换。RJ45座如果自带变压器就省事了,直接把差分对连过去;如果是分离式变压器,比如HR911105A,就要多留几个电阻电容的位置。我在原理图里给TXP、TXN和RXP、RXN差分对加了终端电阻和滤波电容,PCB走线尽量等长,保证信号完整性。
LED状态指示就两路,nLED1和nLED2,可以配置成link和activity指示。原理图上一般串联1k限流电阻到LED再拉到3.3V,方便观察链路状态。调试的时候,网口指示灯能直接告诉你PHY是否连上了对端设备,这是最快速的一级故障判断手段。
2.2 RMII接口与STM32F429的引脚分配
STM32F429的以太网MAC支持两种接口模式:MII和RMII。MII需要16根数据线,占用引脚多但理论上吞吐更高;RMII只需要7根信号线,数据线只有2位,但需要外部提供50MHz的REF_CLK。我的板子最终选RMII模式,因为省引脚,而且100M速率下RMII完全够用,没必要为了理论上的带宽多占用9根IO。
RMII模式下,STM32F429需要用到这9个引脚:
- PA1 -> ETH_RMII_REF_CLK
- PA2 -> ETH_MDIO
- PA7 -> ETH_RMII_CRS_DV
- PC1 -> ETH_MDC
- PC4 -> ETH_RMII_RXD0
- PC5 -> ETH_RMII_RXD1
- PB11 -> ETH_RMII_TX_EN
- PB12 -> ETH_RMII_TXD0
- PB13 -> ETH_RMII_TXD1
这9个引脚必须严格按复用功能配置。曾经有位同事把RXD0和RXD1接反了,结果MAC一直收到乱包,排查了大半天。所以画原理图时一定要把引脚对应关系反复核对,数据手册上怎么标就怎么接。尤其注意CRS_DV信号,它在RMII模式下由PHY芯片驱动给MAC,把载波侦听和数据有效合并到一个引脚上,接错就会导致链路起不来。
REF_CLK的接法有两种:一种是LAN8720A的XCLKOUT引脚输出50MHz时钟到STM32F429的PA1;另一种是STM32F429用MCO引脚输出50MHz给PHY芯片。我强烈建议用前者,也就是用PHY自己的时钟来驱动MAC,这样两边时钟同步,软件上少配置一层MCO,排查问题的时候也少一个变量。标准库初始化时,如果采用外部PHY时钟方案,就不需要在RCC配置里开启MCO输出,直接配置GPIO复用即可。
3. 底层驱动的实现细节
3.1 通过MDIO总线读写PHY寄存器
在初始化LAN8720A之前,首先得确认MCU和PHY之间的MDIO通信正常。MDIO总线就两根线:MDC是管理时钟,由MAC输出,频率不能超过2.5MHz;MDIO是管理数据线,双向传输,用来读写PHY寄存器。
读PHY寄存器的方法很简单,标准库里已经封装好了ETH_ReadPHYRegister和ETH_WritePHYRegister这两个函数。但有个小坑是,这两个函数依赖GPIO和MDC时钟先配置好。实际项目里,我先写了一个PHY_Init函数,第一步就是读PHY的ID寄存器,也就是寄存器2和寄存器3。LAN8720A的PHY ID是0x0007C0F1,读出来能对上,说明PHY复位和MDIO通道都正常,再进行后续配置。
读取PHY ID这一步特别值得做,因为很多以太网问题其实是PHY芯片根本没有正常工作,这时候MAC层什么都收不到,光在应用层浪费时间。我每次调新板子,拿到手第一件事就是写一段简单的读ID代码,通过串口打印出来,看到0x0007C0F1才敢往下走。如果读到的全是0xFFFF,那基本可以断定硬件有问题,要么供电不对,要么复位没拉起来,要么MDIO引脚接错。
MDIO总线本身是慢速接口,操作时不需要太高的时序性能,但要注意MDC时钟频率不能太高,STM32F429默认分频出来的时钟一般在2.5MHz以内,符合PHY规范。写寄存器时还要注意MDIO前导码和数据格式,这些标准库底层都已经处理好了,不需要自己实现,但了解原理有助于排查信号完整性或者上拉电阻缺失引起的偶发读写失败。
3.2 LAN8720A的复位与初始化序列
LAN8720A的复位方式有两种:硬件复位和软件复位。硬件复位就是把nRST引脚拉低至少1ms再释放,这个时序很重要,复位时间不够会导致PHY内部状态不稳定,后续自协商一直失败。我的板子把nRST接到了STM32F429的一个GPIO上,用软件控制复位时机,而不是简单的上电RC复位,这样MCU初始化完成后再去控制PHY复位,时序完全可控。
软件复位是通过BMCR寄存器(地址0)的第15位实现的。置1后PHY会执行完整的复位流程,然后该位自动清零。实际操作时,我建议硬件复位和软件复位都做一遍:MCU上电先把nRST拉低等20ms再拉高,然后通过MDIO写BMCR的复位位,轮询等待复位完成,这样最稳妥。复位完成后不要立刻开始自协商,等个100ms左右让PHY内部稳定。
初始化序列里有一个关键操作:把PHY配置为RMII模式。LAN8720A有一个专门的寄存器叫PHY Special Control/Status Register,地址是31,Bit0就是RMII模式选择。默认值是0表示MII模式,必须写1切换成RMII。这一步漏掉的后果就是PHY和MAC速率协商不一致,表现出来是物理链路能建立,但数据完全不通,ping包全丢。
还要办的事是配置自协商。默认情况下LAN8720A上电后会自动协商速率和双工模式,一般建议开启,因为我接的是普通交换机。如果是点对点连接两台设备,自协商有可能会失败,此时可以强制设置速度和双工。具体操作是BMCR寄存器的Bit13和Bit8组合,强制100M全双工就设0x2100。我的应用场景是接交换机,所以开启了自协商。
3.3 ETH外设的RMII配置与DMA描述符
STM32F429的ETH模块初始化,关键是配置好MAC控制器、DMA控制器和描述符链表。标准库的ETH_BSP_Config函数看起来很长,但核心就几件事。
首先是MAC地址配置。用一个6字节数组把MAC地址填进去,注意高位在前。MAC地址别用太随机的值,建议从MCU的96位唯一ID芯片UID派生一个48位MAC地址,避免多台设备同时上网时MAC冲突。比如把UID的低3字节和高3字节做一次异或,生成一个大概率不重复的地址,虽然不保证全球唯一,但在局域网里足够用了。
然后是DMA描述符。以太网DMA的收发是典型的描述符环结构。STM32F429的ETH_DMA外设维护两组链表,一组接收描述符,一组发送描述符。每个描述符指向一个数据缓冲区,接收到的帧由DMA自动写入缓冲区,处理完后再回收描述符。标准库工程一般给四个接收描述符和四个发送描述符,缓冲区大小可以配置。我的接收缓冲区用了1524字节,因为要容纳最大1518字节的以太网帧加4字节CRC,虽说CRC是硬件计算的,但缓冲区留点余量没坏处。
初始化配置里还有一个参数容易让人忽略:ETH_InitStructure.ETH_DMAArbitration。这个参数是DMA收发优先级配置,在高流量场景下会影响吞吐,我的应用没有高并发需求,保持默认即可。
一旦ETH_Init返回ETH_SUCCESS,ETH DMA就开始接收数据了。之后要做的是开启ETH_DMA_IT_RX中断,在接收中断里把数据从描述符缓冲区拷贝到应用层,然后释放描述符,回到接收状态。描述符的使用是环形结构,检查描述符的OwnBit是否为0来判断是否有新数据,这是整个收包流程的核心。如果没有及时释放描述符,DMA缓冲区很快会被占满,新数据帧直接丢弃,这是以太网丢包的一个隐藏原因。
4. TCP数据通路的搭建
4.1 协议栈选择:裸机轮询还是lwIP
有了MAC和PHY,以太网链路层算是通了,但要做TCP数据通信,必须在这个基础上再跑一套TCP/IP协议栈。这里有两个方向:要么自己实现一个精简的TCP/IP协议栈,手写UDP、ARP、ICMP这些协议;要么移植lwIP这种现成的开源协议栈。我毫不犹豫选了lwIP,因为自己写协议栈的周期非常长,而且稳定性很难保证,这种成熟方案没有必要重复造轮子。
lwIP的全称是lightweight IP,是为嵌入式系统设计的轻量级TCP/IP协议栈,内存占用比Linux内核里的协议栈小一个数量级,非常适合STM32F429这种资源还算充裕但不算丰富的MCU。STM32F429有256KB RAM,跑一个带TCP Server功能的lwIP,RAM开销大概在40KB到50KB左右,完全负担得起。
lwIP的移植有一个固定套路:底层以太网驱动调用low_level_output函数发送数据,接收的数据通过low_level_input函数进入协议栈。协议栈本身与硬件无关,只要把netif结构体注册好,再把收发函数绑定进去,基本就能跑起来。如果是带操作系统的环境,lwIP可以跑在一个单独的线程里,用信号量和邮箱做进程间通信;如果是裸机,需要周期调用tcpip_thread和ethernetif_input,保证协议栈及时处理数据包。
我这次工程里加了FreeRTOS,lwIP跑在独立线程里,这样TCP Server线程不会被底层收包拖累,代码结构也更清晰。如果你不想上操作系统,裸机跑lwIP也能实现TCP通信,只要保证中断回调里不阻塞、主循环足够快就行,但多任务扩展起来会麻烦一些。
4.2 实现TCP Server的核心代码
TCP Server的实现逻辑并不复杂。在lwIP里先用netconn_new创建连接,然后netconn_bind绑定本地端口,比如8080,再netconn_listen进入监听状态,之后一直循环netconn_accept接收客户端连接。建立连接后,netconn_recv接收数据、netconn_write发送数据。这套API是lwIP的netconn接口,比raw API好理解得多,适合快速开发。
下面是我在项目中实际使用的TCP Server线程核心片段:
static void tcp_server_thread(void *arg) { struct netconn *conn, *newconn; struct netbuf *buf; err_t err; conn = netconn_new(NETCONN_TCP); netconn_bind(conn, IP_ADDR_ANY, 8080); netconn_listen(conn); while (1) { err = netconn_accept(conn, &newconn); if (err == ERR_OK) { while ((err = netconn_recv(newconn, &buf)) == ERR_OK) { void *data; u16_t len; netbuf_data(buf, &data, &len); // 这里调用应用层处理函数 process_recv_data(data, len); netconn_write(newconn, ack_data, strlen(ack_data), NETCONN_NOCOPY); netbuf_delete(buf); } } netconn_close(newconn); netconn_delete(newconn); } }这个线程是死循环,只处理一个监听连接。如果项目要求支持多个客户端,那就要在netconn_accept成功之后创建新线程处理每个连接。但多线程在MCU上开销比较大,我的场景是单上位机对单设备,单连接循环就够了。实际测试时,电脑上的TCP调试助手能稳定连接、收发数据,连续跑24小时没有断连。
lwIP在STM32F429上还有一个细节:系统时钟基准用的是SysTick,在lwip_init之前要把tcpip_init和网卡初始化配好。如果开了DHCP客户端,还要在收到DHCP应答之后更新netif的IP地址和掩码,否则获取到的IP不会生效。我这边为了调试方便,直接用静态IP,例如192.168.1.100,避免依赖路由器DHCP服务。
5. 调试记录与常见问题排查
5.1 我实际踩过的几个坑
排第一的坑是PHY地址问题。LAN8720A的PHY地址由PHYAD0引脚的上下拉决定,默认是0,我的原理图上PHYAD0脚是悬空的,理论上也应该是0。可实际焊上芯片后,MDIO读地址0一直失败,返回0xFFFF。排查后发现是贴片时PHY芯片旁边的去耦电容虚焊,导致芯片内部电源不稳定,MDIO通信时序乱掉了。重新补焊之后,读取ID立刻正常。这个教训告诉我,MDIO读失败时优先怀疑硬件电源和焊接,不要一上来就改软件。
排第二的坑是REF_CLK没起来。用25MHz晶振给LAN8720A提供时钟,但XCLKOUT引脚始终没有50MHz输出。用示波器一量,晶振根本没有起振。查了一圈,发现晶振的负载电容焊成了15pF,而LAN8720A数据手册要求的是18pF。这种几皮法的差异平时可能影响不大,但整个系统对50MHz参考时钟的稳定性要求很高,负载不匹配导致起振困难。换电容后一切正常。后来我在画原理图时,都会特意在晶振附近标注推荐电容值,防止产线换料时焊错。
排第三的坑是TCP连接建立后很快断开。这个问题查了很久,最终发现是lwIP的重传超时时间配置得太短,丢一个包就触发超时重传,重传几次失败后连接就被关闭。把lwipopts.h里的TCP_SYNMAXRTX和TCP_MSS适当调大,重新编译烧录,连接就稳定了。要注意,lwIP的默认参数是给通用场景的,用在低带宽的嵌入式环境里经常需要微调,别直接用默认值就以为万事大吉。
还有一个容易忽视的问题是DMA描述符缓冲区与Cache的一致性。F429没有D-Cache,所以不用考虑这个问题。但如果你换成带D-Cache的芯片,比如H7系列,需要处理Cache和DMA缓冲区的一致性问题,否则收到的数据会出现随机错乱。
5.2 常见问题速查表
| 现象 | 可能原因 | 排查与解决办法 |
|---|---|---|
| MDIO读PHY ID返回0xFFFF | PHY供电或复位异常、MDIO引脚接错 | 量3.3V和VDDCR电压,检查复位引脚时序,核对引脚连接 |
| 无法建立TCP连接,但串口有打印 | IP或网段配置错误 | 确认静态IP和子网掩码,先Ping通设备再连TCP |
| 能Ping通但收发数据丢包 | DMA描述符不足或缓冲区溢出 | 增加描述符数量,接收缓冲区调到1524字节 |
| 速率协商只有10M,无法到100M | RMII模式没配置 | 设置PHY寄存器31的Bit0为1,切换RMII模式 |
| 网口指示灯亮但数据不通 | TXD/RXD引脚接反或REF_CLK异常 | 用示波器核对CRS_DV和TXD信号,检查引脚复用 |
| 连接建立后反复断连 | lwIP重传超时参数不合理 | 调整TCP_SYNMAXRTX和TCP_MSS |
| 通信正常但偶尔卡死 | 中断优先级和临界区处理不当 | 确保ETH中断优先级合理,避免长时间关闭中断 |
5.3 快速定位问题的几个技巧
调以太网比调串口难的地方在于数据是双向的,出了问题不好判断是软件还是硬件。我的经验是先把问题分层:物理层看PHY的连接状态寄存器,链路层看MAC的收发统计寄存器,网络层再看ARP和Ping结果。
第一步永远是确认物理层。读取PHY寄存器1(BMSR),Bit2是链路状态,为1说明物理链路建立了。再读寄存器31的Bit2,这是LAN8720A报告的速度,0是10M,1是100M,可以确认自协商结果。这两个寄存器值读出来,物理层有没有问题基本就有定论了。
第二步确认MAC层。STM32F429的ETH_MAC有统计寄存器,能从寄存器层面看到收发帧数、CRC错误数、对齐错误数。我在调试时直接读取ETH_GetDMAFlagStatus,检查DMA的接收错误标志位。如果MAC层有错误计数持续增长,说明物理层信号质量可能不好,要检查PCB走线、网络变压器和RJ45座。
第三步才轮到协议栈。如果物理层和链路层都好,但TCP连不上,那就用电脑上的Wireshark抓包,看设备有没有发出ARP响应,有没有SYN包回来。Wireshark能直接判断是设备没发包还是PC端没收到,把问题范围快速缩小到某一侧。这个方法比瞎猜高效太多,我在嵌入式网络调试中强烈推荐。
6. 实测效果与后续扩展
6.1 数据吞吐做一个初步估算
在稳定建立TCP连接之后,我做了简单的吞吐测试。PC端通过网线直连设备和通过交换机接入两种方式都测过。直连时自协商结果是100M全双工,TCP单连接单向上传速度实测在85Mbps左右,这个数据对大多数工业通信场景都够用了。如果对端是交换机,速率会受交换机背板带宽影响,但也在合理范围。
实际上,对于这个采集设备来说,每秒上报的数据量也就几百KB,根本跑不到线速,吞吐余量非常充足。所以做以太网设计时不用一味追求极限吞吐,把稳定性做好才是关键。在连续跑了两天的高低温箱测试后,TCP连接没有断过,链路层零丢包,这个结果给了我很大信心。
做吞吐测试时有一个小细节:电脑端的网卡高级设置里最好关闭节能以太网和流控,否则实测速率会受影响。另外,用iperf测试比用TCP调试助手发大文件更准确,因为iperf能排除应用层瓶颈,直接测协议栈的吞吐上限。
6.2 这个方案还可以怎么扩展
STM32F429加LAN8720A这个平台的可扩展性很强,简单列几个可以继续做的方向。
一是从单客户端改成多客户端支持,把netconn_accept后的处理逻辑拆成多线程,就能同时服务多台上位机。配合FreeRTOS的信号量和队列,可以把接收到的数据分发给不同的任务处理。不过要注意,每增加一个TCP连接,lwIP的PBUF和线程栈都要跟着增加,RAM占用会明显上升,需要预留充足空间。
二是加Modbus TCP协议,把采集到的传感器数据通过Modbus寄存器方式暴露给上位机,这样不需要自定义协议,直接用现成的Modbus调试工具就能读取数据。Modbus TCP的报文基于TCP承载,在lwIP上调通TCP后再套一层Modbus解析并不难。我目前正在做这个扩展,主要工作量在协议解析和寄存器映射,底层TCP通路已经很稳定。
三是结合F429自带的SDRAM和LCD控制器,在本地做数据可视化界面,以太网收到的数据直接显示在屏幕上,调试的时候不用外接上位机,一台设备就能完成全链路验证。这就属于项目后期锦上添花的部分了,前提是底层通信已经足够可靠。
做这类项目越到后面越能体会到,硬件方案选型的第一颗螺丝拧对了,后面所有工作都水到渠成。STM32F429内置MAC加LAN8720A的组合,是当前F4系列里做以太网最稳的一条路,调试工具成熟、参考代码多、成本还低,值得推荐给做工业数据采集和物联网网关的朋友。
本文还有配套的精品资源,点击获取