新闻详情

新闻详情

首页 / 资讯中心 / 详情

GD32H759以太网驱动实战:从硬件到RT-Thread调通全记录

发布时间:2026/9/14 3:43:35来源:尧图网络
GD32H759以太网驱动实战:从硬件到RT-Thread调通全记录
GD32H759这颗料拿到手也有一阵了前面把RT-Thread跑起来之后第二件正事就是把这个片上以太网外设enet啃下来。毕竟工控设备嘛联网通信是标配不管是走Modbus TCP还是MQTT上报底层那根网线必须稳稳当当。这篇就顺着我实际调板的顺序把GD32H759的enet驱动从硬件设计、软件框架到裸机调试路上踩过的坑一次性说清楚。先说结论GD32H759的以太网MAC外设整体设计和STM32H7那套非常接近如果你之前玩过H7的FMC和ETH上手会很快。但接近不代表一样寄存器偏移、时钟树、描述符相关的细节差异还是有的直接抄ST的代码肯定翻车。RT-Thread这边有现成的drv_enet.c驱动框架但board级适配、PHY芯片的Reset时序、以及D-Cache的缓存一致性处理仍然需要自己动手调这部分才是真正耗时间的地方。1. 硬件前提与系统框图1.1 先认清GD32H759的MAC外设GD32H759属于兆易创新的高性能系列Cortex-M7内核主频可以跑到600MHz。以太网部分集成了一个完整的10/100M MAC控制器支持MII和RMII两种接口模式自带DMA控制器和收发FIFOMAC地址过滤、VLAN过滤这些基础功能也都有。但没有内置PHY芯片所以板上必须外接一颗PHY比如LAN8720A、YT8512H或者KSZ8081之类。不管用什么PHY对于驱动开发来说第一时间要把整条数据链路在脑子里画出来CPU的MAC通过MDIO管脚读写PHY寄存器通过MII/RMII接口收发以太网帧DMA负责把内存里的数据搬运到MAC的发送FIFO以及把接收FIFO里的数据搬回内存。RT-Thread的上层应用比如lwIP通过netdev接口调到底层驱动驱动再操作MAC和DMA寄存器。这一条链上任何一环不对表现出的现象都是“网口不通”但排查方向完全不同。1.2 RMII接口方案与PHY选型我手上这块板子用的是RMII接口原因是RMII只需要7根信号线TXD0/TXD1、TX_EN、RXD0/RXD1、CRS_DV、REF_CLK比MII的16根线少了一半多布线压力小工控主板上很常见。RMII的工作频率是50MHz数据位宽2bit所以收发一帧数据需要两个时钟周期理解这个时序对后面调问题有帮助。PHY这块我选的是LAN8720A理由很简单便宜、量大、资料全而且是RMII接口和GD32H759是绝配。选择PHY的时候要注意几点PHY地址是硬件决定的一般通过PHYAD0/PHYAD1引脚上下拉设置RMII参考时钟可以由MAC输出也可以由外部晶振或者PHY自己产生必须和硬件设计保持一致PHY的复位引脚有没有接到MCU的GPIO上如果有驱动里要先做复位操作。还有一个容易忽略的点PHY芯片的寄存器地址空间是5位的所以MDIO总线上最多31个PHY。但实际板子上一般就一颗读取PHY ID的时候如果读到全0或者全1先别急着怀疑代码用示波器量一下MDC和MDIO的波形很多时候是引脚复用没配好。1.3 引脚复用与GPIO配置GD32H759的以太网引脚分布在不同的GPIO端口上配置的时候必须用GPIO_AF_x枚举找到正确的复用功能号。以我的板子为例RMII的7根线分别用了PA、PB、PC三个端口引脚和复用号的对应关系如下信号引脚复用功能RMII_REF_CLKPA1AF11RMII_MDCPC1AF11RMII_MDIOPA2AF11RMII_CRS_DVPA7AF11RMII_RXD0PC4AF11RMII_RXD1PC5AF11RMII_TX_ENPB11AF11RMII_TXD0PB12AF11RMII_TXD1PB13AF11注意GD32H759的参考手册里GPIO复用功能表在不同的章节可能标注的AF编号有出入一定要以最新的芯片手册为准。我在调的时候发现SDK头文件里的AF枚举和手册表对不上坑了一下午最后是对着寄存器位定义手动算出来的。如果你是第一次用这颗料建议先把GPIOAF配置打印出来和手册核对一遍再继续。2. RT-Thread enet驱动框架解析2.1 RT-Thread的enet设备模型RT-Thread的以太网驱动是挂在设备框架下的lwIP协议栈通过netdev接口层访问网卡设备。整个层次从下往上大致是底层硬件驱动实现rt_ether_ops结构体里的init、open、close、read、write、control等函数eth_device层RT-Thread自带的以太网设备抽象负责把底层驱动包装成标准设备netdev层通过注册netdev结构体把eth_device接入lwIP的协议栈接口lwIP协议栈负责TCP/IP协议处理socket/应用层用户程序这个设计的最直接好处是换一颗MCU甚至换一套MAC硬件只要底层驱动实现rt_ether_ops上层应用代码一行都不用改。但坏处也在这——框架封装了一层出了问题容易不知道到底该看哪一层。我的经验是从下往上查先确认硬件链路通PHY能读到ID再看驱动有没有成功调用open和link_change回调最后才查lwIP的IP分配问题。2.2 drv_enet.c 中的关键数据结构RT-Thread的GD32系列enet驱动一般放在bsp/gd32h759-sk-eval/drivers/目录下核心文件是drv_enet.c。文件里几个关键结构体要搞清楚struct gd32_eth是驱动的私有数据包含了MAC基地址、PHY地址、DMA描述符指针、缓冲区地址等。struct eth_dma_desc是DMA描述符GD32H759的DMA描述符有两种模式ring模式和chain模式。ring模式就是描述符首尾相连成环状每个描述符指向一个独立的bufferchain模式则是描述符本身链式连接最后一个描述符指向第一个。两种模式都能用但RT-Thread默认的驱动实现用的是ring模式和ST的HAL库套路类似。还有一组和缓冲区相关的宏定义在驱动头文件里可以找到#define ENET_RX_BUF_SIZE 1536 #define ENET_TX_BUF_SIZE 1536 #define ENET_RX_DESC_CNT 4 #define ENET_TX_DESC_CNT 4这里的RX缓冲区大小1536字节是算过的标准以太网帧最大1518字节14字节头1500字节负载4字节CRC加上VLAN标签就变成1522字节再加上一些对齐填充1536够用还留了余量。描述符数量4个意味着DMA在接收队列里可以提前准备4个buffer数据来的时候直接DMA落内存不用CPU实时干预降低丢包概率。2.3 驱动初始化主要流程rt_hw_eth_init()是驱动初始化的入口整体流程可以拆成六步使能MAC外设时钟和GPIO时钟配置引脚复用配置MAC的DMA、MAC地址、速率和双工模式初始化DMA描述符和收发缓冲区配置PHY读取PHY ID、软复位、协商速率注册eth_device到RT-Thread的设备框架使能DMA接收启动link检测线程前面两步多半直接抄官方demo没问题重点在第三步和第四步。比如DMA描述符的初始化一定要确保描述符的地址按32字节对齐缓冲区地址按4字节对齐。如果对齐不正确DMA搬运数据的时候会莫名其妙出错表现是能收到包但数据是乱的。3. 驱动初始化关键流程实战3.1 时钟和引脚初始化在RT-Thread的board初始化代码里需要先使能GPIO和ENET的时钟。GD32H759的时钟树里ENET有两个时钟一个是AHB总线时钟控制寄存器访问一个是APB时钟控制MAC的时序逻辑。两个时钟都要打开缺一个驱动就跑不起来。static void enet_gpio_config(void) { /* 使能GPIO时钟 */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOC); rcu_periph_clock_enable(RCU_ENET); /* PA1: RMII_REF_CLK, PA2: RMII_MDIO, PA7: RMII_CRS_DV */ gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7); /* PB11: RMII_TX_EN, PB12: RMII_TXD0, PB13: RMII_TXD1 */ gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13); gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13); gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13); /* PC1: RMII_MDC, PC4: RMII_RXD0, PC5: RMII_RXD1 */ gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5); }这里一个关键点RMII_REF_CLK这种时钟信号线输出速率等级要高一些我习惯统一配50MHz的GPIO速度。有些参考代码里图省事不配gpio_output_options_set实际也能工作但信号质量在高速率下可能出问题工控环境里电磁干扰大该做的还是得做。3.2 MAC寄存器的配置顺序引脚的坑过了之后就是MAC寄存器层面。GD32H759的enet寄存器和ST的几乎一样ENET_MAC_BASE、ENET_DMA_BASE操作位定义基本对得上。但注意GD32的库函数名和ST不一样比如ST的HAL_ETH_Init()对应GD的enet_init()函数内部封装的寄存器操作顺序是MAC配置 → DMA配置 → 地址过滤。/* 配置MAC */ enet_init(ENET_100M_FULLDUPLEX, ENET_RX_MODE, ENET_TX_MODE, ENET_AUTO_POLARITY_DISABLE); /* 配置MAC地址 */ enet_mac_address_set(ENET_ADDR0, mac_addr); /* 使能DMA接收和发送 */ enet_enable(ENET_RX_DMA_ENABLE | ENET_TX_DMA_ENABLE);我的经验是enet_init之前一定要先确认PHY已经完成上电复位。如果PHY还没准备好MAC根本读不到PHY的链路状态后面协商出来的速率和双工模式全是错的。尤其是带硬件复位引脚的PHY驱动里要先拉低复位引脚至少10ms再拉高再等PHY内部上电稳定这个时间我一般给到150ms以上才继续往下走。3.3 DMA描述符循环队列的初始化DMA描述符是enet驱动里最容易出bug的地方我见过太多人死在这。GD32H759的描述符结构如下struct eth_dma_desc { volatile uint32_t status; /* 控制/状态 */ volatile uint32_t control; /* 控制字段 */ volatile uint32_t buf_addr; /* 缓冲区地址 */ volatile uint32_t buf_next; /* 下一个描述符地址 */ };每个描述符占16字节必须按32字节对齐。我给的宏定义是描述符数组缓冲区数组static struct eth_dma_desc rx_desc_tab[ENET_RX_DESC_CNT] __attribute__((aligned(32))); static struct eth_dma_desc tx_desc_tab[ENET_TX_DESC_CNT] __attribute__((aligned(32))); static uint8_t rx_buf[ENET_RX_DESC_CNT][ENET_RX_BUF_SIZE] __attribute__((aligned(4))); static uint8_t tx_buf[ENET_TX_DESC_CNT][ENET_TX_BUF_SIZE] __attribute__((aligned(4)));初始化的时候要把每个RX描述符的buf_addr指向对应的rx_buf数组并设置接收状态位告诉DMA这个描述符是空闲可用的。然后让最后一个描述符的buf_next指回第一个描述符形成环形队列。for (uint8_t i 0; i ENET_RX_DESC_CNT; i) { rx_desc_tab[i].status ENET_DESC_RX_OWNED_BY_DMA; rx_desc_tab[i].buf_addr (uint32_t)rx_buf[i][0]; rx_desc_tab[i].buf_next (i ENET_RX_DESC_CNT - 1) ? (uint32_t)rx_desc_tab[0] : (uint32_t)rx_desc_tab[i 1]; }这个OWNED_BY_DMA位是关键。DMA只会处理Owned-by-DMA的描述符处理完后会把这个位清掉变成Owned-by-CPU。驱动收完一包数据处理完之后要把这个位重新置位DMA才会继续使用这个描述符。如果驱动代码忘了重新置位收到的包会越来越少最后直接卡死表现就是网口假死ping不通了。3.4 PHY的读取与配置PHY访问通过MDIO总线GD32的库提供了enet_phy_read()和enet_phy_write()接口读取流程是先写PHY地址和寄存器地址到MAC的PHY数据寄存器然后等待操作完成再读数据。时序上是半双工操作MDC时钟由MAC产生一般频率不超过2.5MHzRMII模式下可以更高一些但保守起见就按2.5MHz来设计。PHY驱动第一步永远是读PHY IDLAN8720A的PHY ID是0x0007C0F1寄存器2和3的组合如果读出来不是这个值就要检查接线和地址配置uint32_t phy_id (enet_phy_read(phy_addr, 2) 16) | enet_phy_read(phy_addr, 3); rt_kprintf(PHY ID: 0x%08X\n, phy_id);PHY的复位分硬件复位和软件复位。软件复位是写寄存器0的BIT151然后等待它自动清零。但很多PHY的软件复位之后需要额外等待一段时间才能真正稳定不同芯片不一样。LAN8720A我遇到过软件复位后立刻读寄存器会失败的情况所以驱动里加了轮询等待和延时。enet_phy_write(phy_addr, 0, 0x8000); /* 软件复位 */ for (volatile uint32_t i 0; i 100000; i); while (enet_phy_read(phy_addr, 0) 0x8000); /* 等待复位完成 */网上有说法LAN8720A的软件复位之后要等1ms以上实测下来确实有这个必要不然接下来配置ANEG自动协商可能会失败。3.5 接收与发送的中断处理GD32H759的以太网DMA中断可以触发多种事件接收完成、发送完成、接收错误、链路状态变化等。RT-Thread的驱动会注册一个中断服务函数中断里主要做两件事清中断标志位然后调用rt_sem_release释放信号量通知接收线程来取数据。void enet_isr(void) { uint32_t status ENET_DMA_STAT; if (status ENET_DMA_STAT_RI) { ENET_DMA_STAT ENET_DMA_STAT_RI; /* 清中断标志 */ rt_sem_release(rx_sem); } }这个设计很典型中断里只做最轻量级的操作实际的数据处理放到接收线程里避免长时间占用中断上下文。lwIP的接收线程优先级一般设得比较高确保网络包能及时被处理不然缓冲区满了会丢包。发送路径相对简单调用enet的发送接口时把待发送数据拷贝到tx_buf然后设置描述符的Owned-by-DMA位DMA会自己把数据搬出去。发送完成的中断可以暂时不开或者开了之后只做个计数因为TCP/IP层自带重传机制偶尔丢一个发送完成中断问题不大。4. 调试中遇到的问题与排查实录4.1 问题一MDIO读PHY ID全是0xFFFF这是最常见的开局问题现象是驱动在初始化PHY时读到的ID全是0xFFFF或者0x0000。排查顺序是固定的先量PHY的供电确认电源正常再量复位引脚电平确认不在复位状态然后用示波器抓MDC和MDIO波形确认MDIO时钟有输出、数据线有响应。如果波形正常但读不到数据检查MDIO引脚的上拉电阻——MDIO是双向开漏信号必须有上拉电阻才能正常工作有些核心板为了省元件把这个电阻省了就等着踩坑吧。另外注意MDIO的地址是5位的有些PHY支持地址0到31但默认地址各不相同。LAN8720A的默认地址是0如果你的硬件设计里没做地址选择引脚驱动就必须用0。4.2 问题二Link up了但ping不通PHY状态正常网口灯也亮了但ping不通。这说明物理层已经协商成功问题出在MAC或者上层协议。这时候先别急着怀疑协议栈在驱动里做一个回环测试最有效。GD32H759的MAC支持内部回环模式把回环使能后发送的数据会直接回到接收路径不经过PHY和网线。如果回环模式下能收到自己发出去的包说明MAC和DMA链路没问题问题在PHY配置或者lwIP层。回环测试通过后再用wireshark抓包。有些时候是因为MAC地址配错了或者ARP请求响应了但IP地址冲突。还有一次我遇到的问题是RX描述符的buffer地址没有按4字节对齐导致DMA接收的数据在内存里错位以太网帧头全乱了lwIP直接丢包。这种问题肉眼很难看出来抓包比对一下帧头就能定位。4.3 问题三DMA收发数据错乱偶发丢包这个问题的根源基本都在缓存一致性上。Cortex-M7有D-CacheCPU写的tx_buf如果还停留在cache里DMA去读内存时拿到的是旧数据反过来DMA写进来的rx_buf如果还在cache里CPU读的时候拿到的也是旧数据。解决办法是收发缓冲区和描述符都放到非缓存的memory section里或者每次收发数据前手动执行cache clean/invalidate操作。GD32H759的启动文件里一般已经定义了非缓存段#if defined(__ICCARM__) #pragma location .noncacheable #elif defined(__CC_ARM) __attribute__((section(noncacheable), zero_init)) #endif在RT-Thread的驱动里最简单的做法是把rx_buf和tx_buf都定义在noncacheable段。描述符因为DMA也要访问同样建议放noncacheable段。4.4 问题四开发板用了一段时间后网口假死网口用着用着突然ping不通了重启应用又好了。这种“慢性病”最折磨人通常原因是长时间运行后描述符队列出现了泄漏或者死锁。常见场景是收发缓冲区不够某个时刻接收描述符全部被DMA占用驱动没有及时回收后续的包全部丢弃。排查方法是加一个统计计数器打印每个描述符的状态和当前DMA的读写指针。我在调试时写过一段代码定期打印for (uint8_t i 0; i ENET_RX_DESC_CNT; i) { rt_kprintf(rx desc[%d] status: 0x%08X\n, i, rx_desc_tab[i].status); }如果发现某个描述符一直是Owned-by-DMA状态但DMA又没在处理它说明驱动漏了重新置位操作或者中断丢了。这种问题没有捷径只能逐个检查驱动里控制描述符流转的每个分支。5. 实测性能与优化思路5.1 吞吐量基准测试驱动调通之后我用iperf做了简单打流测试。在100Mbps的网络里TCP单向传输速度大约能跑到94Mbps左右UDP也差不多CPU占用率在50%上下浮动。这个成绩对于工控应用来说已经不错了。RT-Thread的lwIP是线程化处理的中断进来只释放信号量真正拷贝和处理数据都在接收线程完成业务线程几乎不会被阻塞。所以哪怕网络流量再大电机控制、IO扫描这些实时任务也不会被网络拖垮。5.2 几个性能优化方向如果你的场景要求更高的吞吐量或者更低的CPU占用可以从这几个方向入手增大DMA描述符数量。默认4个RX描述符缓冲区满的时候容易丢包改成8个或16个可以提升接收突发能力。开启DMA的RX/TX阈值调整。修改DMA控制寄存器的RTC接收阈值位让DMA在积累一定数据后才触发中断减少中断频率。使用零拷贝发送。lwIP的PBUF可以配置成由驱动直接分配内存发送数据时不需要从lwIP拷贝到tx_buf能省掉一次memcpy的时间。GD32的库函数里有enet_descriptors_chain_init()之类的链式模式接口可以参考。如果M7的D-Cache用起来了尽量把网络相关的缓冲区全部放到noncacheable段从根上规避缓存一致性问题而不是频繁clean/invalidate后者在重负载下性能损耗很明显。5.3 工控场景里的特殊注意事项工业现场用网口跟开发板跑demo完全是两回事。以下几点是我在工控项目中总结出来的环境里大的电磁干扰会导致网线劣化或者丢包率上升板级设计时网口变压器和连接器位置要远离高压功率器件驱动能力弱的PHY还要考虑信号端接电阻的匹配。如果设备需要支持掉线自动恢复光靠PHY的link状态中断不够应用层最好加一个TCP的连接保活机制比如lwIP的TCP_KEEPALIVE选项或者应用层心跳。只依赖底层link检测会出现PHY状态没变但物理链路已经不通的尴尬场景。多网口场景比如双以太网冗余需要给每个网口单独的PHY地址和独立的收发线程RT-Thread也能支持多个enet设备实例但名称要区分好配置起来比单网口复杂不少。写在后面的一点经验GD32H759的enet驱动调下来整体体验比想象中顺畅主要原因是RT-Thread的驱动框架把lwIP对接的部分都封装好了真正需要手写的就是MAC初始化和PHY管理。但反过来框架封装也意味着出了问题必须先弄清楚是哪一层掉了链子。我的调试习惯是先确认硬件量波形、读ID再做回环测试MAC内部回环最后才联调上层lwIP socket。每一层验证通过再往上走能省下大量瞎猜的时间。另外多啰嗦一句开发阶段多用rt_kprintf打印关键状态比如PHY ID、link状态、描述符状态线上运行的时候这些打印一定要去掉不然printf本身就会拖慢中断响应和实时性。最后分享一个调板小技巧第一次上电调试enet时先不要插网线在PHY初始化完成后读一下PHY的基本状态寄存器确认芯片正确识别了网线插入。如果PHY本身就没识别到网线插入后面所有软件层面的调试都是在做无用功。这一条顺手记下来以后调别的板子也能少走弯路。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

