TI DSP网络开发:HAL硬件抽象层移植实战与驱动开发详解

1. 项目概述与HAL核心价值

在嵌入式网络开发领域,尤其是基于德州仪器(TI)TMS320C6000系列DSP的项目中,实现稳定可靠的网络通信是许多工业控制、音视频处理和通信设备的核心需求。然而,直接让TCP/IP协议栈去操作千差万别的硬件外设,如不同厂商的以太网MAC控制器或UART芯片,无异于让一个只会说普通话的人去指挥一支由各国士兵组成的部队,沟通成本极高且极易出错。硬件抽象层(Hardware Abstraction Layer, HAL)正是为了解决这一难题而生的“通用翻译官”和“标准化指挥官”。

简单来说,HAL是位于底层硬件和上层协议栈(乃至应用软件)之间的一层软件。它的核心使命是封装硬件差异,提供统一接口。想象一下,无论底层是TI自家的EMAC、SMSC的LAN91C111还是Macronix的MX98728,通过HAL,上层的网络协议栈看到的都是一个名为“以太网设备”的标准化组件,它只关心“发送数据包”和“接收数据包”这类抽象指令,至于具体如何配置PHY寄存器、如何触发DMA传输这些脏活累活,统统由HAL下的具体驱动去完成。这种设计带来了巨大的技术价值:可移植性可维护性。当你需要将一套成熟的网络应用从一个C6455 DSK板卡迁移到另一个基于DM642的定制硬件上时,理论上你只需要替换或适配HAL中对应的以太网“迷你驱动”(Mini-Driver),上层的业务代码几乎无需改动。

本次我们要深入探讨的,正是TI为其TMS320C6000 TCP/IP协议栈(曾用名NDK)提供的HAL移植套件(Porting Kit)。这个套件不是一个黑盒库,而是一套完整的源代码,涵盖了协议栈运行所必需的四大驱动:定时器(Timer)、用户LED(User LED)、串口(Serial)和以太网(Ethernet)。其中,用户LED驱动主要用于调试指示,而串口和以太网驱动则是网络功能的核心。移植工作的本质,就是根据你的目标硬件平台,重新“组装”或“编写”这些驱动,特别是其中的硬件相关部分,让协议栈能在你的板子上“跑起来”。无论你是正在开发网络化工业网关,还是为多媒体设备添加网络管理功能,理解并掌握这套HAL的移植,都是打通DSP网络应用“任督二脉”的关键一步。

2. HAL移植套件解析与工程准备

2.1 套件结构总览

拿到移植套件后,第一件事就是理清它的目录结构。套件通常安装在\NDK\SRC\HAL目录下,其组织方式清晰地体现了“抽象”与“具体”的分离:

\HAL ├── TIMER\ # 定时器驱动源码 ├── USERLED\ # 用户LED驱动源码 ├── ETH_STUB\ # 以太网桩驱动(当硬件无以太网时使用) ├── ETH_DM642\ # DM642平台EMAC驱动 ├── ETH_C6455\ # C6455平台EMAC驱动 ├── ETH_MX\ # Macronix MX98728 MAC驱动 ├── ETH_SMSC\ # SMSC LAN91C111 MAC驱动 ├── SER_STUB\ # 串口桩驱动(当硬件无串口时使用) ├── SER_TI750\ # TI TL16C750 UART驱动 └── SER_TI752\ # TI TL16C752双UART驱动

这种结构非常直观。TIMERUSERLED是基础组件。ETH_SER_开头的目录则代表了不同的硬件支持。STUB目录下的驱动是“空实现”,当你的平台不需要该功能时,可以链接它来满足编译依赖,而不会产生实际代码。真正需要你关注的,是那些与你硬件匹配的驱动,或者当你使用非TI官方支持的芯片时,需要参照类似驱动(如ETH_SMSC)来编写自己的驱动。

2.2 构建环境与MAKEHAL工具

