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控制器。它通过内部的状态机,自动完成帧同步、缓冲区切换、中断产生等操作。
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不是万能的,但它用好了,能让你的图像采集系统稳定运行。