电源纹波不是越小越好:从系统需求反推噪声容忍边界 2026/9/14 4:37:39

电源纹波不是越小越好:从系统需求反推噪声容忍边界

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
SpringBoot智能防诈骗平台架构与实现 2026/9/14 4:37:39

SpringBoot智能防诈骗平台架构与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
面向大语言模型的知识协同系统设计与实践 2026/9/14 4:37:39

面向大语言模型的知识协同系统设计与实践

1. 项目概述:这不是一个普通Wiki,而是一套为大语言模型量身定制的知识协同系统 “llm_wiki”这个名称乍看像一个技术名词拼接,但实际落地时,它代表的是一类正在快速演进的新型知识基础设施——不是传统维基百科式的静态文档库&…

阅读更多 →
GDB调试与环境变量管理实战技巧 2026/9/14 4:37:39

GDB调试与环境变量管理实战技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
基于Python的BERT文本相似度检测系统实战解析 2026/9/14 4:37:39

基于Python的BERT文本相似度检测系统实战解析

简介:基于Python与BERT模型的文本相似度检测系统,是一套面向计算机相关专业毕业设计、课程设计及NLP入门者的完整工程。项目采用Python 3.6.8与MySQL 5.7实现,通过BERT双向Transformer预训练模型提取词、句子、篇章的深层语义信息&#xff0c…

阅读更多 →
芸众商城小程序JavaScript开发实战:从请求封装到性能调优 2026/9/14 4:34:39

芸众商城小程序JavaScript开发实战:从请求封装到性能调优

简介:面向芸众商城及微信小程序开发者的原生小程序源码包,基于JavaScript开发,采用微信官方原生框架而非H5封装,具备全开源、可深度二次定制的特点。资源主要解决商城类小程序从页面搭建、商品展示到支付、订单管理等模块的高效实…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