基于DE10-Nano的FPGA VGA显示控制器设计:从时序原理到SDRAM帧缓冲实现

发布时间:2026/5/19 12:56:09

基于DE10-Nano的FPGA VGA显示控制器设计:从时序原理到SDRAM帧缓冲实现 1. 项目概述从一块开发板到一块屏幕的旅程手头有一块友晶科技的DE10-Nano开发板看着它丰富的FPGA资源和高速接口总想着得用它做点“看得见”的东西。直接驱动显示器尤其是经典的VGA接口是一个既能验证数字逻辑设计能力又能获得直观反馈的绝佳项目。这个“基于友晶DE10-Nano开发板的VGA显示控制器模块设计”核心目标就是利用板载的Cyclone V FPGA从头构建一个能够产生标准VGA时序信号并能在屏幕上绘制出图形、文字乃至动态画面的数字系统。它不仅仅是一个简单的信号发生器更是一个涵盖了时钟管理、帧缓冲、图形渲染流水线的完整嵌入式显示子系统原型。对于嵌入式开发者、FPGA初学者或是电子爱好者而言这个项目具有多重价值。首先它是理解视频显示原理的绝佳实践你将亲手操控每一个行同步、场同步脉冲和像素数据。其次它能极大地锻炼你的硬件描述语言如Verilog或VHDL编程能力尤其是状态机设计和时序收敛。最后基于DE10-Nano你可以轻松地将这个显示控制器与板载的ARM Cortex-A9硬核处理器HPS相结合实现软硬协同例如用HPS生成图像数据由FPGA部分高速输出到屏幕这为更复杂的应用如嵌入式GUI、游戏、仪器仪表界面打下了坚实基础。接下来我将拆解整个设计过程从原理到代码从仿真到上板调试分享其中的关键技术与踩过的坑。2. VGA显示原理与DE10-Nano硬件基础解析2.1 VGA接口的“语言”时序与色彩VGAVideo Graphics Array是一种模拟视频接口虽然古老但其时序原理是理解现代数字视频的基础。与HDMI等数字接口传输打包的像素数据不同VGA控制器需要直接生成三类信号来“指挥”显示器工作RGB色彩信号三路模拟信号分别代表红、绿、蓝的强度。在数字域我们通常用数字值如8位来表示然后通过数模转换器DAC变成模拟电压。DE10-Nano板载了专用的VGA DAC芯片ADV7123我们只需提供数字RGB信号即可。行同步HSYNC一个数字脉冲信号用于指示一扫描行Line的开始。场同步VSYNC一个数字脉冲信号用于指示一帧Frame图像的开始。显示一帧图像的过程就像用笔在纸上从左到右、从上到下写字。HSYNC对应换行VSYNC对应翻页。但关键在于这个“写字”过程包含了必要的“空白”时间。以经典的640x48060Hz模式为例其时序参数如下参数描述像素时钟数 (对于640x48060Hz)说明Pixel Clock像素时钟频率25.175 MHz (约)每个像素的传输节奏。Horizontal水平方向-Visible Area可见区域640 pixels实际显示的像素行宽。-Front Porch前沿16 pixels行同步脉冲前的消隐区。-Sync Pulse同步脉冲96 pixels行同步信号低电平宽度。-Back Porch后沿48 pixels行同步脉冲后的消隐区。-Total总周期800 pixels一行总计时间。Vertical垂直方向-Visible Area可见区域480 lines实际显示的行数。-Front Porch前沿10 lines场同步脉冲前的消隐区。-Sync Pulse同步脉冲2 lines场同步信号低电平宽度。-Back Porch后沿33 lines场同步脉冲后的消隐区。-Total总周期525 lines一帧总计行数。注意这些“消隐区”是CRT显示器时代的物理需求电子束回扫时间在LCD上虽无物理必要但必须严格遵守此时序规范否则显示器可能无法识别或图像偏移。我们的FPGA设计核心就是生成一个严格遵循此时序的“节拍器”并在正确的“节拍”可见区域输出对应的RGB像素数据。2.2 DE10-Nano开发板的显示相关硬件资源友晶DE10-Nano板为我们的项目提供了“开箱即用”的硬件支持关键部件如下FPGA芯片Intel Cyclone V SE 5CSEBA6U23I7。提供足够的逻辑单元约110K LE和内存资源用于实现控制器和帧缓冲。VGA DAC芯片ADV7123。这是一个三通道、高速视频DAC。它接收FPGA输出的24位数字RGB信号每通道8位共1677万色和像素时钟并转换为模拟VGA信号。板载电路已经连接好我们只需在FPGA引脚上输出正确信号。时钟源板载50MHz晶振。我们需要通过FPGA内部的PLL锁相环将其倍频/分频生成项目所需的精确像素时钟如25.175MHz。SDRAM64MB。这是实现复杂图形显示的关键。FPGA内部的存储块M10K容量有限无法存储一整帧高彩色图像640x480x24bit ≈ 900KB。外置SDRAM可以作为帧缓冲区Frame Buffer存储整幅图像数据供显示控制器按需读取。HPS硬核处理器系统双核ARM Cortex-A9。这是DE10-Nano的一大特色。我们可以让HPS运行Linux或裸机程序通过FPGA与HPS之间的高速总线如AXI将图像数据写入SDRAM中的帧缓冲再由FPGA端的显示控制器读取并显示实现动态内容更新。理解这些硬件是设计的前提。我们的设计将主要围绕FPGA逻辑展开并充分利用SDRAM和HPS。3. 显示控制器整体架构与模块划分一个完整的VGA显示控制器绝非一个简单的计数器。为了结构清晰、易于维护和扩展我采用自顶向下的模块化设计。整个系统可以分为以下几个核心模块它们协同工作如同一个微型显卡。3.1 顶层系统框图与数据流整个系统的数据流和模块交互如下图所示概念性描述----------------------- | HPS (ARM) | | (生成图像数据/指令) | ---------------------- | AXI总线 v --------------- ---------v--------- ------------------ | | | | | | | SDRAM |----| SDRAM控制器 |----| 帧缓冲管理器 | | (帧缓冲区) | | (读写仲裁与时序) | | (地址生成与管理) | --------------- ------------------ ----------------- | | | | -----------v--------------------------v----------- | | | VGA时序生成器 | | (生成HSYNC, VSYNC, 行列计数消隐信号) | ----------------------------------------------- | | | | -----------v--- ---------v------------- | | | | | 像素数据输出 | | 色彩查找表/图形发生器 | | 逻辑 | | (可选用于图案生成) | -------------- ----------------------- | v -------------- | | | VGA接口引脚 |--- 至 ADV7123 DAC | (输出到物理IO) | ---------------工作流程时序驱动VGA时序生成器以像素时钟运行产生精确的HSYNC、VSYNC信号并输出当前像素的坐标X, Y计数以及消隐信号Blank。地址计算帧缓冲管理器根据当前像素坐标X, Y计算出该像素颜色数据在SDRAM帧缓冲区中的对应内存地址。数据获取该地址被发送给SDRAM控制器。控制器负责复杂的SDRAM协议时序向SDRAM芯片发起读请求。数据流水SDRAM读取有一定延迟潜伏期。为了确保像素数据在“可见区域”准时到达需要设计流水线或FIFO进行缓冲。当数据从SDRAM返回后经由像素数据输出逻辑在非消隐期间将RGB数据输出到FPGA的指定引脚。内容更新HPS可以通过AXI总线直接或通过DMA方式向SDRAM控制器写入新的图像数据从而更新屏幕内容。图形发生器模块则是一个可选的纯FPGA实现用于在没有HPS参与时生成测试图案如彩条、方块。3.2 核心模块功能详述3.2.1 VGA时序生成器 (vga_timing_gen)这是整个系统的“心脏”。它本质上是一个双计数器行计数器、场计数器状态机。输入像素时钟pixel_clk、复位信号。输出hsync,vsync: 行、场同步信号。h_count,v_count: 当前像素的水平和垂直计数值通常从0开始。video_on: 视频有效信号高电平表示当前处于可见区域即非消隐期。设计要点使用参数Parameter/Generic来定义不同显示模式640x480, 800x600等的时序常数提高代码可重用性。h_count从0计数到H_TOTAL-1v_count在每行结束时递增。当h_count和v_count都在可见区域范围内时video_on置高。hsync和vsync的极性高有效或低有效取决于显示模式需要查阅VGA标准。对于640x48060Hz两者都是负极性低电平有效脉冲。3.2.2 帧缓冲管理器与地址生成 (fb_addr_gen)此模块负责将二维的屏幕坐标转换为一维的线性内存地址。输入当前像素坐标h_count, v_count、基地址frame_base_addr。输出读请求信号、要读取的内存地址sdram_addr。地址计算对于最常见的线性帧缓冲公式为address frame_base_addr (v_count * H_DISPLAY h_count) * BYTES_PER_PIXEL。H_DISPLAY是水平可见像素数如640。BYTES_PER_PIXEL是每个像素占用的字节数。对于24位RGBR8G8B8就是3字节。如果使用16位RGB565格式则是2字节可以节省带宽和存储空间但色彩精度降低。流水线对齐由于从发出读地址到数据返回有延迟必须确保地址生成提前于当前像素的显示时间。这需要根据SDRAM控制器的延迟Latency来设置提前量。3.2.3 SDRAM控制器集成与仲裁这是项目中最复杂的部分之一。DE10-Nano的SDRAM芯片型号为IS42S16160D容量64Mb16位数据总线。我们需要一个控制器来管理其初始化、刷新、读写命令。方案选择使用Qsys/Avalon总线这是Intel推荐的方式。在Quartus的Qsys工具中可以拖拽一个“SDRAM Controller”组件它会自动生成一个Avalon-MM总线接口的控制器。我们的FPGA逻辑通过Avalon总线与之通信。这种方式稳定但需要理解Avalon总线协议。使用开源IP核例如社区有一些Verilog实现的SDRAM控制器接口更简单如类似SRAM的接口。但需要自己验证其在Cyclone V上的时序。与HPS共享DE10-Nano的HPS已经通过其内置的SDRAM控制器管理着这片内存。FPGA可以通过“FPGA-to-HPS SDRAM Bridge”来访问同一片SDRAM。这省去了我们实现控制器的麻烦但需要配置HPS的地址映射且访问延迟和带宽可能受HPS影响。仲裁逻辑如果同时存在HPS和FPGA逻辑如图形发生器需要读写帧缓冲则需要一个仲裁器来公平、无冲突地分配SDRAM访问权限。通常优先级可以设置为HPS写 FPGA显示读 FPGA图形生成写。仲裁器需要管理请求、授权和等待。3.2.4 像素数据流水线与输出从SDRAM读出的数据不能直接送到IO引脚需要经过处理。FIFO缓冲在显示控制器和SDRAM控制器之间插入一个异步FIFO。SDRAM控制器以突发Burst方式写入数据显示控制器以稳定的像素时钟速率读取数据。FIFO可以平滑两者之间的速率差异和突发间隔防止画面撕裂。格式转换如果帧缓冲中存储的是RGB56516位数据而VGA DAC需要24位则需要一个转换模块在输出前进行插值或映射。同步输出最终的输出模块在video_on为高时将FIFO中读出的RGB数据送到输出寄存器在video_on为低时消隐期输出黑色或特定颜色。同时将hsync和vsync信号同步到像素时钟域后输出。4. 关键模块的Verilog实现与仿真4.1 VGA时序生成器代码精讲以下是一个参数化的VGA时序生成器Verilog代码核心部分以640x48060Hz为例module vga_timing_gen #( parameter H_DISP 640, // 水平可见像素 parameter H_FP 16, // 水平前沿 parameter H_SYNC 96, // 水平同步脉冲 parameter H_BP 48, // 水平后沿 parameter H_TOTAL H_DISP H_FP H_SYNC H_BP, // 800 parameter V_DISP 480, // 垂直可见行 parameter V_FP 10, // 垂直前沿 parameter V_SYNC 2, // 垂直同步脉冲 parameter V_BP 33, // 垂直后沿 parameter V_TOTAL V_DISP V_FP V_SYNC V_BP // 525 )( input wire clk, // 像素时钟如25MHz input wire rst_n, // 异步低电平复位 output reg hsync, // 行同步 (负极性) output reg vsync, // 场同步 (负极性) output reg video_on, // 视频有效信号 output reg [11:0] h_cnt, // 水平计数器 (0 to H_TOTAL-1) output reg [11:0] v_cnt // 垂直计数器 (0 to V_TOTAL-1) ); // 水平计数器逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) begin h_cnt 12d0; end else begin if (h_cnt H_TOTAL - 1) begin h_cnt 12d0; end else begin h_cnt h_cnt 12d1; end end end // 垂直计数器逻辑在每行结束时递增 always (posedge clk or negedge rst_n) begin if (!rst_n) begin v_cnt 12d0; end else begin if (h_cnt H_TOTAL - 1) begin // 一行结束 if (v_cnt V_TOTAL - 1) begin v_cnt 12d0; end else begin v_cnt v_cnt 12d1; end end end end // 生成hsync信号 (负极性低电平有效) always (posedge clk or negedge rst_n) begin if (!rst_n) begin hsync 1b1; // 无效时为高电平 end else begin // 同步脉冲期间输出低电平 if ((h_cnt H_DISP H_FP) (h_cnt H_DISP H_FP H_SYNC)) begin hsync 1b0; end else begin hsync 1b1; end end end // 生成vsync信号 (负极性低电平有效) always (posedge clk or negedge rst_n) begin if (!rst_n) begin vsync 1b1; end else begin if ((v_cnt V_DISP V_FP) (v_cnt V_DISP V_FP V_SYNC)) begin vsync 1b0; end else begin vsync 1b1; end end end // 生成video_on信号 (高电平表示在可见区域) always (posedge clk or negedge rst_n) begin if (!rst_n) begin video_on 1b0; end else begin if ((h_cnt H_DISP) (v_cnt V_DISP)) begin video_on 1b1; end else begin video_on 1b0; end end end endmodule实操心得在仿真时除了看波形我强烈建议将h_cnt和v_cnt以模拟量的形式显示并叠加hsync和vsync脉冲这样可以直观地看到时序图检查前沿、同步脉冲、后沿的宽度是否正确。在Modelsim或Quartus自带的仿真器中都可以做到。4.2 帧缓冲地址生成与简单图形生成器在没有SDRAM控制器时我们可以先用FPGA片内RAM如M10K实现一个简单的图形生成器来验证时序。module simple_pattern_gen #( parameter H_DISP 640, parameter V_DISP 480 )( input wire clk, input wire rst_n, input wire [11:0] h_cnt, input wire [11:0] v_cnt, input wire video_on, output reg [7:0] vga_r, output reg [7:0] vga_g, output reg [7:0] vga_b ); // 根据坐标生成测试图案 always (posedge clk or negedge rst_n) begin if (!rst_n) begin {vga_r, vga_g, vga_b} 24h000000; end else if (video_on) begin // 示例1彩条 if (h_cnt H_DISP/8) begin {vga_r, vga_g, vga_b} 24hFF0000; // 红 end else if (h_cnt 2*H_DISP/8) begin {vga_r, vga_g, vga_b} 24h00FF00; // 绿 end else if (h_cnt 3*H_DISP/8) begin {vga_r, vga_g, vga_b} 24h0000FF; // 蓝 end // 示例2棋盘格 (32x32像素) else if ( ((h_cnt[5] ^ v_cnt[5]) 1b0) ) begin // 异或判断 {vga_r, vga_g, vga_b} 24hFFFFFF; // 白 end else begin {vga_r, vga_g, vga_b} 24h000000; // 黑 end end else begin // 消隐期输出黑色 {vga_r, vga_g, vga_b} 24h000000; end end endmodule这个模块直接根据h_cnt和v_cnt生成颜色绕过了帧缓冲。将它和时序生成器连接就能在屏幕上看到固定的测试图案是验证VGA接口硬件和时序是否正确的最快方法。4.3 使用Qsys集成SDRAM控制器这是将系统复杂度提升到实用级别的关键一步。在Quartus Prime中通过Platform Designer旧称Qsys进行系统集成创建组件添加“Clock Source”50MHz输入产生系统时钟和PLL生成的像素时钟、“SDRAM Controller”配置为匹配板载芯片的型号、数据位宽16、地址位宽等、“VGA时序生成器”自定义IP、“帧缓冲管理器”自定义IP、“PIO”用于连接VGA输出引脚等。总线连接将需要访问SDRAM的组件如帧缓冲管理器、HPS的FPGA-to-SDRAM桥连接到SDRAM控制器的“Avalon Memory Mapped Slave”端口。注意分配正确的基地址和地址范围。时钟与复位正确连接所有时钟域。SDRAM控制器通常运行在比像素时钟更高的频率如100MHz以获得足够带宽。需要处理好跨时钟域的数据传递使用异步FIFO。生成系统Qsys会生成一个包含所有组件的顶层模块和相应的HDL文件。在Quartus主工程中实例化这个系统并分配好物理引脚尤其是VGA相关的红、绿、蓝、行同步、场同步引脚在DE10-Nano用户手册中有明确对应。踩坑记录第一次使用Qsys时最容易出错的是地址映射。确保你的帧缓冲管理器发出的地址偏移量加上SDRAM控制器在系统中的基地址最终能正确访问到SDRAM的物理地址空间。建议先在HPS端如果使用写一个简单的内存测试程序确认SDRAM访问正常再调试FPGA端的读取逻辑。5. 系统调试、性能优化与问题排查5.1 上板调试流程与工具静态测试编译综合后首先使用Quartus的“Pin Planner”和“Assignment Editor”反复核对引脚分配特别是VGA的引脚电平标准可能是3.3V LVTTL。SignalTap II逻辑分析仪这是FPGA调试的利器。将hsync、vsync、video_on、h_cnt[0]、v_cnt[0]以及RGB数据的某几位抓取进来。通过设置触发条件如vsync上升沿可以捕获到实际的视频时序波形与仿真波形对比确认时序在硬件上完全正确。渐进式调试第一步只烧写“时序生成器简单图形生成器”的组合用显示器观察是否有图像图像是否稳定、居中。如果无图像检查同步信号极性、时钟频率。第二步引入SDRAM控制器和帧缓冲读取逻辑。可以先让HPS或一个简单的FPGA初始化模块向SDRAM的帧缓冲区域写入固定的测试图案如渐变色块。第三步连接完整的读取流水线。使用SignalTap观察从发出读地址到数据返回的延迟调整FIFO深度和读提前量确保数据流不断流。带宽估算与验证对于640x48060Hz24位色深所需像素带宽为640 * 480 * 60 * 24 ≈ 442 Mbps。SDRAM是16位总线假设工作频率为100MHz理论峰值带宽是100M * 16 * 2DDR 3200 Mbps。但实际有效带宽受刷新、行列切换、仲裁开销影响。我们的需求远低于峰值因此带宽充足。但如果升级到1024x76860Hz就需要仔细评估。5.2 常见问题与解决方案速查表现象可能原因排查步骤与解决方案显示器提示“无信号”或“不支持”1. 同步信号极性错误。2. 像素时钟频率偏差太大。3. 同步信号或RGB信号引脚未连接或短路。1. 用示波器或SignalTap测量HSYNC/VSYNC波形对照标准确认极性和脉冲宽度。2. 检查PLL配置确保生成的像素时钟频率精确25.175MHz对640x480很重要。3. 检查Quartus编译报告中的引脚分配和未连接引脚警告。图像抖动或滚动1. 时序参数尤其是前后沿不准确。2. 像素时钟不稳定或有抖动。1. 微调H_FP、H_BP、V_FP、V_BP参数。显示器内部的锁相环PLL对这些值敏感。2. 确保输入给PLL的基准时钟50MHz质量良好PLL设置正确。图像有水平或垂直条纹/噪点1. RGB数据在消隐期未置零或置为固定值干扰了显示器的钳位电路。2. 电源噪声干扰模拟VGA信号。3. SDRAM读取数据错误。1. 确保video_on为低时RGB输出强制为0。2. 检查开发板VGA接口附近的滤波电容或尝试使用带磁环的VGA线。3. 运行SDRAM内存测试或通过SignalTap对比读出的数据和预期是否一致。部分区域显示错误颜色或错位1. 帧缓冲地址计算错误。2. 色彩格式转换错误如RGB565到RGB888。3. FIFO上溢或下溢导致数据错位。1. 在特定坐标如屏幕中心写入特定颜色检查显示是否正确。调试地址计算公式。2. 确认输入输出数据的位宽和映射关系。3. 增加FIFO深度或调整SDRAM读请求的发起时机。使用HPS更新图像时屏幕撕裂HPS写入和FPGA读取帧缓冲发生冲突读取了正在被修改的数据。实现双缓冲Double Buffering在SDRAM中分配两个帧缓冲区域Front Buffer, Back Buffer。显示控制器始终读取Front BufferHPS将新图像写入Back Buffer。当一帧图像写入完成后交换两个缓冲区的指针此操作需在垂直消隐期进行以避免撕裂。5.3 性能优化技巧SDRAM突发Burst读取SDRAM在连续地址访问时效率最高。我们的帧缓冲是线性存储的因此应该配置SDRAM控制器使用最大允许的突发长度如8或16进行读取。这样一次请求可以获取多个像素的数据大幅减少命令开销提高带宽利用率。使用AXI DMA加速HPS到FPGA的数据传输如果HPS需要频繁更新大块图像如视频流通过CPU memcpy效率低下。应启用HPS的DMA控制器通过AXI总线直接搬运数据到SDRAM的帧缓冲区解放CPU。在FPGA内实现简单的2D图形加速如果需要在FPGA端动态绘制一些基本图形如直线、矩形、填充可以设计一个“图形引擎”模块。它接收绘图指令如起点、终点、颜色直接操作SDRAM中的帧缓冲。这比通过HPS写入每个像素要快得多。降低色彩深度如果应用对色彩要求不高使用RGB56516位代替RGB88824位可以立即减少33%的带宽和内存消耗使更高分辨率的显示成为可能。6. 项目总结与进阶应用展望完成这个基础的VGA显示控制器就像是亲手打造了一块简易的显卡核心。它验证了从数字逻辑生成模拟视频信号的完整链条。在这个过程中最深刻的体会是时序的精确性和系统级的协同。每一个时钟沿、每一个信号脉冲都必须严丝合缝而FPGA逻辑、SDRAM控制器、HPS处理器之间的数据流需要精心设计缓冲和仲裁机制才能流畅稳定。这个项目是一个强大的起点以此为基可以探索许多有趣的方向显示复杂UI将帧缓冲与HPS上的图形库如SDL, Qt结合在Linux下开发丰富的嵌入式图形界面。视频叠加Overlay设计多个图层Layer例如一个背景层和一个光标/菜单层在FPGA端实现硬件混合Alpha Blending再输出。硬件加速图形用FPGA实现 Bresenham画线算法、三角形填充等构建一个极简的2D图形加速IP。接入摄像头将DE10-Nano的摄像头接口如TRDB-D5M的数据经过处理后写入帧缓冲实现实时视频显示。移植到其他显示接口理解了VGA的时序本质后可以尝试驱动更现代的接口如HDMI需要专用的 serializer芯片如ADV7513或LVDS用于液晶屏。最后关于工具链的一点心得Quartus Prime和Platform Designer功能强大但略显复杂。初期多花时间阅读官方手册和DE10-Nano的示例项目例如Terasic提供的“DE10-Nano VGA”示例能帮你避开很多坑。调试阶段SignalTap和In-System Memory Content Editor是你的最佳伙伴。这个项目涉及了FPGA开发的多个核心技能点吃透它你对数字系统设计的理解会上一个大台阶。

相关新闻