首页 / 资讯中心 / 文章详情

Zynq视频通路搭建实战:VDMA、VTC与AXI4S-VideoOut关键要点解析

Zynq视频通路搭建实战:VDMA、VTC与AXI4S-VideoOut关键要点解析 ★ FEATURED ARTICLE
如果你和我一样是从裸机点个灯、跑个串口开始碰Zynq那视频通路大概是第一道真正把PS和PL同时拉上战线的坎。VDMA、VTC、AXI4S-VideoOut这三个IP的名字看起来很直白一个搬运数据、一个管时序、一个把流格式转成视频输出但真正在Vivado里连线时你会发现它们之间的时钟相位、复位极性、帧同步信号任何一处接错出来的画面就是黑屏、花屏或者干脆卡死在某个分辨率上。这篇就当是我的私人搭建笔记从Block Design新建到导出XSA给Vitis也就是SDK用把几个文档里含糊带过但实测定生死的关键点一次讲透。1. 视频通路的角色拆分VDMA为什么是核心VTC和AXI4S-VideoOut又是干嘛的1.1 从一路视频数据的流动讲起先说一条最简单的视频链路PS那边把一帧图像数据写到DDR里PL这边需要把它读出来拼成一帧完整的画面按显示器的时序送出去。这里面其实涉及三次数据格式的转换DDR里的数据是线性排列的像素字节通常按行连续存放要送给显示器的信号则是带有HSYNC、VSYNC、DE、像素数据的并行时序信号中间还需要有足够快的搬运通道让PS无需逐像素干预数据就能自动地从DDR流到输出接口。VDMA解决的是第一个问题它能按ARM传入的帧基地址、行步长、分辨率和像素格式把DDR里一段二维的帧数据变成一维连续的AXI4-Stream数据流。所以VDMA本质上是一个带描述的DMA引擎而不是简单的流式搬运工。VTC解决的是第二个问题它负责产生或解析视频时序。当我们要驱动外部屏幕时必须按照VESA或厂商规定的sync极性、前肩、后肩、消隐期、有效期来摆放每一行和每一帧的起始点。VTC可以工作在Generator模式自己产生这些时序也可以工作在Detector模式从外部输入的视频流中提取时序参数。在大多数Zynq视频输出设计中VTC被设置为Generator模式用来配合AXI4S-VideoOut工作。AXI4S-VideoOut解决的是格式转换的最后一个环节它接收AXI4-Stream的像素字节流按照配置好的视频格式解析出R、G、B分量对齐到VTC给出的DE、HSYNC、VSYNC上输出给外部编码器或直接接到HDMI/DP芯片。它内部有个小FIFO用来缓冲跨时钟域的流数据。1.2 VDMA vs AXI DMA帧缓存与2D地址经常有人问为什么不直接用AXI DMA去搬运视频。AXI DMA处理一维线性数据是非常高效的但它不知道视频帧是什么。视频帧有行、有列、有步长而且通常需要两个或三个缓冲交替使用避免撕裂。VDMA则直接把这种“二维帧”的引脚暴露出来ethernet模块不它多了一组寄存器用于配置帧地址、行步长和帧同步。在硬件层面VDMA的MM2SMemory Map to Stream通道会按照编程的帧地址和Row Pitch把每行数据连续读出来组成AXI4-Stream。配合下划线不需要。关键是它还能产生帧完成中断让软件知道什么时候可以写入新一帧数据。所以我通常会给客户或同事画一个更直接的对比表特性AXI DMAVDMA数据组织一维线性二维帧结构支持帧缓冲行步长必须自己拆解硬件通过寄存器控制帧同步需要外部逻辑内置帧同步、帧中断多缓冲需软件参与支持可编程的帧地址切换适用场景高速流式搬运视频帧缓存、图像处理这个差异在真正做视频时非常关键。比如输出1080p60每帧约200万个像素每像素2字节一帧就是4MB。如果某一帧读到一半软件已经把下一帧写到同一块内存画面就会撕裂。VDMA通过帧地址可编程可以让它自动在多个缓冲地址之间循环软件只需在帧中断里更新下一帧地址虽然还需要一些同步策略但硬件已经帮了80%的忙。1.3 VTC它提供的不是像素是时序心跳VTC这个名字容易被低估尤其是我们在学校做数字电路实验时可能也用过简单的计数器来产生H/V同步。但实际视频标准要求的时序并不只是一个计数就能搞定的。拿1080p60来说一行的总周期是2200个像素时钟其中有效像素是1920前面有88个像素的H Front Porch后面有148个像素的H Back Porch再加上44个像素的H Sync一帧总共有1125行有效行是1080帧前肩4行帧后肩36行帧同步5行。这种极其精确的时序用手写Verilog不是不可以但改一个分辨率就得重算一遍而且容易遗漏边界条件。VTC的作用就是把这些时序参数全都做成寄存器软件可以像查表一样改变水平、垂直前肩后肩、同步脉冲宽度以及极性。它内部有对应的行/帧计数器按像素时钟开始计数输出active_video即DE、hsync、vsync和field_id。如果外部需要一行一行的控制它也可以工作在Detector模式下解析出这些参数来。在硬件搭建阶段我们需要做的几件事主要是确定VTC工作在Generator模式并且把它的时钟和输出连接给AXI4S-VideoOut。1.4 AXI4S-VideoOut从流格式到并行像素AXI4S-VideoOut在Xilinx IP命名里全称是AXI4-Stream to Video Out它的输入是AXI4-Stream输出是一组带时序关系的并行视频总线。它内部会解析S_AXIS上的tlast、tuser把一帧一帧的数据正确识别出来。假如VDMA送过来的流没有按照帧边界产生tlast这个IP就会一直等在那画面不会输出。这就是为什么硬件连线时要把tuser和tlast接对。另外需要注意AXI4S-VideoOut并不主动产生任何时序。它可以接收VTC输出的vtc_video信号或者自己内部生成一个简单的video信号当不接VTC时。实际项目中我们可以选择让AXI4S-VideoOut在内部生成时序但灵活性很低因为那些前肩后肩参数并不可调。所以典型的做法就是让它依赖外部VTC提供的vtc_video、vtc_hsync、vtc_vsync来对齐输出。这样FPGA里解决了一大半时序问题SDK里只需要配置寄存器改动分辨率也相当友好。2. 动手前的系统规划PS端接口选择与PL侧时钟复位分配2.1 Zynq PS侧需要哪些通路很多人搭Block Design的时候习惯性把Zynq PS的所有接口都拉出来能勾的都勾上但视频链路其实只需要三类M_AXI_GP0/GP1用于PS访问VDMA的控制寄存器配置帧地址、启动传输、读取状态S_AXI_HP0/HP1/HP2/HP3用于VDMA的MM2S和S2MM通道去读写DDR。如果你只用VDMA做输出那至少需要一条HP接口作为读端口连接VDMA的M_AXI_MM2SDDR的地址映射HP口访问的是DDR的物理地址和CPU看到的一样。这个不需要额外配置但要在地址编辑器中给VDMA的读写端口分配范围建议统一放在DDR地址空间里比如0x00000000往上。在这个规划阶段我踩过最明显的一个坑是把VDMA的M_AXI_MM2S接到了GP0上。GP口带宽有限而且延迟大即使能工作跑1080p60时也会因为DDR仲裁问题导致偶发丢帧。HP口是为高带宽外设设计的它会有比较深的缓冲和独立的FIFO实测用HP口帧率高很多。所以从硬件设计阶段就要明确控制走GP数据走HP。2.2 时钟方案的来源视频链路里最少有两种时钟一是AXI总线的时钟用于VDMA读写寄存器和搬运数据二是像素时钟用于VTC和AXI4S-VideoOut产生输出时序。这两个时钟往往是不同频率的。具体来说VDMA挂在某个AXI总线上该总线的时钟通常来自Zynq PS的FCLK_CLK0或FCLK_CLK1一般配置为100MHz或150MHz。VDMA内部有两个时钟域一个是S_AXI_LITE时钟用于寄存器访问一个是M_AXI_MM2S时钟用于数据搬运通常我们会把这两个都连接到同一个FCLK上省去跨时钟域的额外FIFO。像素时钟则是根据你要输出的视频格式决定的。比如720p60的像素时钟是74.25MHz1080p60是148.5MHz有些标准是148.5/1.001≈148.35MHz。这个像素时钟不能随便用FCLK分频产生因为FCLK本身精度有限。更常见的做法是使用Zynq PL侧的pl_clk或专门的外部时钟芯片但在调试时也可以用一块MMCM/PLL从100MHz经PLL锁出精确的148.5MHz。在Block Design中我习惯用Clocking Wizard这个IP来生成像素时钟这样不用打开两个不同的时钟源。重点是VTC和AXI4S-VideoOut必须使用同一个像素时钟。这是很多人忽略的。如果你给VTC的时钟是74.25MHz却给AXI4S-VideoOut接了一个75MHz甚至FPGA内部自己生成的74MHz最终画面会出现行不同步、颜色错位、甚至完全没有输出的现象。因为VTC按照74.25MHz计数产生时序AXI4S-VideoOut每个clk周期采样并输出一组像素两边频率不一致时序就对不齐了。2.3 复位的处理视频链路的复位是整个设计里看起来最简单、实则最容易让人抓狂的地方。这三颗IP的复位信号极性必须和上游驱动保持一致。通常Vivado IP核的复位引脚默认是高有效还是低有效可以在Configuration界面里设置。我的习惯是把所有IP的复位极性统一成“低有效”也就是reset_n并且用一个统一的Processor System Reset IP来生成。这个IP会根据PS的FCLK_CLK0生成peripheral_aresetn它能保证所有外设复位信号与时钟同步释放这是避免“复位撤太快”或者“时钟未稳定就复位”这种情况的关键。另外还有一个小细节复位释放顺序。在配置VTC时通常需要先让VTC的计时器与像素时钟同步然后再释放AXI4S-VideoOut的复位。如果同时释放有时会因为起始计数器未对齐导致DE与HSYNC错开半行。这里我们不一定要在硬件上做出延迟更好的做法是在软件初始化时按顺序操作先复位VDMA再配置VTC最后打开AXI4S-VideoOut。硬件上只需要保证模块复位信号都有效具体时序靠软件控制。3. Block Design中的关键连线与参数一步一步搭起来3.1 新建Block Design与添加Zynq PS核无论你是用Vivado的IP Integrator还是用Tcl脚本流程都一样。新建Block Design后首先添加Zynq UltraScale MPSoC或Zynq-7000的PS核。这里要记得双击配置PS核在PS-PL Configuration中使能两个接口S_AXI_HP0_FPD或HP0和M_AXI_GP0使能UART用于调试在Clock Configuration里使能FCLK_CLK0频率设成100MHz或150MHz作为大部分AXI总线的时钟如果你希望使用PL侧MMCM来生成像素时钟可以在同一个时钟配置里额外输出一个FCLK_CLK1或直接把FCLK_CLK0频率设成像素时钟的倍数然后通过MMCM生成像素时钟。这种规划看起来简单但对后期调试影响很大。我习惯把FCLK_CLK0设成100MHz用MMCM倍频到像素时钟然后输出给VTC和AXI4S-VideoOut。这样VDMA的寄存器访问时钟和DDR AXI时钟保持在100MHz同时像素时钟独立锁相两者互不干扰。3.2 VDMA的参数配置VDMA参数里最常改的是以下四个Stream Data Width每个AXI4-Stream周期传输的像素数据位宽。常见的是32位或64位。如果你处理的图像是RGBA888832bpp一个周期就可以传一个像素如果是RGB88824bpp或者YCbCr422也可以选32位但会略浪费。我这里选32位直接把AXI4S-VideoOut的宽度也配成32位方便调试。MM2S Burst Size建议设置为16或32这和AXI总线的数据宽度有关。太大会浪费DDR带宽太小会增加频率开销。16是官方示例常用的值。Frame Buffers至少选3这样方便实现三缓冲减少来回覆盖的等待。硬件上多几个地址寄存器成本很小。Enable S2MM Channel如果你只做输出可以关掉S2MM减少资源占用但如果以后要做采集或显示建议一并打开因为后续改会牵动地址编辑器和中断。VDMA的端口连接S_AXI_LITE连接到PS的M_AXI_GP0映射到0x40000000之类的地址M_AXI_MM2S连接到PS的S_AXI_HP0_FPD或HP0地址空间映射到DDR起始地址M_AXI_S2MM若启用也连接到同一个或另一个HP口mm2s_introut和s2mm_introut连接到一个concat_irq再接到PS的IRQ组这样SDK里能接收帧中断。一个值得注意的地方是S_AXI_LITE/M_AXI_MM2S的时钟接法。VDMA IP内部的时钟域是分开的S_AXI_LITE和M_AXI_MM2S虽然属于两个AXI接口但通常我们接同一个FCLK_CLK0这样写寄存器地址和搬运数据都在同一个频率域里逻辑简单也没有频率匹配问题。如果你用在异构系统里把两者用在不同频率就必须在Block Design里加interconnect这反而会引入多一跳的延时。3.3 VTC的参数配置VTC的配置要看使用场景。在输出通路中必须把Video Timing Controller配置为Generator模式并打开Enable Vertical Blanking和Enable Horizontal Blanking等选项。这些是可选的但建议打开因为VTC在内部除了产生active_video还需要同步生成对应hsync、vsync。如果不开启可能只有active_video有效而其他信号永远处于低电平输出就没有同步。VTC内部的时序参数在硬件阶段一般不用写死它有一组寄存器可以通过AXI LITE接口由软件配置。所以我们只需要在Block Design里给VTC的ctrl接口即AXI LITE分配地址并让它的clk连接到像素时钟resetn连接到系统复位信号。但有一项必须在硬件阶段就明确VTC的gen_clkGenerator时钟与vtc_video,vtc_hsync,vtc_vsync的连接方式。通常VTC的输出直接接到AXI4S-VideoOut对应的vtc_video、vtc_hsync、vtc_vsync端口上这样AXI4S-VideoOut就能按时序把流数据输出。3.4 AXI4S-VideoOut配置AXI4S-VideoOut的配置分为两步。第一步选择输入AXI4-Stream数据格式。Xilinx IP有AXI4S Component对话框里面要设置Data Format是RGB还是YUV以及每个分量的位宽。如果你是输出到HDMI通常会将数据从RGB或YUV转换成HDMI的24位或36位。这里更直接的做法是选择AXI4-Stream Master不AXI4S-VideoOut是SLAVE接口它接收数据流配置中有一项Video Data类型我一般选RGB888然后数据位宽设为32位含一个未用的Alpha或高位填充这样和VDMA的32位流宽度匹配。第二步选择时序来源。在IP配置里有一个选项是“Include Internal VTC”或“External VTC”。默认是External我们要将它保持为External。这样才能让VTC的时序直接驱动它。如果误设成Internal它内部会用一个简易计数器生成时序很可能和外部VTC冲突出现显示不出来的情况。端口连接S_AXIS连接到VDMA的M_AXIS_MM2Sclk连接到像素时钟resetn连接到复位vtc_video,vtc_hsync,vtc_vsync连接到VTC的对应输出video_out组包括vout_pclk、vout_hsync、vout_vsync、vout_de和vout_data这些最终引出到顶层接HDMI或DisplayPort芯片。注意vout_pclk可以直接输出像素时钟也可以和clk是同一个但在接外部编码器时通常会再经过一个ODDR或IO buffer这不在本篇讨论范围内。3.5 连接验证与地址映射把上述IP都放在Block Design里之后点击“Validate Design”并不会直接告诉你是否有逻辑错误但会检查是否存在未连接的端口或非法连接。一个常见问题是没有给AXI LITE接口分配地址导致验证失败或软件无法访问。在Address Editor中一定要把下列接口分配到合适地址否则后续硬件导出xsa后软件根本找不到设备VDMA_S_AXI_LITE—— 例如0x40000000VTC_ctrl—— 例如0x40001000AXI4S_VideoOut_ctrl—— 例如0x40002000。这些地址写在同一个页面上SDK就会自动根据地址映射生成驱动程序。如果你的地址有重叠或范围不足点“Auto Assign”可以解决但Auto Assign会把它们分配在PS外设后面也还不错。有一点要注意如果地址分配时把VDMA数据的MM2S端口也分配到了某个非DDR地址比如0x80000000而DDR实际在0x00000000那VDMA读DDR就会出错。所以MM2S端口一定要映射到DDR地址范围0x00000000~。我见过不少新手把这端口在地址编辑器里当成外设来分配结果自然是搬运出来的数据全是0。验证通过后再连接顶层引脚。这一步就是把video_out的信号引到芯片IO。在Block Design里可以右键这些输出引脚选择Make External在顶层它会生成video_out_pclk_0等端口。后续可以在XDC里约束到对应的物理引脚上。4. VTC与AXI4S-VideoOut协作中的时序匹配为什么波形明明看着对却还是没有画面4.1 像素时钟从哪里来这个问题看似简单实际却很容易埋雷。很多参考设计里像素时钟直接由FCLK_CLK0提供或由MMCM从FCLK_CLK0生成但关键是MMCM的输入时钟必须稳定而且要确认MMCM复位逻辑不会在初始化过程中导致像素时钟短暂丢失。如果使用Clocking Wizard来生成像素时钟建议把它配置成输入时钟FCLK_CLK0频率100MHz输出时钟像素时钟比如148.5MHz自动锁定状态下等locked信号拉高后再释放各IP的复位。在Block Design里经常会看到有人把locked信号直接空着或者接到非同步复位端。更好的做法是让Clocking Wizard的locked信号接回到Processor System Reset的dcm_locked或aux_resetin以保证只在时钟锁定后释放系统复位。否则复位释放时MMCM还没锁定输出的像素时钟是一段乱跳的时钟VTC的计数器初始状态就会莫名其妙。4.2 同步信号与de、vs、hs的极性VTC产生的hsync、vsync极性是可以配置的。通常HDMI或VGA标准里hsync、vsync有时需要高有效有时低有效取决于具体的显示模式。AXI4S-VideoOut会原封不动地传递vtc_hsync和vtc_vsync所以如果你的XDC或外部接口要求极性相反应该在VTC寄存器里配置等于是软件里设置而不是在硬件上加反相器。常见的一个坑是在使用VTC的默认图样测试时你可能会直接看到一个花屏但用示波器去看vout_hsync却是正常的。这时候要考虑极性。比如某些显示器在低有效同步时正常工作而VTC默认输出的是高有效。解决办法很简单向VTC写入寄存器时把h_polarity和v_polarity配置为0或1即可。在硬件Block Design中虽然这不影响连线但如果你要在上板之前先用仿真查看也可以在IP配置里设置默认极性。4.3 VTC内部如何在锁定模式与自由运行模式下切换VTC有一个非常容易被忽视的“锁定模式”。在Generator模式下它完全可以自由运行也就是自己跑时序但在某些设计里允许它从外部视频源检测同步并锁定。我们要用的是自由运行模式否则它会试图跟随AXI4S-VideoOut输入的vtc_video不检测锁定模式需要额外的输入det_clk和一些det接口。如果我们在Block Design中错误地添加了Detector的接口或者是把VTC的det接口误连到了某个固定电平那么VTC可能永远处于等待锁定状态从而不产生任何输出。所以在IP配置里务必选择Generator Only不要选Generator and Detector。选后者虽然多了一组检测接口但实际用不到还可能因为未连接这些输入而把输出卡在低电平。为了避免这种晦涩问题我在搭建时只选Generator Only这样引脚干净逻辑也更清楚。4.4 常见配置误区FIFO不足、fid连接错误AXI4S-VideoOut内部有一个FIFO用于缓冲从AXI4S进来的数据直到VTC的下一个有效信号来临时开始输出。如果FIFO深度不足而VDMA平均写入带宽略低于像素时钟所需的带宽就会在每一帧中间出现“断流”表现就是画面下部黑屏或滚动条。解决方向有两个一是把VDMA的突发长度调大提高DDR读取效率二是在AXI4S-VideoOut配置里增大FIFO深度如果有这个选项或者降低输出分辨率/帧率以匹配带宽。另一个容易被忽略的信号是fidField ID。在隔行扫描模式下它用于标记当前是奇数场还是偶数场。如果在逐行扫描模式下使用某些IP会把fid当作无定义信号但在极少数情况下AXI4S-VideoOut的寄存器里如果写错了一个字段它可能仍会去采样fid导致时序错乱。我的建议是把所有fid相关信号直接接到0并且在SDK初始化时把interlaced模式设为0。这样能避免很多莫名其妙的隔行/逐行切换问题。5. 从Vivado导出到SDK前必须做的几项检查以及实测中的坑5.1 确认Inverting polarity pinsHSYNC、VSYNC的极性导出XSA之前我强烈建议打开Vivado的Schematic视图检查引出到顶层的video_out_pclk、vout_hsync、vout_vsync的端口方向是否和你想要的方向一致。如果这些信号最终接的是一个低电平有效的接口比如某些HDMI芯片的DE反相不要直接改Block Design里的逻辑而应该在XDC约束中为这些引脚添加IO_INVERT或ENDED属性更严谨的做法是在外部接口上加一级反相或者通过set_property INVERT_ENABLE TRUE不过实际中HDMI芯片的DE通常高有效所以普遍不需要反相。这块在最初设计硬件原理图时就该确认。在XSA导出前打开外部接口的数据手册把极性、电平、上下拉都对照一遍否则等板子回来就晚了。5.2 地址计算与帧缓冲器分配SDK中要让VDMA工作最关键的是给它三个帧缓冲地址。DDR的物理基地址在Zynq中通常是0x00000000但PS的DDR也可能映射到0x00100000或其他位置取决于DDR大小和地址分配。最稳妥的办法是先读取reg base配置或在SDK的xparameters.h中看到DDR的起始地址。然后为每帧分配足够大的连续空间例如一帧1080p RGB888需要192010803≈6.2MB三帧就需要约18.6MB。如果你还有别的程序或OS占内存需要规划好避免VDMA把代码段给覆盖了。我在调试时喜欢把第一帧地址设为DDR基地址第二帧基地址0x8000008MB第三帧0x100000016MB。如果你的分辨率较小也可以把间距缩小。但要注意Cache问题如果PS CPU在写这些帧缓冲区时开启了Cache而VDMA读取的是DDR的真实内容那么CPU写入的数据不会立即可见。在Vitis/SDK中需要在写入数据后调用Xil_DCacheFlushRange(addr, size)或者直接把缓冲区放在Cache禁用区域。这是一个让很多新手冒汗的步骤因为硬件连线全对画面却始终是黑的。5.3 实测中的黑屏/花屏排查路径如果视频通路搭建完成后上电显示器没有图像别急着改代码。我排查的顺序是这样的确认像素时钟是否产生。用示波器看vout_pclk如果没有波形查Clocking Wizard的锁定状态、输入时钟以及PS是否启动。确认VTC是否在输出同步信号。看vout_hsync、vout_vsync的波形。如果同步信号正常至少说明VTC工作在自由运行状态问题可能在数据通路。确认VDMA是否在搬运数据。在SDK里读取VDMA的MM2S_DMACR/MM2S_SR检查是否有错误、是否处于运行状态。同时设置一个很小的分辨率比如640x480用固定颜色填充帧缓冲如果画面能显示纯色说明硬件通路基本通了。确认DE与数据是否对齐。如果出现彩色雪花或横向噪点多半是tuser或tlast对齐有问题或者VDMA的行步长和AXI4S-VideoOut的行长度不一样。此时检查VDMA的Line Buffer Pitch配置必须等于图像行字节数且对齐到4字节。比如192035760已经是4的倍数这没问题但如果是128033840也OK。如果行字节数不是4的倍数必须填充步长。确认寄存器地址和中断是否配置正确。可以通过串口打印VDMA状态寄存器的bit例如Irq overrun或Buffer underflow。这两个是最常出现的错误前者表示上一帧还没读完就被覆盖后者表示读到一半数据断了。解决方法分别是增大帧间隔和优化DDR带宽/Burst。另外有一种特别奇怪的情况显示器显示“No Signal”但示波器显示HSYNC、VSYNC都存在。这个我遇到过原因是vout_deDE信号一直为低。DE在VTC里对应active_video如果VTC配置的分辨率和AXI4S-VideoOut内部期望的分辨率不一致它可能不会产生有效的DE。比如VTC生成的是1080p60时序但AXI4S-VideoOut被配置成720p那么即使同步信号正确有效区域的采样也不会触发。所以要保证两者在分辨率上严格一致。5.4 一个小技巧先用测试Pattern验证时序最有效的初步验证方式不是直接把VDMA的流数据接到输出而是先用Vivado自带的一个简单逻辑替代VDMA生成纯色或彩条。可以在Block Design里临时加一个Video Test Pattern Generator它输出8/10位YUV或RGB接到AXI4S-VideoOut的输入。这样先验证VTC输出部分是否正常。如果测试Pattern能正常显示再把VDMA的数据接回来问题就锁定在VDMA/DDR侧。这个技巧能帮你把链路一分为二省掉很多后端与前端之间的扯皮。我们项目中最终在video_out后端接了一个Silicon Image的HDMI驱动芯片。测试Pattern阶段我用一种争吵最少的方式——在SDK里写一个循环往三个帧缓冲里填红色然后启动VDMA。屏幕上出来纯红色画面的时候整个通路的80%已经确认没问题了。之后主要就是色彩格式、分辨率缩放和应用层的帧管理。纯红色调试画面的代码大概就几行初始化VDMA、设置帧地址和行步长、设置分辨率、启动传输、把帧缓冲fills成红色。只要这一步通后面做OSD或视频叠加就很有底了。6. 写在最后这套硬件搭建之外还有哪些值得养成的好习惯视频通路从硬件搭建到出画本质上是一个排查链路的过程。我见过太多人一上来就在SDK里写大段复杂的图像处理结果只因为Block Design里一根clk没有连对就浪费了整整一天。所以我个人的经验是第一次做的时候千万不要追求一步到位而是把它拆成几个必须阶段先把PS和PL时钟复位配好再让VTC自己产生同步信号并通过测试Pattern验证最后再接VDMA和DDR。每加一级就验证一次这样无论哪一步出问题范围都极小。到了SDK阶段另一个值得好好思考的是帧缓冲区与Cache的关系。很多人显示不出来不是硬件搭建失败而是CPU写帧缓冲之后没有刷新CacheVDMA读到的是旧数据。这也是为什么我先做纯色填充测试因为如果纯色都出不来基本就是硬件/寄存器问题如果纯色能出来改图片又不显示十有八九是Cache和一致性。再一个经验是不要过度信任IP配置窗口里的默认值尤其是AXI4S-VideoOut的Data Format和Frame Width/Height。这些默认值往往来自一个示例工程不一定适合你的显示分辨率。导出到SDK后一定要在初始化代码里再次配置H_ACTIVE,V_ACTIVE,H_FRONT_PORCH,H_BACK_PORCH,H_SYNC,V_FRONT_PORCH,V_BACK_PORCH,V_SYNC所有的时序参数这相当于让软件接管整个TIming Control也是这套架构设计最灵活的地方。硬件搭建只是“舞台”SDK才能让舞台上演戏。这块搞下来之后再回头看当初启动时的困惑其实很清晰地发现VDMA是搬运工VTC是导演AXI4S-VideoOut是演员前面的提词器三者缺一不可但又不互相越界。把这三者的责任边界理清楚硬件搭建流程就能稳定复用。以后想从1080p改到4K或者在中间加一个Scaler、色彩空间转换模块只要时钟和复位结构不变整体框架是不需要大动的。这一篇的硬件搭建部分到这里就结束了下一篇我会整理SDK里的Bare-metal驱动代码具体聊聊怎么往VDMA的帧缓冲区里塞一帧BMP图片并把它稳定地按60fps刷新到屏幕上去。
阅读完成 · 觉得有帮助?
咨询建站