篇10:不用DDR也能玩视频------FPGA无帧缓存显示方案全解析
上一篇我们讲了用DDR做帧缓冲,性能强大,但不是每个项目都负担得起DDR的成本和布线复杂度。
如果你只需要显示简单的UI、传感器数据、或者来自低速接口的图像(如SPI摄像头、UART图片),或者你用的FPGA资源紧张(没有DDR控制器,也不想外挂DRAM),那么无DDR方案 可能是更务实的选择。
这一篇,我会带你分析在不使用DDR的情况下,FPGA如何实现视频显示,包括几种可行的架构、各自的资源要求、开发中的坑,以及什么时候该放弃这个方案。
一、为什么考虑"无DDR"?
| 优势 | 说明 |
|---|---|
| 成本降低 | 省去DDR芯片、PCB走线、终端电阻,BOM减少2~5美元 |
| PCB简化 | 不需要DDR的等长布线、参考平面、VREF,4层板可能变2层板 |
| 功耗降低 | DDR3/DDR4的IO功耗(约每GB/s 100~200mW)和省去PHY功耗 |
| 启动快 | 无需DDR初始化、训练(training)过程,上电即用 |
| 可靠性高 | 减少一颗外部芯片,减少失效点 |
| 劣势 | 说明 |
|---|---|
| 分辨率受限 | 内部BRAM容量有限,无法缓存完整高清帧 |
| 图像内容受限 | 不能存储复杂的全帧图像,只能显示简单图案或实时流 |
| 数据源必须同步 | 如果没有帧缓冲,数据源速率必须精确匹配屏幕刷新率,否则撕裂 |
适用场景 :
- 低分辨率显示(≤ 480×272)
- 仪表盘、波形、字符、简单动画(无需复杂图片)
- 摄像头实时直通显示(不存储,只做格式转换)
- 工业控制HMI(静态界面+局部刷新)
二、无DDR显示方案的三种主流架构
架构A:BRAM完整帧缓冲(小分辨率)
原理 :用FPGA内部BRAM存储完整的一帧图像,显示控制器从BRAM读取,数据源(如CPU、UART、SPI Flash)写入BRAM。
容量计算 :
| 分辨率 | 色深 | 帧缓冲大小 | BRAM块数(36Kb/块) | 典型FPGA可行性 |
|---|---|---|---|---|
| 320×240 | RGB565 | 320×240×16 = 1,228,800 bit | ≈ 34 | 任何小FPGA均可 |
| 480×272 | RGB565 | 480×272×16 ≈ 2,088,960 bit | ≈ 58 | Artix-7 35T有50块,勉强;50T以上可 |
| 640×480 | RGB565 | 640×480×16 = 4,915,200 bit | ≈ 137 | 需中规模FPGA(如A200T) |
| 800×480 | RGB565 | 6,144,000 bit | ≈ 171 | 大容量FPGA,成本不划算 |
优缺点 :
- ✅ 简单,显示控制器与数据源完全异步
- ✅ 支持任意图像内容(因为完整存储)
- ❌ 分辨率上限低(通常≤480×272)
- ❌ BRAM占用量大,影响其他逻辑
适用 :静态图片、简单GUI、字符显示。
架构B:线缓存 + 实时流处理(无帧缓冲)
原理 :不存储完整帧,数据源实时向FPGA发送像素流,FPGA只缓存少量行(line buffer)用于时序重整或格式转换,然后直接输出到屏幕。
典型应用 :
- 摄像头(如OV7670、MT9M001)直接驱动屏幕
- 视频解码器(如ADV7180)输出ITU-R BT.656转RGB
关键挑战 :数据源的行/场同步必须与屏幕时序完全匹配(或通过FIFO异步桥接)。如果数据源输出速率不等于屏幕PCLK,就需要 速率匹配 。
速率匹配方法 :
- 源时钟直接作为PCLK :如果摄像头输出PCLK与屏幕要求的PCLK频率一致,可以直接用。但多数情况下不一致。
- 异步FIFO + 行消隐补偿 :将数据源像素写入FIFO,屏幕控制器从FIFO读取。只要平均写入速率 ≥ 读取速率,且FIFO深度足够覆盖瞬时波动,就能正常工作。行消隐期提供额外的补偿时间。
FIFO深度计算 (示例):
- 源像素时钟 = 12MHz,有效像素率 ≈ 10MP/s
- 屏幕PCLK = 25MHz,有效像素率 = 800×480×60 ≈ 23MP/s
- 写入速率 < 读取速率 → FIFO会空,不行。必须 源速率 ≥ 屏幕有效速率 ,否则丢帧。
实际可用 :当源速率略低于屏幕刷新率时,可以在屏幕消隐期暂停读取,让FIFO积累数据。但会导致帧率降低(屏幕出现重复帧或卡顿)。
优缺点 :
- ✅ 极低延迟(通常只有几行延迟)
- ✅ 几乎不占BRAM(只需几行缓存,几十KB)
- ✅ 支持高分辨率(只要源能提供足够带宽)
- ❌ 数据源必须与屏幕时序强耦合,难以处理异步数据源(如网络图片)
- ❌ 不能实现图像叠加、缩放等复杂处理
适用 :摄像头实时监控、视频直通。
架构C:外部SPI PSRAM / SRAM作为帧缓冲
原理 :外挂一颗便宜的非DDR存储器,如 SPI PSRAM (如APS6404L-3SQR,64Mbit,~$1.5)或 并行SRAM (如IS61LV25616,但价格高)。FPGA通过SPI或并行接口读写,作为帧缓冲。
SPI PSRAM特点 :
- 容量大(可达256Mbit),价格低
- 接口简单(SPI,4~6根线)
- 带宽有限(SPI时钟通常50~100MHz,理论最大50MB/s)
- 随机访问延迟大(SPI命令+等待)
带宽验证 (以800×480 RGB565为例):
- 每帧数据量 = 800×480×2 = 768KB
- 60Hz → 需要读取带宽 768KB × 60 = 46.08 MB/s
- SPI PSRAM @ 100MHz(双倍数据率?普通SPI单沿)最大约12.5MB/s(单线)或50MB/s(四线QSPI)。四线QSPI勉强够用。
实现注意 :
- 必须使用突发读取模式,利用PSRAM的连续地址访问特性
- 需要读缓存FIFO,防止PSRAM延迟导致下溢
- 写操作(数据源更新)会占用带宽,需要合理安排时序(如利用消隐期写入)
优缺点 :
- ✅ 成本低于DDR,布线简单(SPI只需少量IO)
- ✅ 支持中等分辨率(800×480)和复杂图像
- ❌ 带宽紧张,高分辨率(≥1024×600)可能不够
- ❌ 需要额外的SPI控制器和缓存逻辑
适用 :成本敏感的嵌入式显示,分辨率不超过WVGA。
三、系统要求与开发注意事项
3.1 数据源带宽匹配
无论哪种架构,无DDR方案最核心的问题是 数据源速率与屏幕刷新率的匹配 。
公式 :
text
源有效数据速率 ≥ 屏幕有效数据速率
其中有效数据速率 = 分辨率 × 色深 × 帧率。
如果不满足,必须引入帧缓冲(哪怕是小容量的BRAM)来解耦。否则屏幕会出现反复撕裂或闪屏。
工程建议 :在设计初期计算理论速率,留出20%余量。例如屏幕需要46MB/s,源应能提供至少55MB/s。
3.2 时序约束与跨时钟域
无DDR方案中往往存在多个时钟域:
- 数据源时钟(如摄像头PCLK)
- 屏幕像素时钟(PCLK)
- 内部处理时钟(可能更高,用于插值或色彩转换)
跨时钟域处理 :
- 异步FIFO是最可靠的方法,深度根据最大时钟差和突发长度计算。
- 对于单bit控制信号(如行同步),使用两级寄存器同步 + 边沿检测。
注意 :不要试图用组合逻辑直接连接不同时钟域的FIFO读写指针,会引入亚稳态。
3.3 行缓存(Line Buffer)的设计
在实时流处理中,常见操作是缓存N行数据用于:
- 色彩空间转换(如YUV422转RGB需要两行)
- 图像缩放(双线性插值需要2~4行)
- 去隔行(需要两场)
实现方法 :使用BRAM配置为简单的 移位寄存器 (SHIFT_REG)或 双端口RAM ,每次写入一行,读取前一行。
资源估算 :一行800像素RGB565 = 800×16 = 12.8Kb。缓存2行需要25.6Kb,不足一块BRAM(36Kb),可以容纳。缓存4行需51.2Kb,需2块BRAM。
3.4 FIFO深度设计
异步FIFO用于连接数据源和屏幕控制器时,深度必须满足:
- 最大写入突发长度(如摄像头一行有效像素)
- 最大读取连续时间(屏幕一行有效像素)
- 最坏情况下的时钟相位差
经验公式 :
text
FIFO深度 ≥ (写入峰值速率 - 读取峰值速率) × 最大突发时间 + 安全边际
对于摄像头到屏幕的直接直通,通常 一行深度 (如800像素)足够,因为行消隐期可以清空或填充FIFO。
3.5 屏幕消隐期的利用
在无DDR方案中,消隐期是宝贵的时间窗口,可以用于:
- 更新BRAM帧缓冲(如果使用架构A)
- 从SPI PSRAM预取下一行数据(架构C)
- 发送控制命令(如摄像头寄存器配置)
实现技巧 :利用VSYNC或HSYNC中断,触发DMA或状态机在消隐期执行操作。
四、实战案例:用BRAM显示SPI Flash中的图片(480×272)
4.1 系统概述
- FPGA:Cyclone IV EP4CE10(约 46 块 M9K BRAM,每块9Kb)
- 屏幕:480×272,RGB565(16位色)
- 图片存储:SPI Flash(W25Q64),存放一张480×272的RGB565原始数据(约255KB)
- 流程:上电后,FPGA从SPI Flash读取图片数据,写入BRAM帧缓冲,然后显示控制器循环显示。
4.2 资源评估
BRAM需求:480×272×16 = 2,088,960 bits = 255KB。
Cyclone IV M9K块大小 9Kb = 1.125KB,需要 255 / 1.125 ≈ 227 块。而EP4CE10只有 46 块! 不够 。
解决方案 :
- 降低分辨率:320×240 RGB565 = 150KB,需133块,仍然不够。
- 使用更高密度FPGA(如EP4CE22有 66 块,还是不够)。
- 改用压缩图片(如RLE),运行时解压到BRAM。但需要增加解压逻辑。
- 实际选择 :改用更小分辨率(如240×160)或改用外部SRAM。
结论 :对于480×272 RGB565,大部分低端FPGA的BRAM不足以做完整帧缓冲。这是架构A的主要限制。
4.3 可行的低端实现:240×160 RGB565
- 帧缓冲大小 = 240×160×16 = 614,400 bit = 75KB
- BRAM块数 ≈ 75 / 1.125 ≈ 67 块(EP4CE10只有46块,仍然不够!因为M9K是9Kb,实际可用约46×9Kb=414Kb=51.75KB,只能支持 51.75KB / 2字节/像素 = 25875像素,约160×160 分辨率)。
所以, EP4CE10 最大无压缩帧缓冲分辨率约 160×160 RGB565 。
验证 :160×160×16 = 409,600 bit = 50KB,接近极限。
工程建议 :用BRAM做完整帧缓冲时,务必查FPGA手册计算可用BRAM总容量,再倒推最大分辨率。
4.4 代码结构(伪代码)
verilog
// 帧缓冲BRAM(双端口)
xpm_memory_spram #(
.ADDR_WIDTH_A(16), // 160*160=25600,2^15=32768足够
.DATA_WIDTH_A(16),
.MEMORY_SIZE(160*160*16)
) u_framebuffer (...);
// SPI Flash 读取控制器
// 上电后状态机:发送读命令 → 等待数据 → 写入BRAM地址0~25599
// 显示控制器(同篇8,但数据源从BRAM读取)
always @(posedge pclk) begin
if (de) begin
rd_addr <= row * 160 + col;
rgb <= framebuffer_dout; // 从BRAM读出的像素
end
end
五、什么时候该放弃无DDR方案?
当你的需求满足以下任意两条时,请考虑回到DDR方案:
- 分辨率 ≥ 800×480,且需要全彩色(RGB888)
- 需要显示 复杂图片 (照片、地图、视频帧)
- 数据源是 异步的 (如网络、SD卡、随机到来的图像)
- 需要多图层叠加 或透明混合
- 需要缩放、旋转等图像处理
此时,DDR带来的设计复杂度是值得的,因为无DDR方案会在性能、功能、开发时间上付出更高代价。
六、☕ 工程师私房话
面试题:如果摄像头输出速率低于屏幕刷新率,无DDR情况下如何尽量流畅显示?
答案 :可以使用帧重复 或帧保持策略。即屏幕控制器在读完一帧后,重复读取上一帧数据(如果FIFO或行缓存中保留),直到新帧到达。这会导致运动不连贯,但避免了撕裂。实现上需要至少一个完整帧的存储(又回到帧缓冲),或者使用更聪明的预测算法(不推荐)。
实际案例:用廉价FPGA驱动480×272屏显示波形
很多入门示波器、信号发生器只用内部BRAM做波形显示,因为波形区域只占屏幕一部分(如400×200),其余区域为静态刻度。这样帧缓冲可以只缓存波形区域,其他区域实时生成。部分帧缓冲是节省BRAM的实用技巧。
冷知识:为什么早期的VGA显卡没有DDR也能显示?
早期VGA显卡(如ISA总线的)使用 双端口VRAM ,本质上就是片上帧缓冲。那时分辨率低(640×480 256色),帧缓冲约300KB,集成在显卡上。现代FPGA的BRAM容量远大于早期VRAM,所以低分辨率显示完全可以脱离DDR。