TI提供了MAKEHAL.BAT这个批处理工具来简化驱动库的构建过程。但在运行它之前,一个至关重要的前置步骤是设置DSP开发环境。你需要从CCS(Code Composer Studio)的安装目录或NDK根目录下,找到并运行类似dosrun_bios.bat这样的环境配置脚本。这个脚本会设置PATHC6X_C_DIR等环境变量,确保命令行可以调用正确的编译器(cl6x)、汇编器(asm6x)和链接器(lnk6x)。

MAKEHAL的使用命令格式为:makehal [platform] [library] (noclean)

  • platform:指定目标硬件平台,例如dsk6416(针对TMS320C6416 DSK)、evmdm642(针对DM642 EVM)、dsk6455(针对C6455 DSK/EVM)等。这个参数会定义相应的编译器宏(如DSK6416),驱动源码中的条件编译部分会依赖这些宏来适配不同平台的存储器映射地址、时钟配置等。
  • library:指定要构建的驱动库,例如base(构建基础HAL驱动,包括timer、userled和stub)、smsc(构建SMSC LAN91C111驱动)、ti750(构建TL16C750串口驱动)等。
  • noclean:可选参数,如果指定,则不会清理之前构建的中间文件(.obj),可以用于增量编译,节省时间。

实操心得:在开始移植前,强烈建议你先在TI官方支持的开发板(如DSK或EVM)上,使用套件中已有的驱动进行一次完整的构建和测试。这能验证你的开发环境、工具链和基本流程是否正确。命令可能像这样:makehal dsk6455 basemakehal dsk6455 c6455。观察构建过程是否有错误,并理解生成的库文件(.lib)被输出到了哪个目录。这是后续移植工作的“基准线”。

2.3 核心概念理解:NETCTRL、STKEVENT与PBM

在深入驱动代码之前,必须理解协议栈内部与HAL交互的三个核心模块,它们构成了驱动工作的“舞台”和“通信机制”。

2.3.1 网络控制模块(NETCTRL)你可以把NETCTRL看作是协议栈的“大脑”或“调度中心”。它负责管理所有网络接口(以太网、PPP等)、处理路由表、协调协议栈任务调度。对于驱动开发者而言,NETCTRL最重要的角色是提供设备注册和控制的接口。你的以太网或串口驱动在初始化时,需要向NETCTRL注册自己,告诉系统“我这里有一个网络设备”。随后,NETCTRL会负责调用驱动提供的打开、关闭、发送等回调函数。理解NETCTRL的API(在《TCP/IP Stack Programmer’s Reference Guide》中有详细描述)是驱动能够被协议栈正确调用的前提。

2.3.2 栈事件对象(STKEVENT)这是HAL驱动与协议栈任务调度器之间的“信号灯”。协议栈的主调度线程会等待来自各个设备驱动的事件信号。例如,当以太网驱动收到一个新数据包,或者定时器产生一个滴答中断时,它们就需要通过STKEVENT_signal()函数来“点亮”这个信号灯,通知调度器:“有事情需要处理了!”。STKEVENT对象在驱动初始化时被创建并关联到具体的驱动实例上。驱动内部的中断服务程序(ISR)或轮询例程在检测到事件后,必须调用信号函数,这是驱动能够触发协议栈上层处理流程的关键。

2.3.3 包缓冲区管理器(PBM)及其对象网络数据是以“包”(Packet)的形式流动的。PBM是协议栈的“内存池管理者”,专门负责分配和释放用于存储网络数据包的缓冲区。所有基于包通信的设备(以太网、串口HDLC模式)都必须使用PBM提供的缓冲区。PBM_alloc()用于申请一个空包来存放接收到的数据,PBM_free()用于在数据发送完毕后释放包。PBMQ(包缓冲队列)则是一个辅助数据结构,用于管理多个包的队列,例如发送等待队列(PBMQ_tx)和接收就绪队列(PBMQ_rx)。驱动开发者需要熟练使用这些API来高效、安全地管理数据包内存,避免内存泄漏或访问越界。

