4. VDMA原理与配置:Video Direct Memory Access工作原理、VDMA寄存器配置、帧缓存策略

各位同学,今天我们来聊聊VDMA。说实话,我刚接触Zynq的时候,觉得VDMA就是个高级版的DMA,没啥特别的。直到我在一个项目中,图像数据老是丢帧,查了三天才发现是VDMA的帧同步没配好。嗯,从那以后,我对VDMA就多了几分敬畏。

4.1 VDMA工作原理

VDMA,全称Video Direct Memory Access。说白了,它就是专门为视频数据设计的DMA控制器。普通的DMA搬数据,搬完就完事了。但视频数据不一样,它是一帧一帧连续来的,你得保证每一帧都能完整地搬到内存里,还不能丢帧。

VDMA的核心工作模式有三种:

  • 同步模式:VDMA和视频源同步工作,来一帧搬一帧。我习惯用这种模式做实时采集。
  • 异步模式:VDMA独立于视频源工作,适合做帧缓存。你想想看,如果采集和显示速度不一样,就得靠这个模式缓冲。
  • 自动模式:VDMA自动管理帧缓冲区的切换,说白了就是帮你省心。

我个人觉得,理解VDMA的关键在于搞清楚它和普通DMA的区别。普通DMA是单次传输,VDMA是循环传输。它会自动在多个帧缓冲区之间轮转,保证数据不冲突。

核心要点:VDMA本质上是一个带有帧缓存管理功能的DMA控制器。它通过内部的状态机,自动完成帧同步、缓冲区切换、中断产生等操作。

VDMA工作原理框图 视频源 (摄像头/HDMI) 数据流 VDMA控制器 帧同步管理 缓冲区切换 中断控制 AXI总线 DDR内存 帧缓冲区0 帧缓冲区1 读回数据 显示设备 (HDMI/LCD) 帧缓存策略 双缓冲:采集→缓冲区0,显示→缓冲区1,交替使用 三缓冲:采集、显示、处理各占一个缓冲区,避免等待

4.2 VDMA寄存器配置

配置VDMA,说白了就是写寄存器。但VDMA的寄存器挺多的,我刚开始看的时候也头大。后来我总结了一个规律:你只需要关注三类寄存器。

寄存器类别 寄存器名称 功能描述
控制类 S2MM_CONTROL 启动/停止VDMA传输,配置中断
地址类 S2MM_START_ADDR 设置帧缓冲区的起始地址
参数类 S2MM_VSIZE / HSIZE 配置图像的行数、每行字节数

我记得第一次配置VDMA时,忘了设置行宽对齐。结果图像显示出来全是斜的。后来才知道,VDMA要求每行数据的字节数必须是4的倍数。你想想看,如果图像宽度是640像素,RGB888格式,每行就是1920字节,刚好对齐。但如果是640x480的灰度图,每行640字节,就不对齐了。

我的经验:配置VDMA时,先配控制寄存器,再配地址寄存器,最后配参数寄存器。顺序错了,VDMA可能不工作。

下面是一个典型的VDMA配置代码片段:

// 配置VDMA写通道(S2MM)
Xil_Out32(VDMA_BASE + 0x00, 0x00000003);  // 复位并启动VDMA
Xil_Out32(VDMA_BASE + 0x5C, 0x00000000);  // 清除所有中断
Xil_Out32(VDMA_BASE + 0xAC, 0x00000000);  // 配置帧延迟

// 设置帧缓冲区地址
Xil_Out32(VDMA_BASE + 0x0C, FRAME_BUF_0);  // 帧缓冲区0
Xil_Out32(VDMA_BASE + 0x10, FRAME_BUF_1);  // 帧缓冲区1
Xil_Out32(VDMA_BASE + 0x14, FRAME_BUF_2);  // 帧缓冲区2

// 配置图像参数
Xil_Out32(VDMA_BASE + 0x50, 480);          // 垂直尺寸(行数)
Xil_Out32(VDMA_BASE + 0x54, 1920);         // 水平尺寸(每行字节数)
Xil_Out32(VDMA_BASE + 0x58, 1920);         // 行跨距(对齐后的字节数)

注意:行跨距(STRIDE)和水平尺寸(HSIZE)不一定相等。如果图像做了对齐填充,STRIDE会大于HSIZE。我曾经在这个坑里栽过跟头,图像显示出来有黑边,就是因为STRIDE设错了。

4.3 帧缓存策略

帧缓存策略,说白了就是怎么管理内存里的那几个缓冲区。我见过三种常见的策略:

  • 单缓冲:只有一个缓冲区。简单,但容易出问题。采集和显示共用一块内存,你写的时候我在读,画面就会撕裂。
  • 双缓冲:两个缓冲区交替使用。一个用于采集,一个用于显示。这是最常用的方案。我大部分项目都用这个。
  • 三缓冲:三个缓冲区轮转。采集、显示、处理各占一个。适合需要图像处理的场景,比如做边缘检测。

我个人习惯用双缓冲。为什么呢?因为三缓冲虽然流畅,但占内存多。对于1080p的RGB图像,一帧就要6MB左右。三缓冲就是18MB,对DDR压力不小。

帧缓存切换的时机也很关键。VDMA会在每一帧结束时自动切换缓冲区。但你要注意,切换是有延迟的。我曾经在一个项目中,采集帧率是60fps,显示帧率也是60fps,但VDMA切换缓冲区需要几个时钟周期。结果就是偶尔丢一帧。后来我加了帧同步信号,才解决这个问题。

避坑指南:我曾经在帧缓存切换时,没有等VDMA完成当前帧的传输就去读数据。结果读到的是一半旧帧一半新帧的混合数据。正确的做法是:等VDMA产生帧完成中断后,再切换缓冲区。

嗯,关于VDMA的原理和配置,今天就讲到这里。帧缓存策略的选择,说白了就是看你的应用场景。实时性要求高,就用双缓冲或三缓冲。内存紧张,就用单缓冲,但要做好同步。记住一点:VDMA不是万能的,但它用好了,能让你的图像采集系统稳定运行。

可可精品资料库