ARTICLE DETAIL

资讯详情

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

基于F28388D的以太网开发:EMAC驱动与LWIP移植实战

基于F28388D的以太网开发:EMAC驱动与LWIP移植实战 去年年底我做了一批基于TI C2000系列的高实时性控制板其中一块核心板用的是YXDSP-F28388D。硬件回来之后调试工作基本顺利唯独网络通信这一块卡了我将近两周。不是PHY不通就是LWIP协议栈移植后ping不通要么就干脆进不了中断。现在回头看很多问题其实都是对芯片的EMAC模块理解不够深以及移植LWIP时接口细节处理不到位导致的。这篇文章我准备把整个调试过程完整记录下来从F28388D的EMAC硬件特性、管脚配置、MDIO通信、PHY芯片驱动到LWIP协议栈的底层接口封装、内存管理、中断处理再到最终通过ping和TCP/UDP实测。内容会比较长但基本都是可以直接复现的实操经验哪怕是第一次接触C2000以太网开发的工程师按这个思路走下来也能少踩很多坑。先说清楚这篇文章适合谁看手里有YXDSP-F28388D开发板或者类似C2000系列芯片、要把网络通信跑起来的硬件工程师和嵌入式软件工程师。如果你只是做应用层开发不碰底层协议栈那直接看最后的常见问题部分就够了。1. 为什么选F28388D跑以太网先搞清楚硬件底子1.1 双核架构里那颗容易被忽略的CM4内核F28388D这颗芯片在C2000家族里属于比较特殊的存在它不光是有一颗主频200MHz的C28x DSP内核还集成了一颗运行频率最高200MHz的Arm Cortex-M4内核。很多人拿到这颗芯片只盯着C28x用忽略了CM4的存在但实际上这颗CM4就是为通信类任务准备的。它帮你把以太网、USB、CAN这类需要协议栈支撑的外设都挂在了CM4这一侧让C28x腾出精力专心做实时控制。从系统架构上看F28388D的以太网MAC是一个符合IEEE 802.3标准的三速10/100/1000MbpsEMAC模块内部自带DMA控制器支持MII、RMII、RGMII三种接口模式。实际项目里由于RMII只需要2根数据线加一根50MHz参考时钟能省掉不少IO资源所以我最终选的是RMII100Mbps的方案。开发板载的PHY芯片是TI自家的DP83822工作在RMII从模式MAC侧提供50MHz的REF_CLK给它。这里有个特别需要注意的点F28388D的EMAC虽然挂在CM4总线上但C28x通过IPC机制也能间接访问到MAC寄存器和DMA描述符。不过正常开发中不建议双核同时操作EMAC否则DMA描述符的同步问题会让你崩溃。我实测下来最稳妥的做法是把LWIP和EMAC驱动全部放在CM4上跑C28x通过IPC消息和共享内存和CM4做数据交互。这样职责划分清楚控制周期也不会被网络任务干扰。1.2 从EMAC到PHY芯片的信号链路搞清楚F28388D的EMAC和外部PHY芯片之间的连接方式是移植前最重要的一步。整个链路分两部分一边是MAC通过DMA访问内部RAM拿数据另一边是MAC通过外部接口把数据发给PHY芯片再由PHY芯片完成物理层的编码和收发。RMII接口下引脚信号总共有这么几个TX_EN发送使能、TX0/TX12位发送数据、RX0/RX12位接收数据、REF_CLK50MHz参考时钟、CRS_DV载波侦听/数据有效接收方向、MDIO管理数据输入输出、MDC管理时钟。开发板上DP83822的地址我确认过是0x00由RX_D0、RX_D1、RX_D2三个引脚上的上下拉电阻决定的如果你的板子PHY地址不是0后面所有PHY寄存器读写都会失败这一点后面排查问题时还会提到。除了数据通道PHY芯片还需要单独的复位引脚和控制引脚。DP83822的复位一般是低电平有效至少需要保持10ms以上才能稳定释放。另外DP83822有个RX_DV/RX_ER复用引脚在RMII模式下要特别注意寄存器配置否则接收方向会莫名其妙丢包。1.3 开发工具链的版本匹配问题把这一节单独拿出来写是因为我在配置工程的时候吃过版本不匹配的亏。TI官方给的C2000Ware里带有Ethernet的例程但例程默认是用CCSCode Composer Studio建的工程。如果你习惯用IAR或Keil直接移植例程代码时头文件路径、启动文件、链接脚本都要自己重新配一遍工作量不小。我的建议是如果条件允许还是用CCS配合TI官方例程来做。因为C2000Ware里的例程是直接适配芯片的包括管脚初始化、MAC复位时序、PHY配置代码都是验证过的你拿到后改改IP地址就能先把链路跑通然后再逐步替换成自己的业务代码。我用的版本是CCS 12.x配合C2000Ware 5.01这套组合测试下来比较稳定。2. EMAC底层驱动的搭建从寄存器到DP838222.1 时钟树与管脚复用第一道关卡F28388D片内有两个PLL分别给C28x和CM4提供时钟。以太网这块比较特殊EMAC需要独立的时钟在RMII模式下MAC侧需要提供50MHz的REF_CLK给PHY。这个50MHz不是随便从某个GPIO输出的必须通过芯片内部的时钟分配器把CM4的PLL输出分频后送到指定引脚。管脚复用上RMII模式涉及的几个关键IO分别是PB7RMII_CRS_DV、PB8RMII_RX0、PB9RMII_RX1、PD0RMII_TX_EN、PD1RMII_TX0、PD2RMII_TX1、PD4RMII_MDC、PD5RMII_MDIO、PD6EMAC_REF_CLK。开发板上这些引脚默认可能有其他复用功能比如接了LED或者按键配置的时候一定要把GPIO的复用功能切换到EMAC外设上否则信号出不去也进不来。实际调试时我最常犯的错误是GPIO配置好了但忘了使能EMAC外设的时钟门控。F28388D的CM4侧每个外设都有独立的时钟使能位藏在系统控制模块里。EMAC的时钟使能位不在常规的PCLKCR寄存器组里而是在一个专属于CM4域的寄存器中这一点和TI的TMS320F28xxx系列传统外设完全不同。我在初始化代码里写了一组延时用来保证管脚复用配置、时钟使能、外设复位释放之间的时序正确。实测发现GPIO配置寄存器写完后如果不加几个时钟周期的延时马上操作EMAC总线上会有概率出现冲突轻则配置失败重则直接触发硬件错误中断。2.2 无需外部SDRAMDMABUF描述符与缓冲区的内存布局F28388D的EMAC自带一个DMA引擎通过描述符链表的方式管理收发缓冲区。每个描述符包含数据缓冲区的地址、数据长度、控制标志和状态标志。DMA引擎会按照描述符链表依次搬运数据收发各自独立维护一套描述符链表。这块芯片的以太网控制器比较实用的一点是描述符和缓冲区都可以放在普通的片上RAM里。F28388D的CM4域有256KB的RAM分成多个bank其中有一部分是紧耦合内存TCM访问速度最快。我在设计内存布局时把LWIP的PBUF池和描述符都放在了非TCM区域因为LWIP在运行时会频繁申请和释放内存如果用TCM反而容易因为访问冲突引入不必要的等待周期。收发缓冲区的大小我设置了1520字节比标准的以太网MTU1500字节多出20字节用于容纳VLAN标签或者将来加时间戳的扩展字段。实际使用中如果只跑标准IP包1520字节足够用了。每个方向的描述符数量我配置了16个因为F28388D的EMAC DMA支持描述符预取机制描述符太少容易在突发流量下出现DMA空闲等待太多则浪费内存16个是我测下来比较均衡的值。2.3 PHY芯片DP83822的初始化过程PHY芯片的寄存器读写是通过MDIO总线完成的。DP83822在上电后默认处于CMII模式需要根据实际板子用的是MII还是RMII来修改寄存器。F28388D的EMAC模块的MAC控制寄存器里有个接口模式选择位而PHY芯片这边也有对应的模式配置寄存器两边必须匹配。DP83822的初始化流程我总结为四步复位、模式配置、ANEG自动协商配置、链路状态检查。复位最简单拉低复位引脚保持一段时间再释放。模式配置要读写PHY的寄存器0x1FPHYCR其中bit14和bit13控制RMII模式。ANEG配置是把寄存器0x00的bit12、bit13速度/双工选择都置为1让PHY自动协商到100M全双工。链路状态的检查是最容易踩坑的DP83822的寄存器0x01BMSRbit2指示链路是否建立但这个位在链路未建立时不是稳定状态。我把轮询函数写成一个带超时的循环每10ms读一次最多等2秒。如果2秒后还是没有建立链路就打印错误并检查网线连接。3. LWIP协议栈的移植核心接口逐个击破3.1 决定的瞬间用RAW API还是NETCONN APILWIP支持三种编程接口RAW API、NETCONN API基于操作系统模拟层和Socket API。F28388D上跑LWIP选择哪种接口关系到整个软件架构的复杂度。我在这颗芯片上最终选择了NETCONN API。原因有两个第一CM4内核上我跑了一个轻量级的FreeRTOS系统LWIP可以通过sys_arch适配层挂到FreeRTOS上用信号量和邮箱机制来同步中断和协议栈线程第二后续应用层要同时处理TCP服务、UDP广播、MQTT客户端多个连接用NETCONN的多线程模型比RAW API的单线程轮询模型开发效率高得多。如果你是不跑RTOS、只想要一个极简的TCP/IP栈那RAW API在无操作系统环境下确实能省掉不少RAM开销。但F28388D这块芯片根本不缺RAM所以没必要为了省那几十KB内存去把代码复杂度抬高。3.2 sys_arch适配层LWIP和FreeRTOS之间的桥梁移植LWIP到带RTOS的环境最核心的工作就是写sys_arch.c文件。这个文件实现了LWIP定义的操作系统抽象接口包括信号量、互斥锁、邮箱消息队列、系统时钟等。LWIP中的信号量分为二进制信号量和计数信号量我在实现时直接封装了FreeRTOS的xSemaphoreCreateBinary和xSemaphoreCreateCounting。这里有个细节FreeRTOS的二进制信号量在调用xSemaphoreGive时会有“优先级反转”的问题LWIP已经考虑到了这一点它在协议栈内部会尽量使用邮箱而不是信号量来做数据传递。所以我在实现邮箱时用的是FreeRTOS的队列接口。时钟接口比较简单LWIP要求提供一个以毫秒为单位的系统时间函数。我用的是CM4内核的SysTick配置成1ms中断一次全局变量递增加上volatile修饰符防止编译器优化后读不到最新值。移植完成后我在lwipopts.h里定义了NO_SYS为0开启了OS支持。同时把内存池大小调大因为默认配置的PBUF池在TCP通信时明显偏小会导致收发的数据被频繁拷贝降低吞吐量。3.3 网卡驱动层ethernetif.c的修改重点LWIP的网卡驱动层核心函数就是low_level_init、low_level_output和low_level_input三个。low_level_init要做的事非常多初始化EMAC DMA描述符、建立收发描述符链表、使能MAC和DMA、设置接收过滤规则最后还要把网卡的MAC地址写入硬件寄存器。F28388D的MAC地址寄存器是分高低两组存的分别是MAC_SA0、MAC_SA1、MAC_SA2。我为了方便后续产品出厂时烧录唯一MAC把MAC地址定义成了三个连续的16位变量通过配置接口可以在初始化前修改。low_level_output做的事情是把LWIP的PBUF数据通过DMA发送出去。这里有个重要的内存问题LWIP传给网卡驱动的PBUF不一定是一段连续的内存可能是多个分段组成的链表。但EMAC的DMA发送要求数据缓冲区是物理上连续的地址。解决方法是在驱动内部预分配一块发送缓冲区在low_level_output里把PBUF链表里的数据全部拷贝到这这块连续内存然后再交给DMA发送。实测下来这种方式虽然多了一次memcpy但对100Mbps以太网来说开销完全可以接受。我在测试中跑到了60Mbps以上的TCP吞吐CPU占用率不到30%说明拷贝不是瓶颈。low_level_output的返回值要注意必须返回ERR_OK或者ERR_IF不能返回ERR_MEM这种错误码。因为LWIP在发送失败时会根据返回值决定是否重传如果返回值不准确会导致协议栈状态机混乱。low_level_input是在收到以太网帧时被中断服务函数调用的。在这个函数里把DMA描述符中收到的数据包装成一个PBUF结构再调用netif-input函数交给协议栈。DMA描述符只有16个如果入包速度大于协议栈处理速度描述符会耗尽。这种情况下我在中断里做了一次简单的丢包统计如果描述符用完就直接丢弃新包。这样保证老任务不阻塞新任务也不会把系统拖死。3.4 中断服务函数的编写把收包和协议栈剥离开F28388D的EMAC DMA中断源比较多接收完成、发送完成、接收错误、发送错误、链路状态变化等。在CM4上这些中断统一映射到EMAC的中断线需要在中断服务函数里读取DMA中断状态寄存器来判断具体事件。我最开始直接把协议栈处理函数放在中断里调用发现一旦网络流量稍微大一点其他中断就会严重滞后C28x的实时控制任务也会被动受影响。后来把整个设计改成中断里只做最轻量的事情——把收到的数据帧放入一个环形缓冲区置一个标志位然后发送一个信号量唤醒协议栈线程。协议栈线程是优先级比较低的它被唤醒后从环形缓冲区取数据再调用ethernetif_input完成PBUF的构造和上报。实测下来这个优化非常有效。在64字节小包、全速发送的情况下协议栈线程的CPU占用率稳定在可接受范围内其他任务的实时性也没有被破坏。4. 联调阶段从ping通到TCP吞吐测试4.1 第一个里程碑ping通本地回环当代码写完第一次上电准备ping的那一刻整个人的心跳跟这个时钟频率差不多。我的测试方法是先ping通PC上的虚拟网卡再连接到开发板第一次ping通了说明MAC地址和PHY的链接都基本正常了。但这里有一个迷惑性很强的情况开发板上电几秒后PC网卡显示网络已连接但是ping不通。这个现象通常不是因为收发通道坏了而是因为ARP请求没有得到响应。ARP响应的前提是LWIP协议栈要能正确读取到网卡的MAC地址。我排查下来问题出在NETIF初始化时传入的MAC地址和我写入EMAC硬件寄存器的地址不一致。两者偏一位ARP就无法回包PC端的ARP缓存刷新后ping才显示超时。还有一种可能性是接收中断没有正常工作网卡收到了ARP请求但CPU没感知到数据到了。排查方式是在LWIP网卡接收回调里打一个串口日志看看收到包的时候是否会进入中断。4.2 丢包率的那些坑ARP缓存超时、DMA描述符耗尽ping通了并不代表一切正常。我在测试1000个ping包时发现偶尔会有丢包但丢包率在0.1%左右看起来问题不大。但那个时期总感觉哪里不对劲。排查一圈后发现问题出在ARP缓存上。由于我的PC和开发板长时间没有通信ARP缓存超时后PC发往开发板的数据会先发一个ARP请求。开发板的回复偶尔会晚于PC的ARP超时默认3秒导致PC认为ARP失败后续的TCP/UDP数据包自然发不出去。解决这个问题的思路有两个一是缩短开发板ARP响应的时间这个和LWIP协议栈内部处理ARP请求的优先级有关不太好改二是在应用层和PC上做处理把PC的ARP缓存设置为不超时或者在开发板加一个周期性的ARP广播包我最后用了第二个方案在空闲任务里每5秒主动发送一个免费ARP包把链路状态保持在活跃状态。另一个内网大规模抓包时发现的问题如果DMA描述符数量太少一旦出现突发流量接收方向的数据包就会被丢掉。我测试时把接收描述符扩展到24个后突发流量下的丢包率明显下降了。4.3 测试TCP和UDP吞吐量实测数据为了验证整体性能我在开发板上用LWIP的NETCONN接口分别写了一个TCP回环服务器和一个UDP回环服务器。PC端用网络调试助手和iperf3分别测试。TCP测试时TCP窗口大小设置为64KB实测吞吐率在55-60Mbps之间。这个成绩对于100Mbps以太网已经不错了瓶颈主要在协议栈的内存拷贝和中断处理上进一步优化可以开启LWIP的零拷贝选项但那样会增加PBUF的维护复杂度也会增加DMA缓冲区的管理难度。UDP的测试更直观我让PC端每秒发送1000个UDP包每个包1024字节负载开发板原样回传PC端统计返回率结果几乎达到100%。这个结果说明在中等流量下驱动的收包能力和协议栈的处理能力都能跟得上。4.4 与C28x的交互让实时控制核心和网络通信协调工作前面说了F28388D的双核最终要协同工作。我在CM4上跑的LWIP和网卡驱动而C28x跑的是电机控制算法。两个核心通过IPC寄存器组通信。实际工程中控制指令从PC下发到CM4CM4通过IPC发给C28xC28x实时调整PWM输出同时把采样到的电流、速度回传给CM4再由CM4通过TCP/UDP上报给上位机。整个过程中CM4的以太网中断不会干扰C28x的PWM中断因为两者分别在各自的内核中断控制器下不共享中断线。在测试中我把TCP回环和电机控制同时打开观察C28x侧的执行时间、PWM波形是否受网络通信的影响。实测下来TCP传输持续进行时PWM周期抖动不超过0.5%控制环的完整性保持得很好。这也验证了当初选择双核任务的正确性。5. 常见问题速查与排错技巧5.1 错误总结表遇到这些情况优先查哪里现象可能原因排查步骤网络连接显示已连接但ping不通EMAC MAC地址配置错误对比NETIF初始化传入的地址和硬件寄存器实际写入的地址抓ARP包确认是否发出ARP响应网络显示未连接PHY芯片MII/RMII模式不匹配读取PHY寄存器0x1F确认模式配置检查MAC控制寄存器的接口模式位MDIO读写超时PHY地址配置错误确认DP83822的引脚上下拉读取PHY的ID寄存器验证地址是否正确短包正常、长包丢包DMA缓冲区长度不足将缓冲区设置为1520字节以上确认描述符链表绑定正确偶尔ping超时部分包丢失接收描述符数量不够或ARP超时增加接收描述符数量加入免费ARP机制保持链路状态系统进入硬件错误中断内存访问冲突或描述符内存被踩踏检查描述符内存区域是否与其他变量重叠确认DMA缓冲区地址对齐到32字节TCP吞吐量极低LWIP内存池太小或收发有额外拷贝调大lwipopts.h中的PBUF池和TCP窗口查看mem_stats统计5.2 不要迷信官方例程两个容易被忽视的细节C2000Ware里的官方以太网例程“可以直接跑通”这件事是建立在特定开发板、特定编译器、特定版本的库函数匹配的基础上的。如果你换了一个PHY芯片哪怕只是换了PHY的地址都要重新检查MDIO时序。DP83822在RMII从模式下对REF_CLK的质量要求非常高。我用示波器量过几次发现如果REF_CLK的上升沿不够陡峭PHY芯片会周期性地误判数据。后来我把时钟源从GPIO的常规输出改成了芯片内部的专用时钟输出波形明显变好丢包率也降了下来。这一条在硬件设计阶段就要提前规划好不能等软件调完了再改板。5.3 调试过程中的工具推荐示波器、逻辑分析仪、Wireshark调试以太网工具很重要。软件层面Wireshark是必备的它能看到PC端的ARP、ICMP、TCP/UDP报文交换情况很多协议栈问题抓包看一眼就能定位。硬件层面百兆以太网虽然信号频率不算高但调试PHY芯片时用示波器看RMII接口的TX/RX波形、REF_CLK质量还是很有效的。至少要把REF_CLK的频率精度、上升下降时间、TX_EN和TX0/TX1的对齐关系测一遍。逻辑分析仪更像是跑协议时的辅助工具它能同时抓多路信号观察MDIO时序和PHY寄存器的读写内容。不过如果你在软件层已经能正常读取PHY寄存器了逻辑分析仪的作用就不大了。但排查MDIO问题时它比示波器方便因为能看到协议帧的完整序列。6. 写在最后再聊聊F28388D网络通信的扩展想法做了几个月的以太网通信项目我的感受是F28388D这颗芯片强大的地方在于它把实时控制和网络通信放在同一个芯片里设计得好的话两者可以互不干扰地协同工作。相比传统的“MCUARM网卡芯片”的多芯片方案集成度高的优势非常明显尤其在体积和功耗敏感的应用场景下。如果你后续要把这个方案用在自己的产品上有几个方向可以扩展一是把EMAC直接接到MII接口的千兆PHY上这样可以支持千兆以太网但前提是CM4的频率和DMA描述符数量都要相应调整二是在CM4上跑一个完整的嵌入式Linux那样可以用标准Linux网络协议栈连LWIP都省了但启动时间和实时性会有所牺牲三是增加EtherCAT或其他工业现场总线的支持这个已经是C2000系列的强项和以太网配合起来能够覆盖更多应用场景。最后再分享一个小经验做这种偏底层的以太网开发遇到问题时不要急着搜“为什么ping不通”而是先按照链路层→网络层→传输层的顺序逐层排查确认PHY在工作、MAC能发能收、ARP能通、TCP能传一层层排查下来大部分问题都能快速定位。我踩过的这些坑希望对你有帮助也欢迎在调试中遇到新问题时回来交流。
返回列表