
GD32H759 RT-Thread 工控实战——第2篇 ENET 驱动移植与调试记录上一篇文章里我把整体项目框架搭起来之后很多朋友都在问“MCU 换到 GD32H759到底和之前用 STM32H7 的工程有多大差别”尤其是底层外设驱动这块最棘手的就是网络部分。我这次直接把 GD32H759 的 ENET 控制器跑起来并且接入 RT-Thread 的 eth 驱动框架整个过程从寄存器配置、底层 ESP 到网络协议栈跑通说实话踩坑不算少但把思路梳理清楚之后你会发现 GD32H759 的 ENET 其实就是一个相对“正统”的 GMAC DMA 结构配置起来不比 STM32 复杂多少。这篇文章我会把 ENET 驱动从零到通的完整链路写出来重点是驱动适配、PHY 初始化、RT-Thread 侧的设备注册、中断与轮询收发的细节以及真机调试中遇到的问题和解决办法。如果你正在做 GD32H759 的网口项目或者打算把 RT-Thread 的 lwIP 跑在一块国产 ARM 芯片上这篇内容值得收藏。1. 为什么要单独写一篇 ENET 驱动很多 MCU 工程师对串口、CAN、SPI 这些外设已经很熟到以太网这里通常会卡住。原因不是寄存器有多复杂而是网络协议栈、DMA 描述符、PHY 管理、中断处理这几层是套在一起的。串口你只要能收发一个字节就“通了”但网口必须满足“物理层协商→MAC 收包→DMA 搬运→协议栈解析→应用层读数据”这条完整链路任何一个环节断了现象都差不多ping 不通或者 up 起来之后丢包严重。GD32H759 的 ENET 控制器硬件上支持 10M/100M/1000M内部带 DMA 引擎、描述符链表管理、MAC 控制、以及 MDIO 接口访问外部 PHY。它和 STM32H7 的 MAC 思路上很像都是基于 Synopsys 的 DesignWare 系列所以如果你有 STM32 的移植经验上手会快但细节上仍然有差异比如寄存器偏移、描述符结构体字段、时钟使能位、PHY 地址选择等。我在这个项目里用的是 RMII 接口外挂了一颗国产 PHY 芯片支持 10/100M 自适应。为什么要选 RMII 而不是 GMIIRMII 只需要 6 根信号线到场布线压力和 GPIO 占用都小很多对 PCB 来说是很现实的优势。而且工控现场一般用到百兆带宽足够TCP 吞吐实测能到 80Mbps 以上就没问题RMII 完全没有性能瓶颈。这一篇的定位不是把官方例程抄一遍而是针对“GD32H759 RT-Thread lwIP”这个组合讲清楚驱动是怎么一步步接进来的。接下来我会按做项目时的推进顺序写先做好准备再动手移植然后调试最后聊工控场景的可靠性优化。2. 动手移植前需要搞清楚的三个问题2.1 GD32H759 的 ENET 不是“开箱即用”的外设先说一个很多人容易误解的地方GD32H759 的 MCU 内部只有 MAC 控制器和 DMA它是没有 PHY 的。PHY 是一颗独立芯片常见的有 IP101G、RTL8211、YT8512 等负责把 MAC 送过来的并行数据变成差分信号发到网线上。MAC 和 PHY 之间在 MCU 内部通常走 MII/RMII 接口在控制面走 MDIO/MDC 管理接口。所以在写驱动之前必须确认四件事MCU 的 ENET 时钟是否已经使能并配好频率尤其是 RMII 需要的 50MHz 参考时钟来源引脚复用是否正确RMII 的信号线有没有全部映射到对应端口PHY 芯片的地址是多少通常由 PHY 芯片的 ADIN 引脚拉高拉低决定默认常见是 0x00、0x01 或 0x04PHY 的 data sheet 拿到手确认状态寄存器和控制寄存器的位定义这一条我特别提示一下GD32H759 启用了 ENET 外设后有些引脚是独立的不像某些系列可以任意映射必须在 GPIO 配置时仔细对照芯片手册的 AF 复用表。我用的是官方评估板上同一组引脚但放到自己画的板子上如果走线换了引脚代码不跟着改很容易出现“PHY 能读到 ID但插网线没反应”的奇怪现象。2.2 RT-Thread 的 eth 驱动框架是怎么组织驱动的RT-Thread 的网卡驱动不是让你直接去调 ETH 库函数而是有一个中间层的框架。简单说RT-Thread 把以太网设备抽象成一个个接口你只需要实现驱动然后把驱动挂到系统里。RT-Thread 这边核心的结构体是struct eth_device它和 RT-Thread 的 device 框架是关联的。你在驱动里要处理的几个关键环节初始化函数配置 GPIO、时钟、MAC、DMA、PHYrt_eth_device_init注册设备到系统发送接口把 lwIP 送来的数据包通过 DMA 描述符发出去接收接口从 DMA 描述符收包交给协议栈中断处理收包中断、链路状态变化中断eth_device_linkchange向协议栈汇报链路状态如果你有内核驱动基础看到这里就明白了RT-Thread 的 eth 驱动本质上是包了一个 netdev然后挂到 lwIP 上。所以你不需要在应用里自己创建 socket 的时候去操作寄存器只要把驱动写对lwIP 的socket、send、recv这些接口就会自动走到底层。从这点看移植工作可以分为两大块一块是 MAC/DMA 的硬件初始化另一块是和 RT-Thread 框架对接的软件适配。后面我都会逐步拆开。2.3 在还不确定硬件时先跑一个“裸机链路测试”很多人的网口第一次调不通其实被 PHY 和 MAC 的问题混在一起互相干扰。强烈建议在接 RT-Thread 和 lwIP 之前先用一个最小测试函数验证 MAC 能不能收发裸包。这里说的不是 TCP/IP而是最原始的以太网帧。我当时写了一个裸包测试函数直接往 DMA 描述符写一包数据帧内容设置成目的 MAC 全FF广播帧源 MAC 用一个自定义地址长度设置为 60 字节然后发送出去。另一边用网口抓包工具看有没有数据包出来或者调试机用 tcpdump 抓包。能发出广播帧说明至少 MAC 和 DMA 链路是通的如果连裸包都发不出来就要回头查时钟和描述符配置不要去折腾协议栈。同理收包也可以做一个环回测试用另一台设备不停发包看看接收描述符有没有被更新有没有进接收中断。这两步虽然看起来挺土但能把问题范围迅速缩小一大半。我每次调试新开发板的网口都必做这个步骤能省下大量排错时间。3. ENET 驱动初始化的完整过程拆解3.1 时钟和引脚的底层配置GD32H759 的 ENET 控制器需要两个主要的时钟域。一个是 AHB 总线时钟给 DMA 和寄存器访问用另一个是 MAC 的时钟取决于你用的接口模式。RMII 模式要求 PHY 提供或者外部提供 50MHz 参考时钟这个时钟既给 PHY 用也作为同步基准。我在板子上用的方案是外部 50MHz 有源晶振直接接到 PHY再由 PHY 内部回给 MCU。如果你的板子是从系统时钟分频出来的一定要确保分频后精确等于 50MHz偏差太大直接导致 RMII 收发时序乱掉。初始化 GPIO 时注意 RMII 所需引脚的方向是有区别的。TXD0、TXD1、TX_EN 是输出RXD0、RXD1、CRS_DV、CLK 是输入。我当时把GPIO_MODE_AF_PP设置成了输出有一部分输入引脚也放成了复用推挽结果是链路能协商起来但数据收不到后来对照手册逐一定义把方向改对之后马上正常了。不要嫌这一步繁琐以太网最怕的就是“看起来配置了实际没到位”。3.2 MAC 地址、滤波和帧长度配置MAC 地址是网口最基本的身份信息。GD32H759 的 MAC 地址寄存器组由ENET_MAC_SA0R等构成。写入 MAC 地址时要注意高 16 位和低 32 位的排列顺序很容易搞反。如果你用 MAC 地址00:11:22:33:44:55那么寄存器中保存的方式和你在数组里的字节顺序不是直接对应的。最简单的处理方法是直接用库函数封装好的接口写入整个指针避免手动逐个拼字节。还有一个经常被忽略的点MAC 过滤模式。默认情况下MAC 控制器会过滤掉不是发给自己的单播帧只接受广播帧和地址匹配的帧。调试早期建议把“接收所有帧”的 promiscuous 模式打开不然你发测试包的时候只要目标 MAC 写错一位主板那边的接收过滤就直接把包丢了你还会误以为是 DMA 没工作。MAC 寄存器里还有帧长度限制。标准以太网帧长是 1518 字节如果你的应用里会有 VLAN 标记或者超大帧记得把“巨型帧使能”位打开。不过以工控场景的普遍需求来说默认的 1518 就够用我一般不会为了性能去开 Jumbo Frame因为那样会在交换机侧引入额外的兼容性问题。3.3 DMA 描述符链表的设计与初始化GD32H759 的以太网 DMA 使用描述符来搬运数据。每个描述符对应一块缓冲区描述符里存了缓冲区地址、数据长度、状态信息等。它可以组成一个链表DMA 顺着链表一个一个处理。初次接触的人可能会觉得链表很麻烦其实它的核心作用就是让 DMA 可以“批量且不丢数据”地搬运。我的做法是固定分配两组数组一组给接收一组给发送。接收描述符数量我设置了 8 个发送描述符数量设置了 4 个。为什么接收要比发送多因为网络数据是异步进来的处理速度慢的时候要有足够缓冲顶着如果接收描述符太少一两次中断处理不及时就会丢包。工厂环境下偶尔一个广播风暴就能让这个差别体现得非常明显。初始化描述符的时候有几处容易冲动的地方描述符的地址必须按 4 字节对齐有的平台要求 8 字节对齐接收描述符必须把缓冲区地址提前赋好并把“所有权”标志位Own Bit设置为 DMA 拥有发送描述符不需要预分配缓冲区但要在发送前把长度和地址写进去描述符链表要首尾相接最后一个描述符的 Next Descriptor 指回第一个我当时在初始化时犯过一个低级错误分配了一块缓冲区数组但忘了把ENET_DMA_RXDESC_BUFFER0地址对齐到 4 字节边界结果是接收 DMA 搬运数据时产生对齐错误中断标志一直挂在那里驱动怎么都不进收包流程。后来检测出来后把缓冲区做了一次对齐问题彻底消失。3.4 PHY 初始化不是“读一下 ID”就完事PHY 的初始化在网口驱动里占有很重的位置。很多朋友以为初始化 PHY 就是读一下 PHY ID 寄存器确认能读到值就行。实际上在 RT-Thread 的移植中PHY 初始化更像一个状态机上电后等待自协商完成读取链路状态、速度、双工模式然后把这些参数配置到 MAC 的相应寄存器里。PHY 的控制接口是 MDIO。GD32H759 的驱动可以直接通过 MDIO 读写 PHY 寄存器。初始化的标准流程是复位 PHY等待复位完成读取 PHY ID确认 PHY 型号配置自动协商让 PHY 自己选择速度和双工等待自协商完成ANEG_COMPLETE位置位读取速度状态更新 MAC 的ENET_MAC_CFG_REG中的速度和双工位注意延迟这个事上千万不能省。PHY 的复位和自协商都需要时间我在项目中设置的等待时间最长到 3 秒。如果代码里复位完立刻去读状态很容易读到“自协商还没开始”的值。PHY 的中断/状态轮询方式也需要考虑。RT-Thread 的 ETH 驱动中链路状态是通过PHY 事件来通知的。简单做法是开一个定时器线程每隔 500ms 去读 PHY 的状态寄存器检测到链路从 up 变 down或者从 down 变 up就调用eth_device_linkchange接口通告协议栈。我实际测试后发现轮询间隔 500ms 太长插拔网线时应用层要一两秒才能反应过来改成 200ms 之后既不会占用太多 CPU又能保持链路状态通告的及时性。4. 对接 RT-Thread注册设备、中断与收发包处理4.1 驱动注册和设备初始化的顺序用 RT-Thread 的驱动模型时要保证驱动初始化代码的执行时机正确。我这里的做法是使用INIT_DEVICE_EXPORT宏把rt_hw_enet_device_init导出到设备初始化阶段。简单说RT-Thread 在启动过程中会按顺序调用各段初始化函数INIT_DEVICE_EXPORT对应的设备初始化段是在内核调度器启动之后、lwIP 启动之前执行这个时序恰好满足依赖关系。设备初始化的整体流程是调用 PHY 初始化函数等待自协商完成配置 MAC 地址、MAC 控制寄存器初始化 DMA 描述符链表使能 ENET 中断调用eth_device_init把设备注册到 RT-Thread 的网卡框架里启动 MDIO 轮询监控链路状态这里要注意一点虽然 PHY 的自协商需要几百毫秒但rt_hw_enet_device_init不能真的阻塞在自协商完成上否则整个系统启动过程会卡住。我的做法是初始化结束前 PHY 处于已复位状态注册好设备后就返回后续由链路状态轮询函数去更新。这样系统能快速进到 shell网络是否就绪由协议栈自己处理。4.2 发送路径从 lwIP 到 DMA 描述符在 RT-Thread 驱动框架中底层发送函数是使用eth_device结构体里的eth_device_ops提供的回调函数来注册发送函数。当 lwIP 需要发送数据包时会调用这个函数参数是缓冲区指针和长度。发送处理的几个关键步骤找一个空闲的发送描述符。如果占用中的描述符太多说明上一次发送还没完成这时通常是直接返回错误让上层稍后再试。把要发送的数据拷贝到发送缓冲区。有些设计想省一次拷贝直接把 lwIP 的缓冲区地址交给 DMA但这是在 lwIP 已经做好缓存对齐的前提下才能这样做。我为了稳妥一律拷贝多花几十微秒的问题对整体性能影响不大。设置描述符中的数据长度和数据缓冲区地址。清除 Own Bit将描述符交给 DMA 发送。如果有多个待发送描述符要按顺序链好。很多 RT-Thread 移植例程里的发送函数还包含RT_ASSERT检查和错误统计。自己在调驱动时可以把这些错误计数通过串口打印出来比如发送超时、描述符忙、总线错误等对于定位问题非常有帮助。4.3 接收路径中断触发与数据上送接收路径的设计对吞吐量影响最大。GD32H759 的接收完成会触发ENET_DMA_INT_EN中的接收中断。在中断服务函数里我们遍历接收描述符拿到一个收完的数据包后就需要把它交给 lwIP。RT-Thread 提供的回调函数通常是eth_device_ready它的作用就是告诉 lwIP“设备上有数据可以读了”然后 lwIP 会调用驱动里的接收函数去取数据。中断处理要注意一个关键问题中断上下文的时间不能太长。如果你在中断里把数据包拷贝出来再上送时间短还行一旦收包量大了中断频繁触发会导致整个系统卡顿。我的经验是采用“先关接收中断把检查到的数据包全部放入一个队列退出中断后再由专门的接收线程处理”这种模式。不过 GD32H759 的 DMA 在接收中断产生之后如果描述符处理不过来物理层可能继续接收新的包进缓冲区。所以为了避免丢包最简单可靠的办法是能进中断就尽量快速把所有已完成的接收描述符都取出来哪怕暂时不处理也要把描述符“归还”给 DMA保证 DMA 一直有可用缓冲。我的具体做法在中断函数里遍历当前接收描述符如果 Own Bit 为 0说明数据已经由 DMA 写到内存记录缓冲区地址和长度马上重新设置 Own Bit 为 1将描述符归还给 DMA把数据包加入一个队列并触发接收处理线程接收处理线程调用eth_device_ready并发送给 lwIP这样 DMA 的“接收产能”就被持续释放即便瞬时流量很大也不会因为软件处理慢而把 DMA 缓冲区占满。4.4 中断服务程序的整体编写ESD 处理这块我习惯把整个 ENET 中断服务函数写在gd32_eth_isr里然后在rt_interrupt_enter和rt_interrupt_leave包起来保证 RT-Thread 能正确跟踪中断嵌套。这里有个容易遗漏的细节GD32H759 的 ENET DMA 中断在读取后会清除标志位但部分中断源需要你在发接收线程时额外读一次状态寄存器来“锁存”因为中断标志可能在软件处理过程中再次被置位。我整理了一个通用 ISR 框架读取 DMA 中断状态寄存器如果是发送中断已知一个发送描述符完成置发送完成标志如果是接收中断解析接收描述符并调用接收处理函数如果是总线错误中断把错误状态记录到日志写中断清除寄存器清对应的中断位退出中断时调用rt_interrupt_leave触发信号量唤醒接收处理线程这份代码写好后我专门做过一次 24 小时 7*24 不间断小包收发测试没有出现死机或者内存溢出。稳定的前提其实就是中断处理写干净、描述符归还及时。5. 真机调试从 ping 不通到稳定收发5.1 第一次上电先看“三层状态”代码写好编译烧录之后不要急着开 lwIP 的 DHCP先用 RT-Thread 命令行一点点验证。我通常分三步看状态第一步使用 MDIO 工具去读 PHY 寄存器确认链路是 up 的。在 RT-Thread 里如果没有现成的 mdio 命令可以在 shell 里临时加一个调试函数读 PHY 的PHY_BMSR寄存器看看LINK_STATUS位是不是 1。第二步设置一个静态 IP然后启动 lwIP用ifconfig或者netstat确认网络接口已经 up 并且配置了 IP。第三步用ping 192.168.x.x去 ping 对端。如果 ping 不通不要上来就抓包看图先看ifconfig里有没有统计信息比如发送/接收计数、错误计数。RT-Thread 的 eth 驱动里一般都会把底层收发错误累计到设备统计中从 shell 就能查到。5.2 第一次 ping 不通的常见原因排查我把自己调试中可能遇到的现象和排查点整理出来大家可以直接对着查现象排查方向PHY 寄存器都读不到 IDMDIO 引脚配置错误、PHY 地址不对、PHY 电源没供电PHY 状态 up但 ping 不通MAC 地址没写对、MAC 和 PHY 双工/速度不匹配、描述符没初始化ping 第一次通后面丢包接收描述符归还不及时、缓冲区太小、中断处理时间太长只有本机回环能通对端不通ARP 缓存问题、网络里的 VLAN/物理连接问题发送计数在涨接收计数为 0接收描述符所有权错误、接收中断没使能、DMA 没在接收状态尤其注意“PHY 状态 up 但 ping 不通”这属于最迷惑人的问题之一。因为物理层链路是通的说明网线和 PHY 没问题但你的 MAC 配置里可能犯了个隐蔽的错误速度是 100M双工模式却配置成了半双工。RMII 接口本身是全双工的信号出现这种错误会导致极低的吞吐甚至完全不通但又不影响物理链路状态。我第一次调试时MAC 和 PHY 的双工配置就发生过不一致。PHY 自动协商结果是全双工但 MAC 寄存器里写死的是半双工结果发了 IP 包始终得不到 ARP 应答。查了很久才意识到是 PHY 自协商完成后软件没有把 PHY 返回的双工状态同步到 MAC 寄存器。后来加上了一个更新步骤问题立刻消失。5.3 用抓包工具验证链路层当 ping 不通的时候最好的帮手是抓包工具。我这里说的不是专业网络分析仪就是你笔记本电脑上 Wireshark 或者 tcpdump 就行了。开发板接交换机电脑也接到同一个交换机在电脑上抓包。假如开发板 ping 电脑抓到的包里有开发板发出的 ARP 请求但没有电脑的 ARP 回复说明问题出在电脑到交换机这一段或者开发板的 ARP 请求格式有问题。假如连 ARP 请求都看不到说明开发板发送链路根本不工作重点排查 MAC 地址配置和发送描述符。如果你没有抓包工具也可以做一个更“土”的办法在驱动里把发送的每一帧做 dump打印前 14 个字节。你就能看到目的 MAC、源 MAC、EtherType。如果打印出来的目的地址是 FF:FF:FF:FF:FF:FF 而长度也对就说明发送链路基本没问题。5.4 跑通后做一轮稳定性和性能测试ping 通了并不代表驱动能交付。真正在工控项目里网口会承担 Modbus TCP、S7 通信或者私有协议数据传输长时间运行不掉线、不丢包才是底线。我建议跑通基础功能后做这么几个测试延迟测试连续 ping 1000 个包看最大/最小/平均延迟重点看有没有超时丢包吞吐测试用 iperf 或手动 TCP 传输一个大文件统计发送/接收速率拔插稳定性测试反复拔插网线 100 次确认链路状态切换不会导致系统崩溃长时间测试跑到 7 天以上监控内存和描述符占用是否有缓慢增长这里面我最有感触的是内存泄漏问题。lwIP 和驱动都会动态分配内存如果驱动里某个地方该释放的缓冲没释放短时间看不出问题连续跑几天后系统内存会慢慢耗尽。所以我做长时间测试时除了看网络状态还会周期性打印list_memheap或free的内存信息。如果发现可用内存单调下降基本就是驱动里的资源管理有 bug。6. 工控场景下的几个可靠性优化细节6.1 降低 PHY 复位对系统启动带来的冲击PHY 复位是硬件行为复位过程中 PHY 芯片会消耗额外的电流某些劣质电源方案会在复位瞬间产生压降进而导致 MCU 复位。解决思路有两个一是把 PHY 的复位脚接到一个可控 GPIO在系统启动完成后再延时复位二是如果板上直接把 PHY 复位脚接到了 MCU 的复位电路最好加一个阻容延时电路。我自己的板子用的是 GPIO 控制 PHY 复位这样软件的灵活性更大。启动时先把复位脚拉低 10ms再拉高等上 100ms 再开始 MDIO 访问给 PHY 留足稳定时间。经测试这个延时设置在工业现场的电源波动条件下能稳定工作。6.2 打开接收描述符的“溢出保护”工控现场经常出现“网络广播风暴”也就是局域网上大量广播帧瞬间冲击网卡。如果驱动的接收描述符数量不够或者软件处理速度跟不上丢包几乎是必然的。除了把接收描述符数量设大之外DMA 本身有一个溢出机制当接收描述符全部被占用时可以配置 DMA 将新收到的帧丢弃但保留 DMA 接收中断触发这样软件虽然没有新帧可取但至少不会因为描述符耗尽而引发总线错误。我建议在 GD32H759 的 DMA 配置里开启接收 FIFO 溢出保护功能并预留一个“把接收 DMA 复位”的软件路径。一旦检测到长时间没有空闲描述符就把 DMA 接收状态机复位一次。这招在极端流量下非常有用能避免 DMA 状态机卡死。6.3 双缓冲与 cache 一致性问题GD32H759 是带 Cache 的高性能 MCUETH 缓冲区如果使用普通 SDRAM 或内部 SRAM一定要考虑 Cache 一致性问题。DMA 是外部设备访问内存它不会管 CPU 的 Cache 是否还残留数据。接收时如果 CPU 的 Cache 里有旧数据读到的可能不是 DMA 刚写入的新数据发送时如果数据还停在 CPU 的 Cache 中DMA 可能读到的是老数据。解决这个问题有几种方案使用不需要 Cache 的内存区域比如某些 SRAM 是 AXI SRAM 且没有映射到 Cache 保护区域可以通过 MPU 配置为 non-cacheable在 DMA 传输前调用 Cache 清理/失效操作在开启 MPU 后把所有 ETH 描述符和缓冲区所在的地址段映射成非缓存、非缓冲属性我最初直接用 MPU 把以太网 DMA 描述符和缓冲区所在区域设为 non-cacheable。这个方法简单可靠缺点是访问速度比 cacheable 区域稍慢但对于百兆网口来说这个性能损失可以忽略。在网口驱动调试中Cache 一致性导致的问题往往是最隐蔽的数据看起来有时对有时错丢包也不定时。如果遇到这种“玄学”问题第一时间怀疑 Cache 和 DMA 的交互一测一个准。6.4 让链路状态变化及时通知应用层工控项目里如果网线意外断开应用层往往需要立刻感知进行告警或者切换冗余链路。RT-Thread 里可以用 netdev 的回调机制也可以简单地在一个工作线程里轮询 PHY 状态。我选择的是在 PHY 状态轮询函数里每次检测到链路状态变化后先打印一条日志然后调用netdev_low_level_set_link_status更新网络接口的链路状态标志再回调应用层注册的事件处理函数。这样做之后应用层只需要注册链路变化回调即可任何网线故障都能在 200ms 内被感知到。7. 这一篇的最终成果和下一章展望目前这个项目里 ENET 驱动已经稳定运行了一段时间验证过的功能包括静态 IP 和 DHCP 获取、ARP/TCP/UDP 通信、Modbus TCP 从站响应、最多 4 路并发 TCP 连接长时间运行内存占用保持平稳CPU 负载在中等流量下只占 15% 左右。如果让我总结这次移植的经验一句话就是GD32H759 的 ENET 其实不难难的是把 MAC、DMA、PHY、描述符、中断这几层的关系吃透并且每一步都用可控的验证方式去确认。先裸包测试再 PHY 状态确认然后 lwIP 跑通最后做稳定性优化这条路是最稳的。下一篇文章我准备把重点放在“基于这套 ENET 驱动做 Modbus TCP 从站和应用层数据分发”上聊聊工控中经常碰到的多连接管理、超时处理、以及怎么保证协议栈和业务代码之间互不阻塞。如果你在移植 GD32H759 ENET 驱动的过程中也遇到什么奇葩问题欢迎留言咱们一起把国产 MCU 的坑填平。