☰
FPGA驱动RGB TFT彩条工程:从时序参数到上板调试
2026/10/15 18:09:20 网站建设 项目流程

简介:一份基于Altera FPGA驱动RGB接口液晶屏的完整工程资料,面向FPGA入门者与显示接口开发人员,以tft_rgb_colorbar设计为例,系统展示红绿蓝三色并行传输、像素时钟与行场同步时序生成、帧缓冲、颜色空间转换等关键知识点,并体现Quartus Ⅱ环境下的逻辑综合、仿真和硬件调试方法。压缩包约3.43MB,共126个文件,其中既有工程配置文件、Verilog源代码与SOF位流,也包含多种Quartus工程数据库,以及readme、HTML、JPG等说明性文档,便于对照完整流程理解各模块作用。已有1720人浏览学习,具有实际参考价值。按此工程可快速搭建RGB屏驱动实验环境,通过顶层设计、时序约束与IP配置,可以掌握Altera平台驱动RGB液晶屏的基本框架、接口时序控制思路以及常见排错方法,对后续开发同类显示系统能起到直接借鉴作用。

1. 拿到 TFT 彩条工程后:先用它验证屏和接线,再谈驱动开发

很多入坑 FPGA 的开发者第一次折腾 RGB 接口 TFT 屏,都会经历同一个过程:屏上电亮了,背光正常,但整屏要么花线条、要么颜色发绿发紫,要么干脆静止不动。这时候第一件事不是去调应用层接口,而是先跑一个最基础的彩条工程,把屏的时序、线序、像素时钟挨个验证一遍。这份 tft_rgb_colorbar 资源干的就是这件事:它用 FPGA 的 PL 逻辑直接驱动 RGB 接口 TFT,输出白、黄、青、绿、品、红、蓝、黑八条标准彩条,不依赖操作系统,也不需要额外控制器。它适合三类人:硬件调试工程师拿它验屏验板,FPGA 学习者拿它理解行场同步和像素时钟,做显示方案选型的人拿它快速判断一块屏能不能驱动、驱动参数对不对。

2. RGB 接口 TFT 的驱动模型:像素时钟、行场参数与同步模式

2.1 RGB 接口和 MCU 接口的差别:为什么 FPGA 更适合干这个

RGB 接口和 MCU 接口(比如 8080/6800 并口或 SPI)最大的区别在于:屏内部有没有完整的显存刷新逻辑。MCU 接口屏自带 GRAM,CPU 往显存里写数据就完事,刷新由屏自己完成;RGB 接口屏通常没有大容量 GRAM 或者只保留了很小一块,它要求外部设备在每一帧时间内持续不断地把所有像素点按固定节奏送进来。换句话说,RGB 屏是“实时性”驱动,数据流一旦断,画面立刻花掉。

这就催生了 RGB 接口的一组关键信号,通常包括:像素时钟(PCLK)、行同步(HSYNC)、场同步(VSYNC)、数据使能(DE)以及并行数据线(RGB666/RGB565/RGB888)。有些屏还把 DE 省了,只靠 HSYNC、VSYNC 和 PCLK 来区分有效数据区间,这就是传统同步模式。DE 模式和同步模式的区别会在后面详细讲,但它恰恰是新手最容易栽跟头的点。

FPGA 适合驱动 RGB 屏的原因也很直白:它的时钟频率可控、时序可以用硬件逻辑精确到纳秒级,而且输出引脚多,可以并行推 RGB888 的真彩数据。相比之下单片机虽然有 LCD 控制器,但刷新率和分辨率上限会被主频卡死,遇到 800x480 以上分辨率时往往力不从心。所以在工控、仪器、车载这类对实时性要求高的场景里,FPGA + RGB TFT 是常见组合。

2.2 行场时序参数表:把屏规格书里的数字翻译成 FPGA 参数

拿到一块 RGB 屏,屏厂给的规格书里最核心的一张表就是时序参数。以常见的 800x480 屏幕为例,典型参数如下:

参数含义典型值(像素时钟周期)
H_ACTIVE水平有效像素数800
H_FRONT_PORCH行前肩40
H_SYNC_TIME行同步脉宽128
H_BACK_PORCH行后肩88
V_ACTIVE垂直有效行数480
V_FRONT_PORCH场前肩10
V_SYNC_TIME场同步脉宽2
V_BACK_PORCH场后肩33
PCLK像素时钟33.3 MHz

这几个参数共同决定了行总数和帧总数。水平一行的总时长由有效像素和三个消隐区间相加得到,垂直一帧的总行数同理。驱动模块需要精确数出每个像素的位置,并且在消隐区间里把 DE 拉低。

读这张表的关键是:前肩和后肩的顺序不能搞反。HSYNC 拉高(或拉低,取决于极性配置)之后,先是 H_BACK_PORCH,再是有效数据区,最后是 H_FRONT_PORCH,然后进入下一行。许多新人把前后肩写反,后果是画面整体左移或右移几十个像素,彩条照样能显示,但图像位置明显偏移,属于“能亮但不对”的典型症状。

2.3 像素时钟怎么定:由分辨率、帧率和消隐参数倒推

像素时钟不是随便设的,它要满足:PCLK = H_TOTAL × V_TOTAL × 帧率。其中 H_TOTAL 是行总数,V_TOTAL 是帧总行数。把这个公式反过来用,还能验算当前 PCLK 下实际帧率是多少。

下面这段脚本直接用 800x480 的参数算了一下,可以看到 33.3 MHz 像素时钟落在 60Hz 附近的帧率上,符合绝大多数液晶面板的标称刷新率:

h_act, h_fp, h_sync, h_bp = 800, 40, 128, 88 v_act, v_fp, v_sync, v_bp = 480, 10, 2, 33 pclk = 33.3e6 h_total = h_act + h_fp + h_sync + h_bp v_total = v_act + v_fp + v_sync + v_bp fps = pclk / (h_total * v_total) print(f"H_TOTAL = {h_total}, V_TOTAL = {v_total}") print(f"实际帧率 = {fps:.3f} Hz")

逻辑很简单,但这段计算藏着两点实际经验:第一,PCLK 不能为了省事随便取整数,比如 30 MHz 看起来“够用”,实际算下来帧率只有 54Hz 左右,肉眼可见地闪;第二,PLL 输出有时候和理论值差那么 1–2 MHz,用上面的公式算出来帧率稍偏,只要在面板允许范围内通常能接受,但如果偏得太多就会出现“亮度不均、慢慢滚动”的现象,这时候优先怀疑像素时钟而不是软件逻辑。

不同分辨率对应的常见 PCLK 参考值如下,注意不同面板的消隐参数不完全一样,实际以规格书的 H_TOTAL/V_TOTAL 为准:

分辨率典型 PCLK行总数帧总数
480x2729 MHz525286
800x48033.3 MHz1056525
1024x60051 MHz1344640

3. 工程结构与顶层例化:把 PLL、时序控制器、彩条生成器串起来

3.1 顶层 RTL:三个模块组成的最小驱动系统

整个工程从顶层看是三段式的:PLL 生成像素时钟,时序控制器生成行场同步信号和行列坐标,彩条生成器根据坐标输出 RGB 数据。下面是综合后的核心模块例化,直接反映数据流的方向:

module tft_colorbar_top( input wire clk_50m, // 板载 50MHz 时钟 input wire rst_n, // 按键复位,低有效 output wire tft_pclk, // 像素时钟,送给屏的 PCLK output wire tft_hsync, // 行同步 output wire tft_vsync, // 场同步 output wire tft_de, // 数据使能 output wire [15:0] tft_rgb // RGB565 数据 ); wire clk_pix; wire pll_locked; // 像素时钟生成:50MHz 输入,33.3MHz 输出 pll_33m u_pll ( .clk_in1 (clk_50m), .reset (~rst_n), .clk_out1(clk_pix), .locked (pll_locked) ); wire [23:0] rgb888; wire [11:0] x_cnt; wire [11:0] y_cnt; wire hsync_i, vsync_i, de_i; // 时序控制器:产生行场同步和坐标计数 tft_timing_gen #( .H_ACTIVE(800), .H_FRONT(40), .H_SYNC(128), .H_BACK(88), .V_ACTIVE(480), .V_FRONT(10), .V_SYNC(2), .V_BACK(33) ) u_timing ( .clk_pix (clk_pix), .rst_n (pll_locked), .hsync (hsync_i), .vsync (vsync_i), .de (de_i), .x_cnt (x_cnt), .y_cnt (y_cnt) ); // 彩条生成器:按坐标输出 RGB888 colorbar_gen u_colorbar ( .x_cnt (x_cnt), .y_cnt (y_cnt), .de (de_i), .rgb (rgb888) ); // RGB888 转 RGB565,DE 无效时输出 0 assign tft_rgb = de_i ? {rgb888[23:19], rgb888[15:10], rgb888[7:3]} : 16'd0; assign tft_de = de_i; assign tft_hsync = hsync_i; assign tft_vsync = vsync_i; assign tft_pclk = clk_pix; endmodule

这段代码里最值得注意的地方在最后几行:tft_rgb 不是直接把 rgb888 赋值过去,而是先按 RGB565 做了位宽截取,同时用 de_i 做门控。DE 无效区间把数据线拉成 0,而不是让它保持上一刻的值,这一点能避免消隐区间的浮空电平耦合到屏端造成噪点,很多“彩条边缘有雪花”的问题就是这里省了门控导致的。

顶层模块不写任何逻辑,它只负责例化,这也是工程实践里推荐的做法:模块职责单一,后续想换分辨率、换颜色深度,只需要改参数或者替换子模块,顶层的连接关系基本不动。

3.2 时序控制器:HSYNC、VSYNC、DE 到底怎么数出来的

时序控制器是整个工程的心脏,它决定了一个像素何时出现在数据线上。实现思路是用两个计数器:x_cnt 数行内像素,y_cnt 数当前行号。计数器的溢出边界由 H_TOTAL 和 V_TOTAL 决定。

module tft_timing_gen #( parameter H_ACTIVE = 800, parameter H_FRONT = 40, parameter H_SYNC = 128, parameter H_BACK = 88, parameter V_ACTIVE = 480, parameter V_FRONT = 10, parameter V_SYNC = 2, parameter V_BACK = 33 )( input wire clk_pix, input wire rst_n, output reg hsync, output reg vsync, output reg de, output reg [11:0] x_cnt, output reg [11:0] y_cnt ); localparam H_TOTAL = H_ACTIVE + H_FRONT + H_SYNC + H_BACK; localparam V_TOTAL = V_ACTIVE + V_FRONT + V_SYNC + V_BACK; // 水平计数 always @(posedge clk_pix or negedge rst_n) begin if (!rst_n) x_cnt <= 0; else if (x_cnt == H_TOTAL - 1) x_cnt <= 0; else x_cnt <= x_cnt + 1; end // 垂直计数:只在行计数回绕时加一 always @(posedge clk_pix or negedge rst_n) begin if (!rst_n) y_cnt <= 0; else if (x_cnt == H_TOTAL - 1) begin if (y_cnt == V_TOTAL - 1) y_cnt <= 0; else y_cnt <= y_cnt + 1; end end // 行同步:落在 [H_ACTIVE+H_FRONT, H_ACTIVE+H_FRONT+H_SYNC) 区间 always @(*) begin hsync = (x_cnt >= H_ACTIVE + H_FRONT) && (x_cnt < H_ACTIVE + H_FRONT + H_SYNC); end // 场同步:落在 [V_ACTIVE+V_FRONT, V_ACTIVE+V_FRONT+V_SYNC) 区间 always @(*) begin vsync = (y_cnt >= V_ACTIVE + V_FRONT) && (y_cnt < V_ACTIVE + V_FRONT + V_SYNC); end // 数据使能:水平、垂直都在有效区域内 always @(*) begin de = (x_cnt < H_ACTIVE) && (y_cnt < V_ACTIVE); end endmodule

这里的计数顺序和屏规格书是对应得上的。x_cnt 从 0 数到 H_TOTAL-1 然后回绕,代表一行结束;y_cnt 只有在 x_cnt 回绕的同一个时钟沿才加一,确保“一行只数一次”。hsync 的区间判断用的是左闭右开区间,别把边界写反,否则行同步脉冲宽度就会差一个时钟周期。

至于 hsync/vsync 的有效电平,大部分 RGB 屏是低有效,即拉低表示同步脉冲,但也有一些屏是高有效,这是最容易翻车的极性配置。如果手头没有规格书,可以先用逻辑分析仪抓一抓板子原来能正常显示的驱动时序,比猜靠谱得多。

3.3 从 RTL 到 bitstream:约束、综合、烧录四步走

工程写好之后,真正的考验在综合实现阶段。RGB 接口因为是直接裸驱,时序约束虽然不复杂,但缺了它综合工具就不知道你的像素时钟周期是多少,布局布线也不会为时序收敛做努力。

第一步是创建时钟约束,告诉工具板载 50MHz 时钟进来,然后 PLL 输出 33.3MHz 的像素时钟:

create_clock -name clk_50m -period 20.0 [get_ports clk_50m]

第二步是管脚约束,把顶层端口映射到 FPGA 的实际引脚。这一步必须和原理图严格对照,尤其是数据位序。RGB565 模式下哪一位接在哪一根线上,错一位颜色就乱了:

set_property PACKAGE_PIN E3 [get_ports clk_50m] set_property PACKAGE_PIN F4 [get_ports {tft_rgb[0]}] set_property PACKAGE_PIN F5 [get_ports {tft_rgb[1]}] set_property PACKAGE_PIN G3 [get_ports {tft_rgb[2]}] # 其余 tft_rgb 位、hsync/vsync/de/pclk 依此类推

第三步是综合和实现,在综合工具的 Tcl 控制台里执行标准流程。这里不依赖图形界面,几步命令就能跑完。

# 读入 RTL 文件 read_verilog src/tft_colorbar_top.v src/tft_timing_gen.v src/colorbar_gen.v # 读入约束文件 read_xdc constraints/tft_pins.xdc # 综合并生成比特流 synth_design -top tft_colorbar_top -part xc7a35tcsg324-1 opt_design place_design route_design write_bitstream -force tft_colorbar.bit

第四步是烧录。这里提醒一句:第一次上板建议不要把 PCLK 原始输出直接接到屏上,而是先看看屏的接口电平标准。RGB TFT 的数据引脚很多是 3.3V 或者 1.8V 逻辑,FPGA 的 bank 电压配置如果不对,pin 约束再正确也白搭,甚至可能把屏的芯片打坏。

4. 彩条数据生成与位宽映射:RGB888、RGB565 和偏色的第一现场

4.1 用像素坐标切出八条彩条

彩条的本质就是“按横坐标区间输出预设颜色”。标准彩条的顺序是白、黄、青、绿、品红、蓝、红、黑,按亮度从高到低排列,这种排列方式在实际测试里比随机颜色有用得多:既能看出颜色分量是否对应,也能一眼看出行列方向有没有装反。

module colorbar_gen #( parameter H_ACTIVE = 800, parameter BARS = 8 )( input wire [11:0] x_cnt, input wire [11:0] y_cnt, input wire de, output reg [23:0] rgb ); localparam BAR_W = H_ACTIVE / BARS; wire [2:0] bar_idx = x_cnt / BAR_W; always @(*) begin rgb = 24'h000000; if (de) begin case (bar_idx) 3'd0: rgb = 24'hFFFFFF; // 白 3'd1: rgb = 24'hFFFF00; // 黄 3'd2: rgb = 24'h00FFFF; // 青 3'd3: rgb = 24'h00FF00; // 绿 3'd4: rgb = 24'hFF00FF; // 品红 3'd5: rgb = 24'hFF0000; // 红 3'd6: rgb = 24'h0000FF; // 蓝 3'd7: rgb = 24'h000000; // 黑 endcase end end endmodule

bar_idx 的计算用的是除法,在综合工具里会综合成移位和比较器逻辑,因为除数是 2 的幂。800 除以 8 等于 100,正好是整数。如果屏分辨率不是 8 的整数倍,比如 1024x600 恰好也能整除,但有些分辨率不行,这时候可以改成累加判断,避免综合出昂贵的除法器。

4.2 RGB888 转 RGB565:为什么直接截断会出现色偏

很多 RGB 屏的数据接口只有 16 位,采用 RGB565 格式:R 占高 5 位,G 占中间 6 位,B 占低 5 位。而内部逻辑为了方便,通常以 RGB888 格式生成颜色。转换规则是保留高位,丢弃低位。

// RGB888 转 RGB565 assign rgb565[15:11] = rgb888[23:19]; assign rgb565[10:5] = rgb888[15:10]; assign rgb565[4:0] = rgb888[7:3];

这里有个很多人不知道的细节:直接截断低位不够好,更规范的做法是加低位抖色。比如 R 通道 8 位降到 5 位,截断会造成相邻灰阶肉眼可见的过渡断层。但对于彩条这种大色块应用,截断的误差肉眼完全看不出,所以工程里彩条一般直接截断就行。

真正导致偏色的常见原因是位序映射写错。比如把 R 通道接到了屏的 G 引脚上,或者 RGB565 拼接时把 G 的 6 位和 R 的 5 位顺序写反,颜色就会整体错乱。最典型的症状是:白色显示正常,但红色变成蓝色、绿色变成洋红。这种错乱靠调时序没用,只能回头对原理图和 XDC 管脚。

4.3 引脚分配与 DE 模式:对照原理图逐根核对

引脚分配是 RGB 屏调试里最磨人的环节,因为数据线太多,而且不同厂家的屏引脚顺序不一定按常规排列。

屏端引脚信号FPGA 端备注
1GNDGND参考地
2VCC3.3V逻辑电源
3PCLKtft_pclk像素时钟
4HSYNCtft_hsync可配置极性
5VSYNCtft_vsync可配置极性
6DEtft_de数据使能
7D0tft_rgb[0]B 分量 LSB
8D1tft_rgb[1]B 分量
............
22D15tft_rgb[15]R 分量 MSB

上表只是一个示例,实际板子千差万别。我的习惯是拿到原理图先把每一根数据线从屏端追到 FPGA 引脚,标出 RGB 分量归属,再写 XDC 约束文件。这一步看着枯燥,但能省掉上板后至少两小时的盲调时间。

DE 模式的接线比同步模式更简单:DE 信号拉高时数据线刚好对应一个有效像素。很多现代 RGB 屏驱动可以直接忽略 HSYNC/VSYNC,只靠 DE 判断有效区域,前提是这些同步端要按规格书接上,不能悬空。如果板子没有引出 DE,就得用同步模式,这时候要把控制器的 DE 信号去掉,靠行场同步信号推算有效区域,时序逻辑要复杂一些。

4.4 参数调测:改一处时序参数后该观察什么现象

调彩条时序时,最有效的观察对象不是颜色,而是彩条的位置、宽度和稳定性。如果彩条整体右移,说明 H_BACK_PORCH 偏大或者 H_FRONT_PORCH 偏小;如果彩条左右摇摆,多半是 HSYNC 极性配置反了;如果整帧图像上下滚动,是 VSYNC 频域和 PCLK 不匹配,本质上是帧率没算对。

给一个常见的调测顺序:

  1. 先定 PCLK,用示波器或逻辑分析仪确认 PLL 输出频率和规格书标称值的误差在 5% 以内。
  2. 再配 HSYNC/VSYNC 极性,观察屏能否稳定锁定画面,滚屏现象是否消失。
  3. 最后校 DE 边界,逐行数像素,确认有效区域相对屏幕居中。
  4. 如果屏上有测试图案(一般是 OSD 菜单),用它对比彩条边界,能快速判断左右偏移量。

5. 上板避坑与常见问题排查:黑屏、偏色、偏移从哪里查起

5.1 现象:上电后背光亮,但屏幕全黑或全白

这种现象在 RGB 屏调试里出现率最高。背光亮说明电源和背光驱动正常,全黑或全白说明数据链路没建立起来。

原因往往有两个:像素时钟没起来,或者 DE 信号从未拉高。PLL 没有 lock 时,clk_pix 输出可能是自由的时钟,但很多时候综合工具默认 PLL 未 lock 时输出为 0,屏端等不到有效像素时钟就保持黑屏。

解决方法是把 PLL 的 locked 信号接回顶层,并且让 locked 作为整套时序控制器的复位释放条件,而不是直接用按键复位。这样能保证屏端只有在像素时钟稳定输出后才开始接收数据。

// 正确做法:locked 同时作为时序控制器的异步复位释放 tft_timing_gen u_timing ( .clk_pix (clk_pix), .rst_n (pll_locked), .hsync (hsync_i), .vsync (vsync_i), .de (de_i), .x_cnt (x_cnt), .y_cnt (y_cnt) );

另外检查一下 DE 是否真的拉高了。用调试工具在 FPGA 内部抓 x_cnt、y_cnt 和 de,如果 de 始终为 0,那多半是时序控制器的有效区域判断条件写错,比如把 H_ACTIVE 写成 799,或者 de 逻辑里漏掉了 y_cnt 的判断。

5.2 现象:彩条能显示,但颜色顺序或色相错乱

彩条显示了,说明时序基本通了,这时候颜色错乱是纯数据映射问题。

最常见的原因是 RGB 分量接反。比如屏端数据线定义是 BGR 顺序,而 FPGA 端按 RGB 顺序输出,结果白色不变,但黄色和蓝色互换、红色和青色互换。这种错乱在彩条下的表现非常直观:标准的白黄青绿品红蓝黑序列会变成白蓝绿青红品黄黑。

解决方法是先确认屏规格书上的数据位定义,再看 XDC 引脚绑定。如果屏端是 BGR 排列,但 FPGA 端已经按物理引脚编号绑定,无法改线序,就在 RTL 里做分量交换:

// 屏端 BGR 排列,逻辑内部 RGB 输出,做一次分量重排 assign tft_r = rgb888[23:16]; assign tft_g = rgb888[15:8]; assign tft_b = rgb888[7:0]; // 如果确认屏端物理引脚顺序是 BGR,则交换输出 assign tft_rgb = {rgb888[7:0], rgb888[15:8], rgb888[23:16]};

还有一个隐蔽的问题:RGB565 模式下数据位宽不够,有些屏实际支持 RGB666(18 位),多出来的低位不接但悬空了,会造成低灰阶颜色不准。此时把未接的引脚做弱下拉或者输出 0,能消除部分随机色偏。

5.3 现象:画面闪烁、滚屏或彩条边缘毛刺

闪烁和滚屏往往是时序频率问题,不是逻辑错误。一个直接判断方法:用手机慢动作录像看滚屏方向,如果竖条纹不断从左往右跑,说明 HSYNC 频率和 PCLK 不同步;如果整屏从上往下反复滚,说明 VSYNC 关系没建立好。

先确认 PLL 输出的像素时钟频率是否精确。很多屏对 PCLK 的容忍范围在 ±10% 以内,超出就会周期性丢帧。如果频率没问,再看 HSYNC 极性是否配置反,极性反了会直接导致图像左右翻转或滚动。

彩条边缘毛刺则属于信号完整性问题。RGB 数据线在屏端没有终端匹配时,高速翻转的边沿反射会造成数据采样错误。解决方式有三选一:降低 PCLK、缩短排线长度、在数据线上串 22–33 欧姆电阻。如果是模块排线过长导致,最有效的是把 PCLK 做反相输出,让数据在时钟中间沿被采样,这个操作在很多场合能救回一条全是毛刺的排线。

// 反相像素时钟输出,让屏在数据稳定的中间沿采样 assign tft_pclk = ~clk_pix;

注意这招治标不治本,如果反相后毛刺消失了,说明数据建立时间确实偏紧,后续应该走背板布线优化。

5.4 排查顺序:从信号源到屏端的三板斧

遇到任何显示异常,先别乱改参数,按顺序查三层:

第一层查内部逻辑:用 FPGA 自带的逻辑分析仪抓 hsync、vsync、de、x_cnt、y_cnt,确认时序控制器的输出波形和想要的时序一致。第二层查外部信号:用示波器测量 FPGA 引脚处的 PCLK 和 DE,确认没有电平漂移、过冲或频偏。第三层查屏端接线:断电状态下用万用表量每一根数据线是否真的连通,特别是 FPC 排线的金手指接触点,很多时候是接触不良导致某一位数据悬浮。

这一套流程走下来,80% 的黑屏、偏色问题都能定位到具体环节。剩下的 20% 里,有一大半是电源问题,比如屏的背光驱动电流不够,导致亮度纹波在画面上形成水波纹,这种靠调时序解决不了,要查电源纹波。

6. 进阶:按键切彩条模式与自动量测时序参数的验证方法

6.1 按键切换标准彩条、纯色与网格测试图

彩条跑通之后,下一步值得做的是把纯色画面和网格测试图加进去。纯色画面能验证屏是否偏色,网格画面能验证行列方向有没有装反,这在批量验屏时比观察彩条效率更高。

// 按键消抖后切换三种测试图案 reg [1:0] test_mode; always @(posedge clk_50m or negedge rst_n) begin if (!rst_n) test_mode <= 2'd0; else if (key_press) test_mode <= test_mode + 2'd1; end // 在彩条生成器里根据模式选择输出 always @(*) begin case (test_mode) 2'd0: rgb = colorbar; // 标准彩条 2'd1: rgb = solid_color; // 单色纯色 2'd2: rgb = grid_pattern; // 白底黑格网格 default: rgb = 24'h000000; endcase end

按键消抖最稳妥的方式不是只做一次电平边沿检测,而是用计数器延时确认按键状态稳定之后再触发。之前的工程里我用过简单下降沿检测,结果按键按下瞬间的多段抖动让模式连跳两档,后来把消抖计数加到 20ms 才稳定下来。

6.2 不查规格书也能反推时序参数:用计数器统计有效区间

最后一招是我个人很喜欢的技巧。手头屏的规格书丢了,或者卖家给的参数表根本不准确,就直接用 FPGA 统计实际 DE 波形,反推有效区域和总周期。

思路是在帧同步信号到来时统计 DE 高电平的时钟数,得到 H_ACTIVE;再统计一帧内 DE 上升沿的次数,得到 V_ACTIVE;最后统计两次 VSYNC 之间的总像素数,得到 H_TOTAL 乘 V_TOTAL 的乘积。把这三个数字组合起来,就能直接算出屏幕的实际时序参数。

// 在 VSYNC 有效期间统计 DE 高电平数量 reg [15:0] de_count; always @(posedge clk_pix or negedge rst_n) begin if (!rst_n) de_count <= 16'd0; else if (vsync_i && y_cnt == V_ACTIVE) de_count <= de_count + de_i; end

统计出来的数字可以直接写进时序参数模块,不需要逐项查规格书。这个方法的可靠性相当高,因为 DE 波形反映的是屏控制器实际期望的时序,而不是规格书上理论上写的那组数字。从那以后,我每次外购新屏都不再盲目相信卖家文档,先烧彩条、再统计 DE、最后对照标准彩条做颜色校验,这套流程三板斧走完,屏能不能用基本有定论。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询