ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

F28388D以太网实战:LWIP协议栈移植与EMAC调试全记录

F28388D以太网实战:LWIP协议栈移植与EMAC调试全记录 最近在调手头这片YXDSP-F28388D开发板的以太网功能从拿到板子时的EMAC寄存器一脸懵到最后LWIP协议栈跑起来、能稳定ping通、能挂TCP服务器整个过程踩了不少坑。今天把这段完整记录整理成文给同样在C2000平台上做EtherNET通信的朋友一个参考。先说结论F28388D这颗芯片做以太网通信是完全够用的片上自带10/100M EMAC配合DP83822这颗PHY芯片再移植一套LWIP协议栈就能做出一个稳定可靠的网络节点。整个过程的核心工作量不在硬件也不在协议栈本身而在“让协议栈适配你的硬件平台”这一层的胶水代码上。下面我从硬件设计开始一步步拆解整个过程。1. 项目概述与方案选型为什么是F28388DLWIP1.1 F28388D的EMAC模块到底是什么F28388D是TI C2000家族里的旗舰级芯片双核架构一颗C28x DSP主核加一颗ARM Cortex-M4辅助核主频分别是200MHz和200MHzCM4实际跑在125MHz或者更高要看配置。很多人一听C2000就默认它是纯电机控制芯片其实F28388D已经把网络接口也做进来了片内集成了一路完整的10/100Mbps Ethernet MAC支持MII和RMII两种PHY接口模式。这意味着你不用外扩SPI转以太网模块也不用拿SPI口去挂ENC28J60直接用芯片原生的MAC外设就能做网络通信。YXDSP-F28388D这块开发板就是围绕F28388D做的最小系统板板上集成了一颗TI的DP83822 PHY芯片带RJ45网口座引出了必要的LED指示灯。板子的整体思路很清晰把F28388D的EMAC引脚通过RMII接口接到DP83822然后由PHY芯片驱动网口变压器和RJ45座最终引出一个标准百兆以太网口。这里有个容易混淆的点很多人以为“以太网通信”就是LWIP的事其实LWIP只是第3层以上的协议栈真正干苦力活的是EMAC这个硬件外设。EMAC负责收发以太网帧处理CRC校验、帧对齐、FIFO缓冲以及和DMA控制器的配合。LWIP负责IP/TCP/UDP这些协议层的组包拆包。你网络不通很可能是EMAC这层就没配置对协议栈倒是背了不少黑锅。1.2 双核架构下的协议栈跑位和分工F28388D的双核架构在跑以太网时有个非常有意思的分配方式。EMAC模块挂在CM4内核的外设总线上本质上EMAC的寄存器只能由CM4核来操作。C28x核是碰不到EMAC寄存器的。这意味着LWIP协议栈必须跑在CM4核上而C28x核做实时控制、采样、电机算法这些事两边通过IPCInter-Processor Communication机制交换数据。这其实是个很优雅的设计。实时控制任务对时间确定性要求极高如果让C28x核去处理TCP的重传超时、ARP老化这些事务控制周期会被严重干扰。而CM4核跑这些非实时任务刚刚好ARM核处理网络协议栈的串行逻辑很在行。两个核各干各的中间用共享内存或IPC邮箱传数据既不互相拖累又能实现“控制联网”的一体化方案。1.3 整体方案选型的考量LWIPLightweight IP选择在F28388D上其实就是因为它是个轻量级、可裁剪、资源占用可控的TCP/IP协议栈特别适合嵌入式MCU这种资源受限环境。它和TI自带的NDKNetwork Developer Kit比起来LWIP更开放、社区资料更多而且源码级可控。NDK虽然集成度高但说实话用起来文档少、配置繁琐而且TI对NDK的支持重心早已不在C2000上。LWIP则不同从STM32到C2000大家都在移植踩坑资料一抓一大把。我最终的方案是CM4内核跑LWIP应用逻辑C28x内核跑实时控制两边通过共享内存映射IPC中断通信。这套架构在调试时有个好处网络协议栈出了问题你只需要单步调试CM4侧的程序不会影响C28x侧的实时控制代码。2. 拿到开发板先过一遍硬件EMAC接口与PHY选型2.1 YXDSP-F28388D开发板的以太网电路要点这块板子的以太网电路我拿到手后对照原理图核了一遍基本就是TI参考设计的路子F28388D的EMAC通过RMII接口接DP83822DP83822的XI/XO引脚接25MHz晶振内部通过PLL倍频出50MHz RMII参考时钟RMII的REF_CLK由PHY芯片输出给MAC侧不是MAC输出给PHY板上的MDIO/MDC引脚接上下拉电阻配置PHY地址网口变压器集成在RJ45座内部带中心抽头匹配电路每组电源都做了滤波磁珠和去耦电容处理关键的地方在于RMII的REF_CLK时钟方向。RMII接口需要50MHz的参考时钟这个时钟可以由MAC提供也可以由PHY提供。YXDSP-F28388D板上的设计是由DP83822输出REF_CLK给F28388D的EMAC模块也就是说PHY是时钟主设备。这个细节在硬件上已经固定死了你改不了但软件里配置时需要对应好别在代码里把EMAC配成自己输出时钟那样两边时钟不同步网络肯定起不来。DP83822的PHY地址由PHYAD引脚的电平决定板上一般会通过上下拉电阻配置成某个固定地址常见的是0x0或者0x01。我板子上的是0x01。你在写驱动时MDIO读到的PHY地址一定要和实际硬件对上不然读出来的寄存器全是0xFFFF。2.2 RMII vs MII我为什么最终用RMIIF28388D的EMAC同时支持MII和RMII。MII需要16根数据线TXD[3:0]、RXD[3:0]加时钟和控制线RMII只需要7根TXD[1:0]、RXD[1:0]加时钟和控制线。RMII的数据线少一半但代价是时钟频率从25MHz翻倍到50MHz。对于YXDSP-F28388D这种开发板RMII是板上已经固定的接法因为引脚省、PCB好布线。但如果是自己做硬件我建议优先考虑RMII原因很现实引脚占用少F28388D的引脚资源很紧张省出来的引脚可以留给其他外设开发板参考设计就是RMII你照着抄不会错100Mbps速率下RMII完全够用吞吐量天花板不是接口宽度决定的MII的25MHz时钟在PCB布线时对等长要求更严苛RMII的50MHz只有一组时钟反而好处理当然选择RMII也要注意一个问题50MHz时钟的EMI会比25MHz大一些但这对开发板来说不是事做产品时在时钟脚加RC滤波或磁珠就能压住。2.3 上电第一步先手动复位PHY很多人上电就直接跑程序发现PHY的寄存器读不出来就开始怀疑焊接问题。其实很多时候是PHY的复位时序没处理好。DP83822的复位有两种方式硬件复位引脚和软件复位寄存器。YXDSP-F28388D板上PHY的硬件复位脚如果有引出一般会接到DSP的GPIO上或者直接和DSP的复位信号绑定。如果PHY复位脚和DSP的复位脚绑在一起那上电后DSP和PHY是同时开始复位的。但DSP从Flash启动到程序跑到main函数需要一段时间这段时间PHY早就复位完成且开始正常工作。理论上没问题。但保险起见我习惯在初始化PHY之前先通过GPIO把PHY的复位脚拉低一段时间至少10ms再释放拉高然后延时100ms以上等PHY内部PLL锁定再去读PHY寄存器。这一步能解决90%的“PHY读不到”问题。// 假设PHY_RST接在GPIO43上 GPIO_WritePin(GPIO43, 0); // 拉低复位 DELAY_US(20000); // 保持20ms复位低电平 GPIO_WritePin(GPIO43, 1); // 释放复位 DELAY_US(150000); // 等待150ms等PHY PLL稳定注意这个延时一定要给够。DP83822的PLL锁定时间在数据手册上写着典型值几十毫秒但低温或者电源纹波大的情况下有可能更久。我实际测试时延时从100ms加到150ms后再也没有出现读PHY ID失败的情况。3. LWIP协议栈的移植与关键代码实现3.1 移植前的准备工作LWIP版本我选的是2.1.2这个版本稳定、资料多而且对中小型嵌入式设备的内存开销控制得很好。移植时我直接去GitHub拉取了LWIP官方仓库的STABLE-2.1.2分支源码然后自己写平台的sys_arch层。在动手写代码之前我把LWIP源码里的lwipopts.h、cc.h、arch.h、sys_arch.h这四个文件单独抽出来先做了一轮裁剪。lwipopts.h是LWIP的配置文件里面定义了协议栈要用什么协议、开多少PCB、内存怎么分配cc.h和arch.h是平台相关的类型定义和字节序定义sys_arch.h是操作系统抽象层如果用裸机不带RTOS就用LWIP的NO_SYS模式。我这次项目用的是裸机定时器中断轮询的方式没有上RTOS所以配置了NO_SYS1。这样LWIP内部不依赖信号量、邮箱这些OS原语所有的协议栈处理都用sys_check_timeouts()周期性驱动。好处是代码简单、没有多线程竞态问题坏处是如果你在中断里调用LWIP API要特别小心全局临界区保护。3.2 ethernetif.c的核心low_level_init、low_level_input、low_level_outputLWIP的移植核心是三个函数low_level_init、low_level_input、low_level_output。这三兄弟是LWIP和数据链路层的桥梁。F28388D的EMAC和LWIP的数据交互全靠这三个函数里对DMA描述符、数据缓冲区的操作。low_level_init初始化EMAC外设和PHY配置MAC地址、设置接收过滤、启动DMA收发。这个函数里最重要的操作是把描述符环Descriptor Ring里的每个缓存地址提前注册给EMAC这样EMAC收到数据后能自动通过DMA把数据搬到内存指定位置。static err_t low_level_init(struct netif *netif) { // 1. 初始化PHY读PHY ID 确认链路 phy_basic_init(); // 2. 设置MAC地址 netif-hwaddr[0] 0x00; netif-hwaddr[1] 0x1A; netif-hwaddr[2] 0x7D; netif-hwaddr[3] 0xDA; netif-hwaddr[4] 0x71; netif-hwaddr[5] 0x32; // 3. 配置EMAC接收描述符缓冲 for (uint32_t i 0; i ETH_RX_DESC_CNT; i) { pkt_rx_buf[i] (uint8_t *)MEM_ALIGN(rx_buffer_mem[i], 4); emac_rx_desc[i].buff_ptr (uint32_t)pkt_rx_buf[i]; emac_rx_desc[i].next_desc (uint32_t)emac_rx_desc[(i 1) % ETH_RX_DESC_CNT]; } // 4. 使能EMAC中断启动接收 emac_StartRx(); return ERR_OK; }low_level_input当EMAC收到数据并写到接收缓冲区后LWIP会调用这个函数把数据从EMAC的缓冲区拷贝到pbuf里然后交给协议栈处理。这里有个性能关键点能零拷贝就零拷贝不能零拷贝就尽量用pbuf_alloc让协议栈直接使用EMAC缓冲区。我实测过拷贝和零拷贝两种方式的性能差异在100Mbps满速时memcpy的方式会吞掉将近30%的吞吐量。所以我最终用的是pbuf_custom结构让pbuf直接指向EMAC的接收缓冲区等协议栈处理完数据后在自定义free回调里把缓冲区还给DMA描述符环。static struct pbuf *low_level_input(struct netif *netif) { struct pbuf *p NULL; uint32_t len 0; uint32_t desc_idx current_rx_desc; // 检查当前描述符是否有数据 if (!(emac_rx_desc[desc_idx].flags EMAC_RX_DESC_EMPTY)) { len emac_rx_desc[desc_idx].data_len; // 让pbuf直接引用接收缓冲区不拷贝 p pbuf_alloced_custom(PBUF_RAW, len, PBUF_RAM, rx_custom_pbuf[desc_idx], pkt_rx_buf[desc_idx], len); if (p NULL) { // pbuf分配失败也要把描述符归还给DMA emac_rx_desc[desc_idx].flags | EMAC_RX_DESC_EMPTY; } else { // 自定义free回调会重新设置描述符 } current_rx_desc (desc_idx 1) % ETH_RX_DESC_CNT; } return p; }low_level_output发送函数把pbuf里的数据拆分成以太网帧写入DMA发送描述符。LWIP协议栈发数据时可能一个TCP包会被拆成多个pbuf段中间还要插入以太网头和IP头。所以low_level_output里要处理链式pbuf把所有段的数据串成一个DMA描述符上的多个缓冲区地址。static err_t low_level_output(struct netif *netif, struct pbuf *p) { uint32_t desc_idx current_tx_desc; uint32_t total_len 0; // 把链式pbuf的每一段地址都填到发送描述符的buffer chain里 for (struct pbuf *q p; q ! NULL; q q-next) { tx_buffers[desc_idx][buf_cnt].addr (uint32_t)q-payload; tx_buffers[desc_idx][buf_cnt].length q-len; total_len q-len; buf_cnt; } emac_tx_desc[desc_idx].data_len total_len; emac_tx_desc[desc_idx].flags | EMAC_TX_DESC_READY; current_tx_desc (desc_idx 1) % ETH_TX_DESC_CNT; return ERR_OK; }3.3 lwipopts.h中的内存与协议配置lwipopts.h是整个移植中最容易踩坑的地方直接决定协议栈的“体质”。我根据F28388D的RAM资源做了如下裁剪// 内存分配方式内存池内存堆结合 #define MEM_LIBC_MALLOC 0 #define MEM_ALIGNMENT 4 #define MEM_SIZE 128 * 1024 // 堆内存池 #define MEMP_NUM_PBUF 40 #define MEMP_NUM_TCP_PCB 16 #define MEMP_NUM_TCP_SEG 128 #define PBUF_POOL_SIZE 64 #define PBUF_POOL_BUFSIZE 1600 // 协议裁剪只保留IP、ICMP、UDP、TCP #define LWIP_ICMP 1 #define LWIP_UDP 1 #define LWIP_TCP 1 #define LWIP_DHCP 1 #define LWIP_DNS 0 #define LWIP_SNMP 0 #define LWIP_NETCONN 0 #define NO_SYS 1这些参数不是拍脑袋定的我是根据实际并发量需求一步步调出来的。MEMP_NUM_TCP_SEG小了会导致TCP接收窗口打不开传输速度上不去PBUF_POOL_SIZE太小会导致高负载下丢包MEM_SIZE太大又浪费RAM。F28388D的RAM有两大块LSx RAM和GSx RAMCM4侧可用RAM是可以通过MemCfg配置调整的。我建议CM4侧至少留256KB以上给网络协议栈和应用缓冲否则做TCP大数据传输时会捉襟见肘。LWIP的内存机制很简单小消息用内存池MEMP固定大小、分配快、无碎片大块数据用内存堆MEM动态分配、可变大小、有碎片风险。我这套配置实测能稳定跑满百兆带宽的TCP收发。3.4 中断接收周期调用裸机下如何驱动协议栈NO_SYS模式下的LWIP协议栈本身没有后台线程所有处理都必须由你的主循环或中断显式调用。我的做法是中断轮询结合EMAC接收中断负责把数据从DMA搬进pbuf调用tcpip_input实际上NO_SYS模式下内部会直接调用ethernet_input提交给协议栈主循环里周期性调用sys_check_timeouts()驱动ARP、DHCP、TCP重传这些定时事件每100ms检测一次PHY链路状态链路变了要通知LWIP// 主循环 while (1) { sys_check_timeouts(); // 处理协议栈定时事件 // 周期性检测PHY链路状态 if (check_link_status() LINK_CHANGED) { if (link_up) netif_set_link_up(g_netif); else netif_set_link_down(g_netif); } app_periodic_task(); // 用户应用任务 }有个细节NO_SYS模式下tcpip_input和直接调用ethernet_input效果一样但职责不能搞混。接收中断这个上下文环境里你不能调用任何可能导致阻塞的LWIP API只能投递数据。如果是RTOS环境通常用邮箱或队列做线程间通信裸机环境就简单得多协议栈在主循环上下文里继续处理刚投递进来的包。4. 集成到CM4还是C28x双核数据通路设计4.1 CM4与C28x的数据交换机制F28388D双核之间数据交换最常用的三种方式共享内存区Shared RAM两个核都能访问通过硬件机制保持一致性IPC中断IPC Interrupt一个核往另一个核发中断通知事件邮箱/消息RAMMessage RAM实现类似邮箱的硬件机制我的设计是开辟一块2KB的共享内存作为控制参数区和数据缓冲区。C28x侧把采样数据、状态字、控制量写进共享内存写完置一个标志位并发IPC中断给CM4。CM4侧收到中断后从共享内存把数据读出来封装成UDP/TCP报文发出去。共享内存的读写要特别注意一致性。F28388D的CM4和C28x访问共享RAM时理论上没有硬件cache一致性问题因为设计上就不带复杂cache但你还是需要保证多字节数据结构的写入原子性。我的做法比较土但可靠写一个数据块之前先写一个“写入中”标志写完之后再改成“写入完成”读方只认“完成”标志。虽然有乒乓缓冲的痕迹但嵌入式环境下简单可靠的方案永远优先。4.2 把C28x侧数据封装成以太网报文的思路这里我给一个实际可用的思路。C28x侧每1kHz产生一批数据比如三路电流采样编码器位置加上时间戳总共32字节。这些数据放到共享内存的两个槽位里交替写入双缓冲。CM4侧收到IPC中断后把最新的那一帧数据打上UDP头发给上位机。UDP比TCP省心的地方在于不需要连接管理发就完了。如果你的应用对数据可靠性要求高比如要求不丢包那用TCP。但TCP会有重传延迟在1kHz高频传输场景下其实UDP应用层序号校验更实用。// CM4侧收到IPC中断后组包发送 void CM4_IPC_Rx_ISR(void) { // 读取共享内存中C28x写入的数据 memcpy(axis_data, shared_ram_buff, sizeof(axis_data)); // 组包UDP 自定义协议头 struct pbuf *p pbuf_alloc(PBUF_TRANSPORT, sizeof(custom_pkt_t), PBUF_RAM); custom_pkt_t *pkt (custom_pkt_t *)p-payload; pkt-id frame_id; pkt-timestamp axis_data.timestamp; pkt-ia axis_data.ia; pkt-ib axis_data.ib; pkt-ic axis_data.ic; pkt-pos axis_data.pos; udp_sendto(udp_pcb, p, remote_ip, REMOTE_PORT); pbuf_free(p); }4.3 实测从C28x采样到上位机收到数据这个链路我用了逻辑分析仪Wireshark抓包验证。具体节奏C28x每1ms产生中断写入共享RAM发IPC给CM4CM4收到IPC中断组UDP包发送上位机Wireshark抓包统计每两个包的时间间隔实测结果是UDP包间隔稳定在1ms±0.02ms几乎无抖动。这说明整个链路的最大延迟瓶颈不在网络而在中断响应的不确定性。F28388D的IPC中断响应是硬件级的中断优先级配置合理的前提下确定性非常好。我同步抓过以太网线上的波形用示波器看RMII的TX_EN和RXDV信号很干净没有毛刺。这说明硬件设计本身是过关的问题基本都出在软件配置上。5. 联调排错实录link up但ping不通问题出在哪5.1 问题清单速查表这里先给一张速查表后面细说几个典型案例。现象可能原因检查手段PHY寄存器读全FPHY地址错误、MDIO时序问题、PHY复位未完成示波器抓MDC/MDIO波形指示灯亮但ping不通MAC没配好、LWIP未初始化、IP地址不对串口打印状态、抓包能ping通但TCP速度慢MEMP_TCP_SEG不够、Nagle算法未关、窗口太小调整lwipopts.h参数运行一段时间网卡卡死描述符没回收、内存碎片、中断优先级问题打印描述符状态、内存统计通过交换机能通但直连不通交叉直连线问题现在网卡都自协商较少见换线和交换机对比5.2 问题APHY寄存器读出来全是0xFFFF这是我第一次上电调试时遇到的头号问题。MDIO读DP83822的寄存器0PHY Identifier一半时返回0xFFFF查啥都查不到。排查思路先确认PHY地址。用万用表量PHYAD引脚的上下拉电阻确认板上的PHY地址到底是多少。YXDSP-F28388D板上是0x01再确认MDC时钟频率。MDIO协议是慢速协议MDC时钟不能太高TI手册建议PHY侧不超过2.5MHz。我一开始把MDC分频配置得太高导致PHY响应不了最后确认复位。把GPIO复位延时加长到150ms问题解决核心结论先软件复位、再延时、再读ID顺序不能乱。PHY上电后需要时间完成内部校准和PLL锁定这个时间预留不够后面全是坑。5.3 问题BARP能通但ICMP不能ping通这个现象很诡异。用PCarp -a能看到开发板的MAC地址说明ARP应答是正常的。但ping开发板IPPC侧一直显示超时。后来查了代码才发现问题出在LWIP的网卡序号上。我在netif_add之后没有调用netif_set_default导致LWIP在判断收到的ICMP包该发给哪个网卡时出了问题虽然只有一个网卡LWIP也会因为默认网卡为空而丢弃某些包。修复很简单netif_set_default(g_netif); netif_set_up(g_netif);同理如果启动DHCP还要在获取到IP后调用netif_set_ipaddr后才允许netif_set_up。顺序错了DHCP能分配到IP但网络不可用属于经典的“半通不通”。5.4 问题C运行一段时间后网络卡死TCP长连接传输文件传了十几MB后网卡突然就死了ping不通重连也连不上。这个问题我排查了很久最后定位到是DMA发送描述符没有正确回收。LWIP发送数据时low_level_output把数据地址交给了DMA描述符但LWIP不会等你DMA发送完就会释放pbuf。如果你在low_level_output里没有等待DMA发送完成标志就直接返回了ERR_OKLWIP会认为数据已经发出去了实际上DMA还在读这块内存。等到缓冲区被复用数据就乱了。解决方案是在low_level_output里增加发送完成等待机制// 等待DMA发送完成带超时 uint32_t timeout 100000; while (!(emac_tx_desc[desc_idx].flags EMAC_TX_DESC_COMPLETE)) { if (--timeout 0) { // 超时说明DMA异常需要复位外设 return ERR_IF; } }但注意这个等待不能放在中断上下文里否则中断嵌套会导致系统卡死。我实际是在主循环上下文里调用tcp_writetcp_output所以这个等待是安全的。如果是高频发送场景更好的方案是用发送完成中断来回收描述符类似Linux的NAPI思路。F28388D的EMAC发送完成中断是有的只是裸机下处理起来要多写不少代码。5.5 避坑技巧汇总根据这次调式经验我整理了5个最容易出问题的点全部经历过全部确认有效EMAC的DMA描述符地址必须4字节对齐。F28388D是32位DMA不对齐会直接卡死或数据错乱。用MEM_ALIGN宏处理即可。lwipopts.h中MEM_ALIGNMENT和平台宽度保持一致。F28388D是32位平台MEM_ALIGNMENT配4不要配8浪费RAM也不要配2XIP问题。RMII参考时钟只能有一个源。要么PHY提供要么MAC提供两边都没配或者都配了结果都是收不到数据。LWIP的PBUF分配失败时一定要有日志。实际运行中pbuf_alloc失败会静默丢包很难排查。我加了一个失败计数器通过UDP周期上报能直观看到系统是否在高负载下丢包。改了PHY寄存器后要等PHY重新协商完成。比如改了速度、双工模式或PHY地址要轮询PHY的Link Status位寄存器1的bit2等到它置1再进行下一步。否则你初始化完了PHY还在自协商前面读到的状态都是“假通”。6. 性能实测与优化方向6.1 吞吐量实测TCP和UDP表现调试稳定后我做了几组性能测试。测试环境是开发板通过网线直连PCPC上用iPerf测试F28388D侧用LWIP跑TCP服务器和UDP echo服务。测试项结果说明TCP下行PC→板6.2MB/s约50Mbps受限于LWIP单线程性能TCP上行板→PC5.8MB/s略低于下行和CPU负载有关UDP下行 1472B包8.1MB/sUDP无ACK开销性能更高UDP上行 1472B包7.9MB/s接近100Mbps线速的65%百兆以太网的线速上限是12.5MB/s我测到6~8MB/s基本符合LWIP裸跑的性能范围。想进一步提升几个方向关闭TCP的Nagle算法tcp_nagle_disable小包延迟能降低不少增大TCP_WNDTCP接收窗口到64KB以上让TCP单连接吞吐量上去接收中断里直接用PBUF_REF零拷贝方案省掉memcpy如果对实时性要求高给EMAC中断设置最高优先级并放到CM4的某个专用中断通道上6.2 后续扩展方向这套基础打通之后扩展空间很大。F28388D只有一个EMAC所以别想在这颗芯片上做多端口交换但单端口做Modbus TCP从站、EtherNet/IP适配层、OPC UA UA over TCP这些应用层协议都是可行的。我下一步计划是把C28x侧的电机控制参数通过这个以太网链路做远程监视和参数整定把上位机的配置页面做成网页直接通过HTTP访问板卡上的Web Server。LWIP有自带httpd server加上SSI动态变量填充就能在嵌入式设备上做个轻量级的调试页面。另外一个实用方向是Bootloader。网络远程固件升级对现场设备非常有用LWIPTFTP就能实现。F28388D支持双Bank Flash升级时可以做到“运行中升级失败自动回滚”配合以太网OTA设备维护成本会低很多。这块开发板的以太网功能从硬件电路设计到LWIP协议栈移植再到双核数据交互整体链路清晰、可复制性强。放在工业控制场景里F28388D的高性能控制加上以太网接口能覆盖不少需要实时控制和联网通信并存的应用。最后再分享一个小技巧调试以太网时Wireshark是最好的老师。我在移植过程中每做一个环节都先在PC上抓包确认先确认PHY通能看到自协商脉冲和FLP再确认MAC通能看到以太网帧再确认LWIP通能看到ARP请求和应答。三层逐层打通出问题时能快速定位到具体层。这个习惯帮我节省了大量的联调时间建议做网络通信的朋友都养成“抓包先行”的习惯。
返回列表