注意事项:协议栈对数据包的内存对齐有严格要求。IP头部要求16位(2字节)对齐。因此,在分配缓冲区或处理接收数据时,必须确保数据包的起始地址是偶数地址。在以太网驱动中,这通常意味着要检查并可能调整sk_buff或类似结构的data指针。在串口HDLC驱动中,LLSERIAL.H中定义的PKT_PREPAD宏(默认为18字节)就是为了在HDLC帧前添加填充,使其总头部长度达到22字节(以满足PPPoE over Ethernet的最大头部要求),并同时保证对齐。忽略对齐要求可能导致数据访问错误(硬件异常)或性能严重下降。

3. 基础驱动移植详解:定时器与用户LED

3.1 用户LED驱动:最简单的起点

用户LED驱动(LLLED.C)在功能上是最简单的,它通常只是一组宏定义,用于控制开发板上的LED指示灯亮灭。在HAL.H文件中,你可能会找到类似#define LED_ON(n) ...#define LED_OFF(n) ...的宏。这些宏最终会展开为对某个特定内存映射地址的写操作。

移植工作

  1. 查找硬件信息:首先需要从你的硬件原理图中找到控制LED的GPIO(通用输入输出)或专用LED控制器的寄存器地址。
  2. 修改宏定义:在HAL.H或你自定义的板级支持包(BSP)头文件中,根据你的硬件修改这些宏。例如,如果LED由GPIO的第0位控制,且置1为亮,那么宏可能修改为:
    #define LED_ON(n) (*(volatile unsigned int *)0x80000000) |= 0x01) // 假设地址0x80000000是GPIO数据寄存器 #define LED_OFF(n) (*(volatile unsigned int *)0x80000000) &= ~0x01)
  3. 初始化函数_llUserLedInit()_llUserLedShutdown()函数在标准实现中通常是空的。如果你的LED硬件需要额外的初始化(例如配置GPIO方向为输出),就需要在这里实现。很多TI官方开发板的硬件初始化是在CCS的GEL文件或系统上电初始化函数中完成的,所以驱动里可能为空。但对于自定义硬件,这里就是放置初始化代码的好地方。

实操心得:LED驱动虽然简单,但它是极好的调试工具。你可以在驱动代码的关键路径(如初始化成功、开始发送、收到数据包)上添加LED闪烁模式,在不依赖串口打印的情况下进行“灯光调试”。这对于早期硬件调试或性能分析非常有用。

3.2 定时器驱动:协议栈的“心跳”

定时器驱动(LLTIMER.C)是协议栈的“心跳发生器”。它负责产生一个周期性的时间基准(通常是100ms的滴答),所有基于时间的协议操作(如TCP重传定时器、ARP缓存过期、DHCP租期更新)都依赖于它。

实现原理: TI的参考实现巧妙地利用了DSP/BIOS实时操作系统的周期函数(PRD,Pseudo Real-time Device)功能。在LLTIMER.C中,一个PRD函数被配置为每100ms触发一次。在这个PRD函数中,主要完成两件事:

  1. 更新内部时钟:维护两个全局变量TimeSTimeMS,分别记录秒和毫秒。TimeMS每次增加100。当TimeMS累积到1000时,TimeS加1,TimeMS清零。这为llTimerGetTime()等API提供了时间查询基础。
  2. 触发调度事件:调用STKEVENT_signal()函数,向协议栈调度器发送一个TIMER事件,告知它“又一个时间片到了,该检查一下有没有超时的任务了”。

精度提升: 参考实现中通过#define DSPBIOS_ENHANCED 1启用了一个增强功能:在每次PRD函数执行时,它不仅更新TimeMS,还会调用DSP/BIOS的CLK_getltime()函数获取一个更高精度的时钟计数值。llTimerGetTime()函数会利用这个值,在两次100ms滴答之间进行插值,从而返回毫秒级精度的时间。如果你的应用对时间精度要求不高,可以关闭此选项以节省少量CPU开销。

