1. 项目概述:从NVDK到DM642 EVM的迁移挑战
几年前,我接手了一个老项目的移植任务,要把一个在TI C6416 NVDK开发板上跑得挺稳的多通道运动检测系统,搬到当时新出的DM642 EVM评估板上。听起来像是换个硬件平台,改改引脚定义就完事了?真干起来才发现,这活儿是个典型的“嵌入式系统外科手术”,核心矛盾就两点:视频端口(Video Ports)的驱动架构彻底变了,以及内部SRAM从1MB骤减到256KB。原来的系统在NVDK上资源充裕,设计上有些“大手大脚”,到了DM642上,就得精打细算,每一KB内存都得用在刀刃上。
这个运动检测系统的核心,是实时处理多路视频流,进行帧间差分计算来检测运动物体。在NVDK上,视频采集和显示依赖一块FPGA和Ateme提供的专用图形库来桥接,数据走的是外部存储器接口(EMIF)。而DM642的亮点在于集成了三个可配置的视频端口,能直接对接像SAA7115解码器和SAA7105编码器这类芯片,相当于把视频数据通路“硬化”了,效率更高,也解放了EMIF的带宽。但好处不是白来的,你得重新学会跟这些视频端口打交道,用TI新推出的FVID驱动框架去配置它们。同时,内存的“豪宅”变“公寓”,迫使你必须重新审视每一个缓冲区、每一个BIOS对象的安家之处,从粗放管理转向精细优化。这次迁移,本质上是一次针对特定硬件(DM642 EVM)的深度适配与性能压榨过程,对于从事TI C6000系列DSP,特别是涉及视频处理的嵌入式开发来说,里面的坑和技巧都非常有代表性。
2. 核心思路与方案选型解析
2.1 视频通路的重构:从FPGA桥接到直连视频端口
在NVDK平台上,视频流路径是:视频源 -> 视频解码芯片 -> FPGA -> EMIF -> DSP。DSP通过Ateme的图形库API与FPGA交互,完成视频帧的抓取和回显。这个方案通用性强,但引入了FPGA这个中间层,增加了延迟和潜在的带宽瓶颈。
DM642的视频端口方案则是:视频源 -> 视频解码芯片(如SAA7115)-> VP0视频端口(捕获)-> DSP内部处理 -> VP2视频端口(显示)-> 视频编码芯片(如SAA7105)。这是一个点对点的直连架构。为什么选择这个方案?核心优势在于“专用”和“直接”。视频端口是DSP片上的外设,有专用的DMA控制器,能够以行/场中断的方式自动将视频数据搬运到指定的内存缓冲区,完全不占用CPU核心,对EMIF带宽的占用也极低。这对于需要处理多路高清(当时是D1分辨率)视频的运动检测应用来说,是保证实时性的基石。
因此,迁移的首要任务就是彻底剥离旧的Ateme图形库依赖,全面转向TI的Driver Development Kit (DDK) 1.1中提供的FVID驱动。FVID(Frame Video Driver)提供了一套统一的、基于IOM模型的API来管理视频端口,抽象了底层硬件细节,让开发者可以更关注业务逻辑。这个选择是必然的,因为它是TI为DM642视频子系统提供的官方、唯一的标准驱动方案。
2.2 内存架构的重新规划:256KB内部SRAM的生存法则
C6416 NVDK拥有1MB的片上SRAM,这让初代系统设计可以很“任性”:几乎所有的代码段、数据段、BIOS对象(如任务堆栈、信号量、队列)以及视频处理中间缓冲区,都可以一股脑地放在内部内存(ISRAM)里,以获得最快的访问速度。
但DM642只有256KB ISRAM,直接照搬必然爆掉。这里的设计思路必须转变:ISRAM是珍贵的战略资源,必须留给最需要、最频繁访问的数据和代码。我们的优化原则是:
- 速度优先:将计算最密集的算法所操作的中间处理缓冲区放在ISRAM中。在运动检测中,就是当前帧与参考帧做差分的那些Y、Cr、Cb分量缓冲区。
- 缓存友好:开辟一大块ISRAM(如128KB)配置为L2 Cache。这样,即使代码和部分数据放在外部慢速SDRAM中,也能通过缓存机制获得接近内部内存的访问性能。
- 外部化非核心资源:将操作系统(DSP/BIOS)的数据对象、大部分代码段、以及非实时的、大的数据缓冲区(如完整的视频帧缓冲区)全部移到32MB的外部SDRAM中。
- 精细化管理:利用链接器命令文件(.cmd)和DSP/BIOS的配置数据库(CDB),精确控制每一个内存段的归属。
这种“核心数据进ISRAM,代码与大缓冲进SDRAM+CACHE”的混合架构,是在有限资源下追求极致性能的经典做法。
3. 视频端口配置与FVID驱动实战
3.1 清理旧驱动与引入FVID
第一步是“拆旧”。在thrCapture.c、thrDisplay.c和thrProcess.c这几个核心线程文件中,移除所有对agl.h和iekc64.h头文件的引用。在链接器命令文件中,移除agl_c64.lib和iekc64_d.lib这两个Ateme图形库文件。同时,删除thrCapture.c中的openInput()、getInputFrame(),以及thrDisplay.c中的openDisplay()、putOutputFrame()等旧驱动函数。这相当于为新的视频通路清空了场地。
接下来是“建新”。FVID驱动的基本使用模式是:创建(FVID_create)、控制(FVID_control)、数据交换(FVID_exchange)、删除(FVID_delete)。
捕获线程(thrCapture.c)初始化示例:
// 包含必要的DDK头文件 #include <fvid.h> #include <evmdm642.h> #include <evmdm642_vport.h> // 在初始化函数中 FVID_Handle capChan; Int status; // 配置捕获通道参数,关键是指定内存段为外部堆(EXTERNALHEAP),因为帧缓冲区很大 EVMDM642_vCapParamsChan.segId = EXTERNALHEAP; // 配置SAA7115解码器参数,通过I2C控制 EVMDM642_vCapParamsSAA7115.hI2C = EVMDM642_I2C_hI2C; // 创建视频捕获通道。"/VP0CAPTURE/A/0"表示使用VP0口的A场(或通道0)进行捕获 capChan = FVID_create("/VP0CAPTURE/A/0", IOM_INPUT, &status, (Ptr)&EVMDM642_vCapParamsChan, NULL); if (capChan == NULL) { // 错误处理... } // 发送控制命令,配置前端解码器(EDC: External Device Control) FVID_control(capChan, VPORT_CMD_EDC_BASE+EDC_CONFIG, (Ptr)&EVMDM642_vCapParamsSAA7115); // 启动捕获通道 FVID_control(capChan, VPORT_CMD_START, NULL);这里的关键是EVMDM642_vCapParamsChan和EVMDM642_vCapParamsSAA7115这两个结构体。它们的详细定义在DDK的示例文件中,通常位于CCS安装目录\boards\evmdm642\examples\video\driver\settings下。你需要根据你的视频格式(如NTSC 720x480)选择合适的源文件(如evmdm642_vcapparamsNTSC.c)添加到你的工程中。一个常见的坑是忘记将这些配置文件加入工程,导致链接时找不到结构体定义。
3.2 视频数据流转与格式转换
在捕获线程的运行函数(thrCaptureRun)中,核心操作是FVID_exchange。这个函数是非阻塞的,它提交一个空闲缓冲区给驱动用于填充下一帧数据,同时返回一个已经填满数据的缓冲区给你处理。
// 假设 capFrameBuf 是包含帧数据的缓冲区指针 FVID_exchange(capChan, &capFrameBuf); // 此时,capFrameBuf 指向新捕获的帧数据从视频端口捕获的原始数据通常是YCrCb 4:2:2隔行格式。但许多图像处理算法(包括我们运动检测中的一些操作)在4:2:0格式上效率更高。因此,我们需要在捕获后立即进行一次转换:
// 定义输入输出缓冲区指针数组 Char *inBuf[3], *outBuf[3]; // 指向捕获的422数据 inBuf[Y] = capFrameBuf->frame.iFrm.y1; inBuf[CR] = capFrameBuf->frame.iFrm.cr1; inBuf[CB] = capFrameBuf->frame.iFrm.cb1; // 指向我们为处理分配的420缓冲区 outBuf[Y] = scombufCap->bufYCRCB[Y]; outBuf[CR] = scombufCap->bufYCRCB[CR]; outBuf[CB] = scombufCap->bufYCRCB[CB]; // 调用422到420的转换函数 yuv422to420(inBuf, outBuf, PROCF_WIDTH, CAPF_HEIGHT, CAPF_WIDTH);这里有个重要细节:PROCF_WIDTH(处理宽度)和CAPF_WIDTH(捕获宽度)可能不同。例如,我们可能捕获D1分辨率(720x480),但只处理其中的CIF区域(352x240)。转换函数需要知道源和目的的行跨度(line pitch,即每行数据的字节数),才能正确拷贝。CAPF_WIDTH就是捕获数据的行跨度。
显示线程(thrDisplay.c)是相反的过程。处理完的数据是YCrCb 4:2:0格式,需要转换回4:2:2格式,然后通过FVID_exchange提交给显示视频端口(VP2)。
// 将处理后的420数据转换回422,以便显示 inBuf[Y] = scombufDisp->bufYCRCB[Y]; inBuf[CR] = scombufDisp->bufYCRCB[CR]; inBuf[CB] = scombufDisp->bufYCRCB[CB]; outBuf[Y] = disFrameBuf->frame.iFrm.y1; outBuf[CR] = disFrameBuf->frame.iFrm.cr1; outBuf[CB] = disFrameBuf->frame.iFrm.cb1; yuv420to422(inBuf, outBuf, PROCF_WIDTH, CAPF_HEIGHT, PROCF_WIDTH); // 将填充好的显示缓冲区提交出去 FVID_exchange(disChan, &disFrameBuf);3.3 输出格式切换与多通道布局
原NVDK系统输出是VGA RGB格式。DM642视频端口更自然地支持BT.656标准的YCrCb输出,这省去了额外的RGB转换步骤,也更符合视频领域通用规范。切换输出格式很简单,只需修改显示参数结构体EVMDM642_vDisParamsSAA7105中的一个字段:
SAA7105_ConfParams EVMDM642_vDisParamsSAA7105 = { SAA7105_AFMT_SVIDEO, // 使用S-Video输出 // SAA7105_AFMT_COMPOSITE, // 或者使用复合视频输出 SAA7105_MODE_NTSC720, SAA7105_IFMT_YCBCR422_INTERLACED, TRUE, FALSE, INV };选择SAA7105_AFMT_SVIDEO或SAA7105_AFMT_COMPOSITE即可在S端子和复合视频之间切换。
对于多通道运动检测,我们需要在同一个显示画面上排列多个处理通道的结果。这涉及到复杂的缓冲区偏移计算。原来RGB模式下只有一个连续的缓冲区,现在Y、Cr、Cb三个分量需要分别计算偏移。我们定义了一个结构体和偏移量宏:
typedef struct placementBuff { Char *y; Char *cr; Char *cb; } placementBuff; // 假设将画面分为4象限,计算每个象限Y分量的起始偏移 #define Q1_Y_OFFSET 0 #define Q2_Y_OFFSET (OPF_WIDTH >> 1) // 半屏宽度 #define Q3_Y_OFFSET ((OPF_WIDTH * OPF_HEIGHT) >> 1) // 半屏面积 #define Q4_Y_OFFSET ((OPF_WIDTH * OPF_HEIGHT >> 1) + (OPF_WIDTH >> 1)) // Cr/Cb分量是Y分量大小的一半(4:2:0采样),偏移量也相应减半 #define Q1_CR_OFFSET 0 #define Q2_CR_OFFSET (Q2_Y_OFFSET >> 1) // ... 其他偏移类似在将缓冲区传递给处理单元(Cell)时,需要设置正确的起始指针:
placementBuff outputBuff; outputBuff.y = scombufDisp->bufYCRCB[Y] + Q2_Y_OFFSET; // 第二象限 outputBuff.cr = scombufDisp->bufYCRCB[CR] + Q2_CR_OFFSET; outputBuff.cb = scombufDisp->bufYCRCB[CB] + Q2_CB_OFFSET; // 通过ICC(Inter-Cell Communication)设置输出缓冲区 ICC_setBuf(chan->cellSet[CHDIFFCELLDIFF].outputIcc[0], &outputBuff, 0);这里的关键点是“行跨度”(linePitch)的处理。在处理单元内部,算法可能按一维数组方式处理数据。但在最终用DAT_copy2d拷贝到显示缓冲区时,必须考虑目标缓冲区实际的二维布局。行跨度就是二维缓冲区中一行的字节数。对于处理单元,其输出行跨度通常是处理宽度(PROCF_WIDTH);而对于最终拷贝到显示缓冲区的操作,行跨度是输出帧的宽度(OPF_WIDTH)。这个值需要作为环境变量传递给处理单元。
// 在执行通道前,设置行跨度 thrProcess.diffEnv->linePitch = OPF_WIDTH; // 传递给差分处理单元 // 在处理单元内部,使用DAT_copy2d进行拷贝,指定正确的行跨度 DAT_copy2d(DAT_1D2D, yOutBuff, outData[Y], PROCF_WIDTH, PROCF_HEIGHT, linePitch); DAT_copy2d(DAT_1D2D, crOutBuff, outData[CR], PROCF_WIDTH>>1, PROCF_HEIGHT>>1, linePitch>>1); // ... 等待拷贝完成如果行跨度设置错误,会导致图像在屏幕上倾斜、错位或者出现奇怪的条纹,调试时需要重点检查。
4. 内存优化与缓存策略深度解析
4.1 内部SRAM(ISRAM)的精细分区
面对256KB的ISRAM,我们必须像规划市中心地块一样精打细算。通过修改链接器命令文件(.cmd)和DSP/BIOS配置,我们可以手动划分这片区域。
一个典型的分区方案如下:
- L2 Cache(128KB):这是性能的倍增器。将ISRAM的一部分配置为缓存,可以显著加速对外部SDRAM中代码和数据的访问。在DM642上,L2 SRAM可以灵活配置为全映射缓存、部分缓存+部分SRAM,或全SRAM。我们选择将其配置为128KB的缓存。这通常在BIOS配置工具中设置,或者在
gel文件初始化时通过写缓存配置寄存器(CACHE_CFG)完成。 - 关键处理缓冲区(约数十KB):剩下的128KB SRAM中,我们划出大部分留给最核心的中间处理缓冲区。在运动检测中,就是
intYBuf,intCrBuf,intCbBuf这些用于帧间差分、滤波等实时计算的缓冲区。将它们放在ISRAM可以确保算法核心循环的访问是零等待的,这对维持高帧率至关重要。 - 内部堆(Internal Heap, 剩余部分):DSP/BIOS运行时需要一些内部堆空间来创建对象(如信号量、邮箱)。这部分不需要太大,几KB到十几KB即可,用于分配那些访问频繁的小型RTOS对象。
具体在.cmd文件中的体现可能是这样的:
MEMORY { ISRAM: origin = 0x00000000, length = 0x00040000 /* 256KB */ SDRAM: origin = 0x80000000, length = 0x02000000 /* 32MB */ ... } SECTIONS { .intYBuffer > ISRAM .intCrBuffer > ISRAM .intCbBuffer > ISRAM .internalHeap > ISRAM .text > SDRAM /* 代码段全部放到外部 */ .bss > SDRAM /* 未初始化数据段 */ .far > SDRAM /* 远数据段 */ .stack > SDRAM /* 系统堆栈 */ .data > SDRAM /* 初始化数据段 */ .const > SDRAM /* 常量段 */ .cio > SDRAM /* C I/O 缓冲区 */ ... }注意事项:.internalHeap段的大小需要在DSP/BIOS配置中明确设置。确保其足够分配你需要的BIOS对象,但又不至于挤占处理缓冲区的空间。一个技巧是,先在宽松配置下运行,通过BIOS的统计工具查看堆的使用峰值,然后再回来调整大小。
4.2 缓存一致性问题与CACHE API的运用
当ISRAM一部分用作缓存后,一个经典的嵌入式问题就出现了:缓存一致性问题。DMA(如视频端口、EDMA)会直接与内存交互,而不经过缓存。如果CPU修改了缓存中的数据但未写回内存,DMA读到的是旧数据;反之,如果DMA向内存写了新数据,但CPU缓存中还是旧数据,CPU就会读到脏数据。
在我们的运动检测系统中,有一个通过GEL(通用仿真器接口)脚本更新参考帧的功能。参考帧数据由外部工具(通过仿真器)直接写入SDRAM。由于这部分SDRAM区域可能已被缓存,CPU看到的可能还是旧的缓存内容。为了解决这个问题,必须使用CACHE API来手动维护一致性:
/* 假设 scombufCap 是捕获缓冲区,prevY, prevCr, prevCb 是存储在SDRAM中的参考帧缓冲区 */ /* 步骤1:将捕获缓冲区(可能在缓存中被修改过)写回并无效化其在L2中的缓存行 */ CACHE_wbInvL2(scombufCap->bufYCRCB[Y], CAPF_SIZE_IN_PIXELS, CACHE_WAIT); CACHE_wbInvL2(scombufCap->bufYCRCB[CR], CAPF_SIZE_IN_PIXELS>>2, CACHE_WAIT); CACHE_wbInvL2(scombufCap->bufYCRCB[CB], CAPF_SIZE_IN_PIXELS>>2, CACHE_WAIT); /* 步骤2:将数据从捕获缓冲区拷贝到参考帧缓冲区(使用DMA加速的DAT_copy) */ prevYId = DAT_copy2d(DAT_2D1D, (Void *) scombufCap->bufYCRCB[Y], (Void *) prevY, PROCF_WIDTH, PROCF_HEIGHT, PROCF_WIDTH); prevCrId = DAT_copy2d(DAT_2D1D, (Void *) scombufCap->bufYCRCB[CR], (Void *) prevCr, PROCF_WIDTH>>1, PROCF_HEIGHT>>1, PROCF_WIDTH>>1); prevCbId = DAT_copy2d(DAT_2D1D, (Void *) scombufCap->bufYCRCB[CB], (Void *) prevCb, PROCF_WIDTH>>1, PROCF_HEIGHT>>1, PROCF_WIDTH>>1); /* 步骤3:等待DMA拷贝完成 */ DAT_wait(prevYId); DAT_wait(prevCrId); DAT_wait(prevCbId); /* 步骤4:无效化参考帧缓冲区在L2缓存中的内容,确保CPU后续读取时从SDRAM获取最新数据 */ CACHE_invL2(prevY, CAPF_SIZE_IN_PIXELS, CACHE_WAIT); CACHE_invL2(prevCr, CAPF_SIZE_IN_PIXELS>>2, CACHE_WAIT); CACHE_invL2(prevCb, CAPF_SIZE_IN_PIXELS>>2, CACHE_WAIT);这里CACHE_wbInvL2和CACHE_invL2的区别至关重要:wbInv(Write-Back and Invalidate)先将缓存中已修改的数据写回内存,然后标记该缓存行无效。inv(Invalidate)则直接丢弃缓存行中的数据,下次访问时从内存重新加载。在DMA传输前后正确使用这些API,是保证系统稳定性的关键。
4.3 SDRAM的配置与代码搬迁
DM642 EVM板载32MB SDRAM,地址从0x8000 0000开始。我们需要将所有非核心的代码和数据都安排到这里。这包括:
- 所有编译器生成的段:
.text(代码),.data(初始化数据),.bss(未初始化全局/静态变量),.const,.far等。 - 几乎所有的DSP/BIOS对象段:
.trcdata,.sysdata,.objdata,以及各种任务堆栈(.taskstack)。
操作是在DSP/BIOS的图形化配置工具(CDB Editor)中完成的。你需要逐一检查每个“属性”(Property)页签,将其中“Memory Segment”下拉框从“Internal Memory”改为“External Memory”或你命名的SDRAM段(如SDRAM)。
一个极易遗漏的坑是:改变内存段后,相关的内存池(Memory Pool)或堆(Heap)的大小和位置也需要相应调整。例如,如果你将.text段移到了SDRAM,那么用于动态分配代码或数据的堆(如EXTERNALHEAP)也必须确保其基地址和大小在SDRAM的有效范围内,并且在链接命令文件中被正确定义和分配。
迁移完成后,由于代码在外部慢速内存中运行,初始性能会下降。这正是我们配置128KB L2 Cache的意义所在。CPU取指和访问数据时,会首先在缓存中查找,命中则全速访问,未命中才去访问SDRAM。对于循环密集的视频处理代码,缓存命中率通常很高,从而能将平均访问速度提升到接近ISRAM的水平。
5. 调试技巧与常见问题排查
5.1 视频端口无信号或花屏
这是移植初期最常见的问题。
- 检查物理连接与电源:确保视频解码器(SAA7115)、编码器(SAA7105)供电正常,视频输入/输出线缆连接正确。DM642 EVM板上的跳线设置(如果有)是否符合视频端口配置(如复合视频 vs. S-Video)。
- 验证I2C配置:视频编解码器的初始化是通过I2C总线配置其内部寄存器完成的。确保I2C驱动已正确初始化,并且
EVMDM642_I2C_hI2C句柄有效。可以使用I2C扫描工具或读取编解码器ID寄存器来验证通信是否成功。 - 核对FVID创建参数:检查
FVID_create调用中使用的设备名称字符串(如"/VP0CAPTURE/A/0")是否与硬件连接匹配。VP0通常用于捕获,VP2用于显示。A/0表示场A或通道0。 - 确认缓冲区尺寸与对齐:视频端口DMA对缓冲区地址和大小可能有对齐要求(如128字节对齐)。确保通过
FVID_exchange提交的缓冲区地址和大小符合驱动要求。使用MEM_align()函数来分配对齐的内存。 - 检查时钟与同步信号:使用示波器或逻辑分析仪测量视频编解码器输出的像素时钟(PCLK)、行同步(HSYNC)和场同步(VSYNC)信号是否正常,并确保DM642视频端口的配置(如时钟极性、同步方式)与之匹配。这些配置都封装在
EVMDM642_vCapParamsSAA7115等结构体中,务必从TI官方示例中拷贝正确的配置值。
5.2 内存错误与数据损坏
- 链接错误(Section placement fails):检查.cmd文件,确保所有定义的段(section)都被分配到了已定义的内存区域,且没有重叠。特别注意
.internalHeap这类在BIOS中配置大小的段,其长度必须与.cmd文件中分配给它的空间一致。 - 运行时内存越界:这是最难查的问题之一。症状可能是随机死机、图像出现随机噪点、或变量值被莫名修改。
- 工具辅助:使用CCS(Code Composer Studio)的内存查看器(Memory Browser),在疑似越界的缓冲区前后设置“内存访问断点”。如果某个函数写到了缓冲区之外,会触发断点。
- 代码审查:重点检查所有数组访问、指针运算,特别是涉及行跨度(
linePitch)和图像宽高的计算。一个像素偏移计算错误,在循环中就会被放大成千上万次越界写。 - 使用安全函数:尽量使用
DAT_copy这类经过优化的库函数进行内存拷贝,它们比手写的循环更安全、更快。
- 缓存一致性问题导致的数据“神游”:表现为处理后的图像时好时坏,或者GEL更新参考帧后算法不生效。
- 确认数据流:画一张数据流图,标明哪些数据是CPU产生/消费的,哪些是DMA(视频端口、EDMA)产生/消费的。
- 添加CACHE维护:在所有CPU与DMA共享的数据缓冲区进行数据交换的前后,严格按照“DMA写 ->
CACHE_inv”,“CPU写 ->CACHE_wb或CACHE_wbInv”的原则添加缓存维护操作。 - 简化调试:可以尝试暂时将L2配置为全SRAM(关闭缓存),如果问题消失,那基本可以断定是缓存一致性问题。
5.3 性能不达标
- Profile是关键:使用CCS的Profiler工具或DSP/BIOS的实时分析工具(如STS模块、LOG模块)来测量关键线程(如
thrProcess)的执行周期。找出最耗时的函数。 - 检查内存访问瓶颈:如果性能分析显示大量时间花在内存访问上,检查你的核心处理循环。
- 确保核心循环数据在ISRAM:使用
#pragma DATA_SECTION指令将核心循环用到的数组强制放到ISRAM段(如.intYBuffer)。 - 优化循环结构:使用编译器支持的
#pragma MUST_ITERATE提供循环次数信息,帮助编译器做更好的软件流水优化。展开内层循环。 - 使用内联函数和 intrinsics:对于像
_abs()、_sadd()、_ssub()等常用操作,使用TI提供的编译器内联函数(intrinsics)可以生成非常高效的汇编指令。
- 确保核心循环数据在ISRAM:使用
- DMA使用是否充分:图像数据的搬运(如格式转换、通道间传递)应尽量使用
DAT_copy2d(背后是EDMA),而不是用CPU的memcpy或循环拷贝。确保DAT_copy调用是异步的,并通过DAT_wait等待完成,这样CPU可以在DMA搬运数据时并行做其他计算。
5.4 从NVDK迁移的特定陷阱
- 宏定义和常量:NVDK和DM642 EVM的板级支持包(BSP)可能使用了不同的宏来定义视频尺寸、内存地址等。例如,
CAPF_WIDTH、OPF_HEIGHT这些值需要根据DM642 EVM支持的视频模式(如NTSC 720x480)重新检查定义。 - 中断向量表(IVT):DM642的中断映射可能与C6416不同。确保中断向量表正确配置,特别是视频端口捕获完成中断、显示完成中断等,它们是否正确连接到了DSP/BIOS的HWI(硬件中断)对象上。
- 外设时钟初始化:DM642的PLL(锁相环)配置、外设时钟分频可能与C6416不同。参考DM642 EVM的GEL文件或示例工程,确保系统时钟、EMIF时钟、视频端口时钟等初始化正确。错误的时钟会导致视频端口无法以正确速率采集数据,或者EDMA传输速度不匹配。
整个迁移过程,就像是在一台更精密但也更紧凑的新机器上,重新部署一套复杂的实时流水线。每一个环节——从视频数据的流入、处理到流出,再到内存中每一字节的摆放——都需要仔细考量。最终,当多路视频稳定地在DM642 EVM上实现实时运动检测并显示时,那种对系统资源完全掌控的感觉,是嵌入式开发独有的乐趣。这种针对特定硬件进行深度优化、在资源限制下榨取每一分性能的经验,对于处理其他嵌入式视觉项目,尤其是基于TI C6000系列DSP的平台,具有很高的复用价值。