移植考量

  • DSP/BIOS PRD配置:你需要确保在你的DSP/BIOS配置(.tcf文件)中,正确创建并配置了一个周期为100ms的PRD对象,并将其函数指向LLTIMER.C中的回调函数。
  • 无操作系统环境:如果你没有使用DSP/BIOS,则需要用硬件定时器中断来实现相同的功能。在中断服务程序(ISR)中,执行更新TimeS/TimeMS和触发STKEVENT_signal的操作。注意中断上下文中对共享变量的保护。
  • 滴答频率:100ms是协议栈的默认心跳周期。不建议随意更改,因为许多协议参数(如默认的TCP重传超时)是基于这个基准计算的。如果必须修改,需要仔细评估对整个协议栈超时行为的影响。

4. 串口驱动移植与HDLC帧处理

4.1 串口驱动架构与双模式运作

串口驱动在嵌入式网络中主要有两个用途:一是作为控制台(TTY),用于系统配置和调试;二是作为PPP链路,通过HDLC帧封装IP数据包,用于拨号或点对点串行连接。TI的HAL串口驱动设计非常清晰地分离了这两者,并采用了“硬件独立层+硬件特定层”的两层架构。

硬件独立层(LLSERIAL.C:这一层实现了标准的llSerialAPI(如llSerialOpen,llSerialClose,llSerialSend等)。它处理通用的逻辑,如设备实例管理、模式切换(字符模式/HDLC模式)、数据包队列(PBMQ_tx,PBMQ_rx)的管理,以及与上层NETCTRL模块的接口。这一层代码通常不需要修改,除非你需要支持多于参考设计数量的串口设备(通过修改LLSERIAL.C中的最大设备数定义)。

硬件特定层(Mini-Driver,如TI750.C:这是移植工作的核心。它包含直接操作UART硬件的代码:初始化、波特率设置、中断处理(或轮询)、字符收发。更重要的是,在HDLC模式下,它需要实现HDLC帧的组帧与解帧逻辑。

4.2 HDLC帧处理:迷你驱动的核心职责

当串口被llSerialOpenHDLC()打开后,迷你驱动就必须以HDLC帧为单位来处理数据。这比简单的字符流收发复杂得多,主要包括:

  1. 帧定界:HDLC帧以特殊的标志字节0x7E开始和结束。接收时,驱动需要识别起始标志,并持续接收直到遇到结束标志。发送时,需要在数据包前后添加标志字节。
  2. 字节填充(透明传输):为了防止数据域中出现与标志字节0x7E或转义字符0x7D相同的数据被误识别,HDLC使用了字节填充。发送时,若数据中出现0x7E0x7D,则在其前插入转义字符0x7D,并将原字符与0x20进行异或(例如,0x7E变成0x7D 0x5E)。接收时则进行相反的解填充操作。SDINFO结构体中的PeerMap字段就是用来配置哪些控制字符需要被转义的位图。
  3. CRC校验:每个HDLC帧末尾包含一个16位的CRC校验码(CCITT标准)。发送时,驱动需要计算整个数据域(不包括标志和填充)的CRC,并将其附加在帧尾。接收时,驱动需要重新计算CRC,并与接收到的CRC进行比较,以验证帧的完整性。SDINFO中的TxCRCRxCRC字段就是用于在收发过程中暂存CRC计算中间值的。

迷你驱动接口函数: 硬件特定层需要实现一组在LLSERIAL.H中声明的函数,供上层LLSERIAL.C调用。关键函数包括:

  • HwSerInit: 初始化硬件,设置波特率、数据位、停止位、校验位和流控。
  • HwSerOpen: 打开设备,通常需要配置中断并使能收发器。
  • HwSerClose: 关闭设备,禁用中断。
  • HwSerTxStart: 启动发送。如果发送器空闲(TxFree != 0),则从PBMQ_tx队列取包,开始发送第一个字节。
  • HwSerTxNext: 被上层或中断调用,发送下一个字节。此函数需要处理HDLC组帧(添加标志、填充、计算并附加CRC)。
  • HwSerRxISR(或轮询函数):在中断或轮询中读取接收到的字符,并进行HDLC解帧状态机处理。状态机需要识别标志位、处理转义字符、计算CRC,并将完整的数据帧放入PBMQ_rx队列,最后调用STKEVENT_signal()通知协议栈。

4.3 数据对齐与缓冲区管理

如前所述,数据包必须16位对齐。对于串口HDLC驱动,LLSERIAL.H中定义的PKT_PREPAD宏(默认18字节)确保了这一点。当迷你驱动通过PBM_alloc()申请一个缓冲区用于接收HDLC帧时,它得到的指针已经考虑了对齐。驱动在向缓冲区(pRxBuf指向的位置)写入数据时,直接写入即可。

发送流程示例

  1. 上层协议栈调用llSerialSend,将数据包放入PBMQ_tx队列。
  2. 如果TxFree标志为真,LLSERIAL.C会调用迷你驱动的HwSerTxStart
  3. HwSerTxStart从队列头取出包,设置hTxPend,pTxBuf,TxCount,初始化TxCRC,然后开始发送起始标志0x7E
  4. 随后,每次需要发送下一个字节时(可能在HwSerTxNext函数或发送完成中断中),驱动检查TxCount
    • 如果TxCount > 0,从pTxBuf读取一个字节,检查是否需要转义(根据PeerMap和数值),更新TxCRC,发送字节(或转义序列)。
    • 如果TxCount == 0,说明数据域已发完,此时发送计算好的TxCRC(两个字节,同样可能需要转义),最后发送结束标志0x7E
    • 发送结束后,调用PBM_free(hTxPend)释放包,并设置TxFree = 1,通知上层可以发送下一个包。

常见问题与排查

  • 数据乱码或丢包:首先检查波特率、数据位、停止位、校验位设置是否与对端设备完全一致。用逻辑分析仪抓取串口波形是最直接的调试手段。
  • HDLC帧无法建立:检查双方的帧定界符(0x7E)和转义规则(0x7D)是否一致。确认PeerMap配置是否正确。可以在驱动中打印原始收发字节,对比分析帧结构。
  • CRC校验错误:确认CRC计算算法是否为标准的CCITT CRC-16(多项式0x1021)。在线计算工具可以帮助验证。同时检查在计算CRC时,是否包含了转义插入前的原始数据。
  • 性能瓶颈:串口速率较低,PPP链路本身是低速链路。避免在中断服务程序中做复杂处理(如大量内存拷贝)。确保PBM缓冲区大小设置合理,避免频繁的内存分配释放。

5. 以太网驱动移植深度解析

5.1 以太网驱动架构

以太网驱动的架构与串口驱动类似,也分为硬件独立层(LLPACKET.C)和硬件特定层(迷你驱动,如SMSC.C)。LLPACKET.C实现了llPacketAPI,负责多设备管理、包队列、与NETCTRL交互等通用逻辑。而硬件特定层则负责直接操作以太网MAC控制器的寄存器。

5.2 硬件特定层(迷你驱动)实现要点

移植或编写一个新的以太网迷你驱动,需要完成以下核心任务:

5.2.1 初始化与配置

  1. 硬件复位:通过控制MAC控制器的复位寄存器,确保硬件处于已知状态。
  2. PHY配置:通过MAC的MDIO接口(或直接GPIO模拟)访问外部的PHY芯片,配置自适应、双工模式、速度等。这部分代码通常比较繁琐,需要仔细阅读PHY芯片的数据手册。
  3. MAC地址设置:将唯一的MAC地址写入MAC控制器的地址寄存器。
  4. DMA描述符初始化:这是高性能以太网驱动的关键。你需要初始化发送和接收描述符环(Descriptor Ring)。每个描述符通常包含数据缓冲区的物理地址、包长度、所有权标志等信息。驱动需要为描述符环和对应的数据缓冲区分配连续、对齐的物理内存(通常使用MEM_alloc或类似函数)。
  5. 中断配置:使能MAC控制器的主要中断源,如接收完成、发送完成、总线错误等,并将中断服务程序(ISR)挂接到DSP的中断向量表。

5.2.2 数据接收流程(RX)

  1. 中断触发:当MAC控制器收到一个完整的帧并存入由空闲描述符指向的缓冲区后,会产生接收中断。
  2. ISR处理:在中断服务程序中,首先检查中断状态寄存器,确认是接收中断。然后遍历接收描述符环,找到所有“被硬件占用”(即已存入数据)的描述符。
  3. 数据提取:对于每个已完成的接收描述符,从其指向的缓冲区中读取数据包。需要检查接收状态(长度、CRC错误、帧错误等)。
  4. 交付协议栈:如果帧有效,调用PBM_alloc()申请一个新的PBM缓冲区,将数据拷贝进去(或者,在支持零拷贝的高级设计中,将描述符直接“交给”PBM,但这需要仔细的内存管理)。然后,将这个PBM包句柄放入PBMQ_rx队列,并调用STKEVENT_signal()
  5. 描述符回收:清理当前描述符的状态,将其所有权交还给硬件(设置相应标志),以便硬件可以继续使用它接收新数据。

5.2.3 数据发送流程(TX)

  1. 协议栈调用:上层协议栈通过llPacketSend将待发送的PBM包放入PBMQ_tx队列。
  2. 驱动取包:如果发送描述符环有空闲条目,驱动从PBMQ_tx队列取出一个包。
  3. 填充描述符:将PBM缓冲区中的数据物理地址和长度填入一个空闲的发送描述符,设置所有权标志为“硬件”,并触发MAC控制器开始发送。
  4. 发送完成中断:当MAC控制器完成帧发送后,会产生发送完成中断。
  5. 资源释放:在发送完成中断中,遍历发送描述符环,找到所有已发送完成的描述符,调用PBM_free()释放对应的PBM缓冲区,并将描述符状态标记为空闲,归还给驱动。

5.3 关键数据结构:PDINFO

类似于串口的SDINFO,以太网迷你驱动也围绕一个核心数据结构PDINFO(在LLPACKET.H中定义)展开。它包含了设备实例的所有状态信息:

  • hEvent:关联的STKEVENT句柄。
  • PBMQ_tx/rx:发送/接收队列。
  • TxDescRing,RxDescRing:发送/接收描述符环的首地址。
  • TxDescHead/Tail,RxDescHead/Tail:描述符环的头尾指针,用于管理空闲和已使用的描述符。
  • PhyAddr:PHY芯片的地址。
  • 各种硬件寄存器基地址、中断号等。

你的迷你驱动代码将大量操作这个结构体的成员。

5.4 性能优化与注意事项

  1. 描述符环大小:描述符环的长度需要在内存占用和性能间权衡。环太小,容易溢出丢包;环太大,浪费内存。通常接收环可以设置得比发送环大一些。
  2. 缓冲区对齐:DMA缓冲区(描述符指向的数据区)必须按照MAC控制器和系统总线的要求进行对齐(通常是32字节或128字节边界)。PBM_alloc()默认返回的缓冲区可能已经满足对齐要求,但最好在驱动初始化时确认。
  3. 缓存一致性:在带有数据缓存(Cache)的DSP(如C6000系列)中,必须小心处理DMA和CPU之间的缓存一致性问题。CPU写入待发送数据到缓存后,必须在启动DMA前,将对应的缓存行写回(Writeback)到主存,否则DMA读到的是旧数据。同样,DMA将接收数据写入主存后,CPU在读取前,必须无效(Invalidate)对应的缓存行,否则读到的是缓存中的旧数据。通常使用CACHE_wbInvCACHE_wb/CACHE_inv等函数来处理。
  4. 中断合并:为了降低中断频率,提高效率,可以启用MAC控制器的中断合并功能,例如每收到N个包或每隔一段时间才产生一次接收中断。
  5. 错误处理:必须完善地处理MAC控制器报告的各种错误,如接收溢出、发送欠载、CRC错误等,并在必要时重置MAC或描述符环。

踩坑实录

  • 丢包严重:最常见的原因是描述符环耗尽。检查驱动是否及时处理了完成的中断并回收了描述符。也可能是接收中断处理函数执行时间太长,导致新的中断被延迟或丢失,可以考虑在中断中只做最少量的工作(如标记标志),将耗时的处理(如交付协议栈)放到任务线程中。
  • 网络不通:首先用ping命令测试。如果完全不通,检查MAC地址配置、PHY链路状态(Link Up/Down)、以及IP地址和路由设置是否正确。可以在驱动初始化后,尝试发送一个简单的ARP请求包,并用抓包工具(如Wireshark)在交换机端口查看是否有数据发出。
  • 性能不达标:除了优化缓存和中断,还可以考虑使用DSP/BIOS的实时分析工具(如RTDX, RTA)来测量中断延迟、任务调度时间,找出瓶颈。对于小包吞吐量测试,协议栈本身的处理开销可能成为瓶颈,此时需要评估协议栈的配置参数(如任务优先级、内存池大小)是否合理。

6. 移植实战步骤与系统集成

6.1 移植工作流

  1. 环境搭建:确保你的CCS工程包含NDK协议栈的库文件和头文件路径。正确运行环境配置脚本。
  2. 创建驱动目录:在\SRC\HAL下为你新的硬件创建目录,例如ETH_MYCHIP。将最接近的现有驱动(如ETH_SMSC)拷贝过来作为模板。
  3. 修改迷你驱动
    • 替换头文件中硬件相关的寄存器定义。
    • 重写HwEthInit,HwEthOpen,HwEthClose,HwEthISR等函数,用你的硬件操作替换模板中的代码。
    • 根据你的硬件描述符结构,修改描述符初始化、遍历和回收的逻辑。
    • 实现PHY的配置和状态读取函数。
  4. 修改构建脚本:你需要修改MAKEHAL.BAT或创建自己的构建脚本,添加对新平台(platform)和新驱动库(library)的支持,主要是定义正确的编译器宏和指定源文件路径。
  5. 编译测试:尝试编译你的新驱动库,解决所有编译错误和警告。
  6. 集成到示例工程:找一个最简单的网络示例工程(如hello示例),将原来链接的以太网库替换成你新编译的库。修改示例工程中的板级支持文件,确保正确初始化你的硬件(时钟、DDR、引脚复用等),并调用正确的驱动初始化函数。
  7. 调试与验证
    • 先调试PHY,确保链路能正常建立(Link Up)。
    • 然后调试驱动初始化,确保描述符环和MAC配置正确,无错误标志。
    • 最后调试数据通路。可以从发送一个固定的ARP请求包开始,用抓包工具验证。再测试环回(自己ping自己),最后测试与外部主机的通信。

6.2 系统集成与配置

驱动编译通过并基本功能正常后,需要将其集成到最终的应用系统中。这主要涉及DSP/BIOS的配置(.tcf文件)和网络参数的设置。

  1. DSP/BIOS配置
    • 中断管理:为以太网MAC中断分配一个硬件中断号(HWI),并将其与你的驱动ISR函数关联。合理设置中断的优先级。
    • 任务与信号量:协议栈本身会创建多个任务(如NetTask)。你需要确保驱动中可能使用的任务、信号量或队列与协议栈的任务优先级协调,避免优先级反转或死锁。
    • 内存段:为描述符环和大数据缓冲区定义专用的内存段(例如IRAMSDRAM),并确保其属性(如缓存性)设置正确。
  2. 网络协议栈配置:在应用程序的cfg文件或初始化代码中,需要正确配置网络参数:
    • NC_NetStart:启动网络系统,传入IP地址、子网掩码、默认网关等。
    • 调用llEthOpen打开你的以太网设备,并将其添加到网络接口列表中。
    • 根据需要配置DHCP客户端、DNS等高级服务。

移植工作是一个从底层硬件到上层应用的系统性工程。耐心、细致的调试和对协议栈机制的深入理解是成功的关键。当你第一次看到你的定制硬件板通过自己移植的驱动成功ping通另一台计算机时,那种成就感是对所有努力的最佳回报。记住,多利用TI官方论坛、代码示例和文档,这些资源能帮你避开很多前人已经踩过的坑。