第29章:RGB LCD彩条显示实验
本章对应原书第 686~718 页,属于「第五篇 实战篇」。
本章在讲什么?(先看这个)
屏幕是怎么被"点亮"的?这一章是进入视频显示世界的第一课。你要学会 RGB LCD 屏的核心概念:像素、分辨率、像素格式(RGB888)、以及最重要的——行场扫描时序(HSYNC/VSYNC/DE)。FPGA 会像一支"画笔",按顺序逐个像素地给屏幕上色。
实验任务是驱动正点原子全系 RGB LCD 屏(4.3 寸到 10.1 寸),在屏幕上显示五条彩条。这一章的时序知识是后面 HDMI、触摸屏、字符显示等所有显示类实验的地基,务必吃透。
核心概念拆解
29.1 RGB TFT-LCD屏简介
TFT-LCD(Thin Film Transistor-Liquid Crystal Display,薄膜晶体管液晶显示屏)的每个像素点都由集成在液晶后面的薄膜晶体管独立驱动,响应速度快、图像质量好。从使用角度,我们不需要深究液晶怎么转动,只需要关注下面几个重点。
分辨率
LCD 屏由一个一个的像素点组成,每个像素点像一个"小灯",由 R(红)、G(绿)、B(蓝)三种颜色组成——RGB 是光的三基色。1080P 的意思是屏幕有 1920×1080 = 2073600 个像素点(横轴 X 有 1920 列,竖轴 Y 有 1080 行),第一个像素点在左上角 A,最后一个在右下角 C:

屏幕尺寸不变时,分辨率越高越清晰;分辨率不变时,屏幕越小越细腻(同是 1080P,5 寸手机比 24 寸显示器细腻得多)。但分辨率只是屏幕好坏的参数之一,还有色彩还原、亮度、可视角度、刷新率等。
像素格式
R、G、B 三部分通常各用 8bit,一个像素点就是 24bit(3 个字节),称为 RGB888:

- bit23~16:RED 通道;bit15~8:GREEN 通道;bit7~0:BLUE 通道;
- 红色 = 24'hFF0000,绿色 = 24'h00FF00,蓝色 = 24'h0000FF;
- 24'hFFFF00 是黄色,24'h000000 是黑色,24'hFFFFFF 是白色。
常用的还有 RGB565(2 字节),但色彩较鲜艳度差一些。想知道任意颜色的数值?用电脑"画图"工具的调色板就能查:

LCD屏接口
RGB LCD 屏接口的信号线如下:
| 信号线 | 描述 |
|---|---|
| R[7:0] | 8 根红色数据线 |
| G[7:0] | 8 根绿色数据线 |
| B[7:0] | 8 根蓝色数据线 |
| DE | 数据使能线 |
| VSYNC | 垂直同步信号线 |
| HSYNC | 水平同步信号线 |
| PCLK | 像素时钟信号线 |
正点原子有五款 RGB LCD 屏:ATK-4342(4.3 寸 480×272,已停产)、ATK-4384(4.3 寸 800×480)、ATK-7084(7 寸 800×480)、ATK-7016(7 寸 1024×600)、ATK-1018(10.1 寸 1280×800)。以 ATK-7016 为例,接口是一个 40PIN FPC 座(0.5mm 间距):

RGB LCD 屏没有读写寄存器,所以没有真正的 ID,模块通过 R7/G7/B7 三个引脚的上/下拉来自定义屏幕 ID,程序读这三个脚就能知道当前接的是哪款屏,从而兼容不同屏幕:
| M2(LCD_B7) | M1(LCD_G7) | M0(LCD_R7) | LCD ID | 说明 |
|---|---|---|---|---|
| 0 | 0 | 0 | ATK-4342 | 480*272 |
| 0 | 0 | 1 | ATK-7084 | 800*480 |
| 0 | 1 | 0 | ATK-7016 | 1024*600 |
| 1 | 0 | 0 | ATK-4384 | 800*480 |
| 1 | 0 | 1 | ATK-1018 | 1280*800 |
LCD屏时间参数:那一圈"黑边"是怎么来的
把屏幕显示想象成一支"画笔"从左到右、从上到下逐个像素涂色,扫完一屏就画好了一帧:

图中可以看到有效显示区外有一圈"黑边",这要追溯到 CRT 显示器的"祖先":CRT 靠电子枪逐行打荧光粉,扫完一行要关掉电子枪快速移回左边(走 Z 形轨迹),扫完一帧要移回左上角。这些移动过程对应的延时就是 HBP、HFP、VBP、VFP。如今的 LCD 屏虽然不需要电子枪了,但屏内 IC 处理一行/一帧数据也需要反应时间,所以这四个参数被保留下来,作用是锁定有效像素数据。
- HSYNC:行同步信号,有效时表示开始显示新的一行;
- VSYNC:帧同步信号,有效时表示开始显示新的一帧。
行时序与帧时序
行显示时序的参数:

- HSPW:行同步信号宽度(HSYNC 持续时间,单位 CLK);
- HBP:行显示后沿(后肩);HOZVAL:行有效显示区(如 1024600 屏为 1024);HFP*:行显示前沿(前肩)。
HSYNC 发出后要等 HSPW+HBP 个 CLK 才出现有效像素数据;一行显示完再等 HFP 个 CLK 才发下一个 HSYNC。所以显示一行的时间 = HSPW + HBP + HOZVAL + HFP。
帧显示时序类似,只是单位变成"行时间":

显示一帧的时间 = (VSPW + VBP + LINE + VFP) × (HSPW + HBP + HOZVAL + HFP)。
配置一款 RGB LCD 屏需要 8 个参数:HSPW、HBP、HOZVAL、HFP、VSPW、VBP、LINE、VFP。
同步模式与像素时钟
RGB LCD 有两种数据同步方式:
- HV Mode(行场同步模式):HSYNC 和 VSYNC 都作为同步信号,DE 必须为低;
- DE Mode(数据使能模式):只有同时处于帧有效区和行有效区时 DE 才为高,此时 VSYNC 和 HSYNC 必须为高。
RGB LCD 屏一般都支持 DE 模式,本章就采用 DE 同步模式。
像素时钟怎么算?以 ATK-7016(1024×600)为例:一帧需要 (3+20+600+12) × (20+140+1024+160) = 635 × 1344 = 853440 个时钟;刷新率 60Hz 时需要 853440 × 60 ≈ 51.2MHz 的像素时钟。实践中不用这么严格,给 ATK-7016 输出 50MHz 即可,刷新率接近 60Hz 且代码好写。
29.2 硬件设计
底板左侧板载 1 个 RGB LCD 接口:

FPGA 引脚输出的颜色数据为 24bit RGB888。特别注意:LCD_R7、LCD_G7、LCD_B7 既作为颜色数据输出,又用来读取屏幕 ID,所以这 24 位数据线对 FPGA 是双向引脚:

模块上以 T 开头的 5 个信号(T_PEN、T_SCK 等)连接触摸芯片,本实验不涉及触摸,后续触摸屏实验再讲。
29.3 实验任务
用开发板的 RGB LCD 接口驱动 RGB LCD 屏(兼容全系型号),显示彩条。
29.4 程序设计
整体模块设计
要兼容所有屏幕,就得先知道接的是哪款屏——所以有四个功能模块加一个顶层:

- 读ID模块(rd_id):上电时把 RGB 双向总线设为输入,读取 lcd_rgb 得到屏幕 ID;
- 时钟分频模块(clk_div):根据 ID 输出对应频率的像素时钟 lcd_pclk;
- LCD显示模块(lcd_display):根据像素坐标输出彩条数据 pixel_data;
- LCD驱动模块(lcd_driver):根据 ID 输出对应时序参数,驱动屏幕并把彩条显示上去。

顶层模块为 lcd_rgb_colorbar,完成各模块例化。
读ID模块
lcd_rgb[7](B7)、lcd_rgb[15](G7)、lcd_rgb[23](R7)分别对应 M2、M1、M0,并译码成 16 位 ID(如 3'b010 → 16'h7016,表示 1024×600 的 7 寸屏)。

ID 只需要读一次,用 rd_flag 标志:rd_flag=0 时读一次 ID 并置 1,之后不再读取:

module rd_id(
input clk , //时钟
input rst_n , //复位,低电平有效
input [23:0] lcd_rgb, //RGB LCD 像素数据,用于读取ID
output reg [15:0] lcd_id //LCD 屏ID
);
//reg define
reg rd_flag; //读ID 标志
//获取LCD ID M2:B7 M1:G7 M0:R7
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rd_flag <= 1'b0;
lcd_id <= 16'd0;
end
else begin
if(rd_flag == 1'b0) begin
rd_flag <= 1'b1;
case({lcd_rgb[7],lcd_rgb[15],lcd_rgb[23]})
3'b000 : lcd_id <= 16'h4342; //4.3' RGB LCD RES:480x272
3'b001 : lcd_id <= 16'h7084; //7' RGB LCD RES:800x480
3'b010 : lcd_id <= 16'h7016; //7' RGB LCD RES:1024x600
3'b100 : lcd_id <= 16'h4384; //4.3' RGB LCD RES:800x480
3'b101 : lcd_id <= 16'h1018; //10' RGB LCD RES:1280x800
default : lcd_id <= 16'd0;
endcase
end
end
end
endmodule

时钟分频模块
各屏幕需要的像素时钟:480×272 为 12.5MHz,800×480 和 1024×600 为 25/50MHz 档,1280×800 理论要 70MHz(需 MMCM/PLL IP 核才能产生,这里简化为输出 50MHz)。本设计用计数分频从 50MHz 系统时钟得到 12.5MHz(4 分频)、25MHz(2 分频),50MHz 直接用系统时钟:


module clk_div(
input clk, //50MHz
input rst_n,
input [15:0] lcd_id,
output reg lcd_pclk
);
//reg define
reg clk_25m;
reg clk_12_5m;
reg div_4_cnt;
//时钟2 分频 输出25MHz 时钟
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
clk_25m <= 1'b0;
else
clk_25m <= ~clk_25m;
end
//时钟4 分频 输出12.5MHz 时钟
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
div_4_cnt <= 1'b0;
clk_12_5m <= 1'b0;
end
else begin
div_4_cnt <= div_4_cnt + 1'b1;
if(div_4_cnt == 1'b1)
clk_12_5m <= ~clk_12_5m;
else
clk_12_5m <= clk_12_5m;
end
end
always @(*) begin
case(lcd_id)
16'h4342 : lcd_pclk = clk_12_5m;
16'h7084 : lcd_pclk = clk_25m;
16'h7016 : lcd_pclk = clk;
16'h4384 : lcd_pclk = clk_25m;
16'h1018 : lcd_pclk = clk;
default : lcd_pclk = 1'b0;
endcase
end
endmodule

LCD驱动模块(本章核心)
LCD 驱动模块用 DE 时序把彩条数据扫描显示到屏幕上:

要点:DE 模式下行场同步信号直接拉高(lcd_hs = lcd_vs = 1'b1),背光 lcd_bl 和复位 lcd_rst 也直接赋 1。以 4384 屏(800×480)为例,时序参数为:h_sync=128、h_back=88、h_disp=800、h_total=1056;v_sync=2、v_back=33、v_disp=480、v_total=525。
模块内部用两个计数器搭出扫描框架:h_cnt(行计数器,对像素时钟计数,0~h_total-1 循环)和 v_cnt(场计数器,每扫完一行加 1,0~v_total-1 循环)。

lcd_de 拉高的条件:h_cnt ≥ h_sync+h_back 且 < h_sync+h_back+h_disp,同时 v_cnt ≥ v_sync+v_back 且 < v_sync+v_back+v_disp。注意 data_req 比 lcd_de 提前一个时钟周期,这是给像素数据生成预留的一拍数据通路延时:


核心代码:
module lcd_driver(
input lcd_pclk, //时钟
input rst_n, //复位,低电平有效
input [15:0] lcd_id, //LCD 屏ID
input [23:0] pixel_data, //像素数据
output reg [10:0] pixel_xpos, //当前像素点横坐标
output reg [10:0] pixel_ypos, //当前像素点纵坐标
output reg [10:0] h_disp, //LCD 屏水平分辨率
output reg [10:0] v_disp, //LCD 屏垂直分辨率
output reg data_req, //数据请求信号
//RGB LCD 接口
output reg lcd_de, //LCD 数据使能信号
output lcd_hs, //LCD 行同步信号
output lcd_vs, //LCD 场同步信号
output lcd_bl, //LCD 背光控制信号
output lcd_clk, //LCD 像素时钟
output lcd_rst, //LCD 复位
output [23:0] lcd_rgb //LCD RGB888 颜色数据
);
//parameter define
// 4.3' 480*272
parameter H_SYNC_4342 = 11'd41; //行同步
parameter H_BACK_4342 = 11'd2; //行显示后沿
parameter H_DISP_4342 = 11'd480; //行有效数据
parameter H_FRONT_4342 = 11'd2; //行显示前沿
parameter H_TOTAL_4342 = 11'd525; //行扫描周期
parameter V_SYNC_4342 = 11'd10; //场同步
parameter V_BACK_4342 = 11'd2; //场显示后沿
parameter V_DISP_4342 = 11'd272; //场有效数据
parameter V_FRONT_4342 = 11'd2; //场显示前沿
parameter V_TOTAL_4342 = 11'd286; //场扫描周期
// ……其余分辨率参数同理(7084/7016/4384/1018),此处省略
//reg define
reg [10:0] h_sync ;
reg [10:0] h_back ;
reg [10:0] h_total;
reg [10:0] v_sync ;
reg [10:0] v_back ;
reg [10:0] v_total;
reg [10:0] h_cnt ;
reg [10:0] v_cnt ;
//RGB LCD 采用DE 模式时,行场同步信号需要拉高
assign lcd_hs = 1'b1; //LCD 行同步信号
assign lcd_vs = 1'b1; //LCD 场同步信号
assign lcd_bl = 1'b1; //LCD 背光控制信号
assign lcd_clk = lcd_pclk; //LCD 像素时钟
assign lcd_rst= 1'b1; //LCD 复位
//RGB888 数据输出
assign lcd_rgb = lcd_de ? pixel_data : 24'd0;
//像素点x 坐标
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
pixel_xpos <= 11'd0;
else if(data_req)
pixel_xpos <= h_cnt + 11'd2 - h_sync - h_back ;
else
pixel_xpos <= 11'd0;
end
//像素点y 坐标
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
pixel_ypos <= 11'd0;
else if(v_cnt >= (v_sync + v_back) && v_cnt < (v_sync + v_back + v_disp))
pixel_ypos <= v_cnt + 11'd1 - (v_sync + v_back) ;
else
pixel_ypos <= 11'd0;
end
//行场时序参数(根据lcd_id选择对应分辨率参数,省略部分case分支)
always @(*) begin
case(lcd_id)
16'h4342 : begin
h_sync = H_SYNC_4342;
h_back = H_BACK_4342;
h_disp = H_DISP_4342;
h_total = H_TOTAL_4342;
v_sync = V_SYNC_4342;
v_back = V_BACK_4342;
v_disp = V_DISP_4342;
v_total = V_TOTAL_4342;
end
// ……其余ID分支省略
endcase
end
//数据使能信号
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
lcd_de <= 1'b0;
else
lcd_de <= data_req;
end
//请求像素点颜色数据输入(比lcd_de提前一拍)
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
data_req<=1'b0;
else if((h_cnt >= h_sync + h_back - 11'd2) && (h_cnt < h_sync + h_back + h_disp - 11'd2)
&& (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp))
data_req <= 1'b1;
else
data_req <= 1'b0;
end
//行计数器对像素时钟计数
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
h_cnt <= 11'd0;
else begin
if(h_cnt == h_total - 11'd1)
h_cnt <= 11'd0;
else
h_cnt <= h_cnt + 11'd1;
end
end
//场计数器对行计数
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
v_cnt <= 11'd0;
else begin
if(h_cnt == h_total - 11'd1) begin
if(v_cnt == v_total - 11'd1)
v_cnt <= 11'd0;
else
v_cnt <= v_cnt + 11'd1;
end
end
end
endmodule


仿真验证了 lcd_de 在 v_cnt 为 35~514、h_cnt 为 215~1015 范围内有效,且 data_req 比 lcd_de 提前一拍。
LCD显示模块
功能很简单:把横坐标 pixel_xpos 在有效显示区内分成五等份,每份填一种颜色(白、黑、红、绿、蓝):


module lcd_display(
input lcd_pclk, //时钟
input rst_n, //复位,低电平有效
input [10:0] pixel_xpos, //当前像素点横坐标
input [10:0] pixel_ypos, //当前像素点纵坐标
input [10:0] h_disp, //LCD 屏水平分辨率
input [10:0] v_disp, //LCD 屏垂直分辨率
output reg [23:0] pixel_data //像素数据
);
//parameter define
parameter WHITE = 24'hFFFFFF; //白色
parameter BLACK = 24'h000000; //黑色
parameter RED = 24'hFF0000; //红色
parameter GREEN = 24'h00FF00; //绿色
parameter BLUE = 24'h0000FF; //蓝色
//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
always @(posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
pixel_data <= BLACK;
else begin
if((pixel_xpos >= 11'd0) && (pixel_xpos < h_disp/5*1))
pixel_data <= WHITE;
else if((pixel_xpos >= h_disp/5*1) && (pixel_xpos < h_disp/5*2))
pixel_data <= BLACK;
else if((pixel_xpos >= h_disp/5*2) && (pixel_xpos < h_disp/5*3))
pixel_data <= RED;
else if((pixel_xpos >= h_disp/5*3) && (pixel_xpos < h_disp/5*4))
pixel_data <= GREEN;
else
pixel_data <= BLUE;
end
end
endmodule
顶层模块
顶层的关键是处理 lcd_rgb 双向引脚的方向切换:lcd_de 为高时(输出像素有效)把引脚切成输出,连接 lcd_rgb_o;lcd_de 为低时把引脚切成高阻态 'Z',此时可以读取引脚电平获取屏幕 ID:
module lcd_rgb_colorbar(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位
//RGB LCD 接口
output lcd_de, //LCD 数据使能信号
output lcd_hs, //LCD 行同步信号
output lcd_vs, //LCD 场同步信号
output lcd_bl, //LCD 背光控制信号
output lcd_clk, //LCD 像素时钟
output lcd_rst, //LCD 复位
inout [23:0] lcd_rgb //LCD RGB888 颜色数据
);
//wire define
wire [15:0] lcd_id ;
wire lcd_pclk ;
wire [10:0] pixel_xpos;
wire [10:0] pixel_ypos;
wire [10:0] h_disp ;
wire [10:0] v_disp ;
wire [23:0] pixel_data;
wire [23:0] lcd_rgb_o ;
wire [23:0] lcd_rgb_i ;
//像素数据方向切换
assign lcd_rgb = lcd_de ? lcd_rgb_o : {24{1'bz}};
assign lcd_rgb_i = lcd_rgb;
//读LCD ID 模块
rd_id u_rd_id(
.clk (sys_clk ),
.rst_n (sys_rst_n),
.lcd_rgb (lcd_rgb_i),
.lcd_id (lcd_id )
);
//时钟分频模块
clk_div u_clk_div(
.clk (sys_clk ),
.rst_n (sys_rst_n),
.lcd_id (lcd_id ),
.lcd_pclk (lcd_pclk )
);
//LCD 显示模块
lcd_display u_lcd_display(
.lcd_pclk (lcd_pclk ),
.rst_n (sys_rst_n ),
.pixel_xpos (pixel_xpos),
.pixel_ypos (pixel_ypos),
.h_disp (h_disp ),
.v_disp (v_disp ),
.pixel_data (pixel_data)
);
//LCD 驱动模块
lcd_driver u_lcd_driver(
.lcd_pclk (lcd_pclk ),
.rst_n (sys_rst_n ),
.lcd_id (lcd_id ),
.pixel_data (pixel_data),
.pixel_xpos (pixel_xpos),
.pixel_ypos (pixel_ypos),
.h_disp (h_disp ),
.v_disp (v_disp ),
.data_req ( ),
.lcd_de (lcd_de ),
.lcd_hs (lcd_hs ),
.lcd_vs (lcd_vs ),
.lcd_bl (lcd_bl ),
.lcd_clk (lcd_clk ),
.lcd_rst (lcd_rst ),
.lcd_rgb (lcd_rgb_o )
);
endmodule
仿真验证
TestBench 的关键点:模拟屏幕 ID 输入。4384 屏的 ID 为 3'b100,反映到 lcd_rgb 引脚上是 24'h80(只有 bit7 为 1)。在 lcd_de 为低时给 lcd_rgb 赋 24'h80 即可:
`timescale 1ns / 1ns
module tb_lcd_rgb_colorbar();
//reg define
reg sys_clk;
reg sys_rst_n;
//wire define
wire lcd_de ;
wire lcd_hs ;
wire lcd_vs ;
wire lcd_bl ;
wire lcd_clk;
wire [23:0] lcd_rgb;
always #10 sys_clk = ~sys_clk;
//lcd_de为低时提供ID激励(4384屏ID为24'h80),为高时释放总线
assign lcd_rgb = lcd_de ? {24{1'bz}} : 24'h80;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
end
lcd_rgb_colorbar u_lcd_rgb_colorbar(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.lcd_de (lcd_de ),
.lcd_hs (lcd_hs ),
.lcd_vs (lcd_vs ),
.lcd_bl (lcd_bl ),
.lcd_clk (lcd_clk),
.lcd_rgb (lcd_rgb)
);
endmodule

波形中 lcd_rgb 在 lcd_de 拉高范围内依次输出白(24'hffffff)、黑(24'h000000)、红(24'hff0000)、绿(24'h00ff00)、蓝(24'h0000ff)五种颜色,符合设计。
29.5 下载验证
管脚分配(部分,全部 24 位 lcd_rgb 都需约束,此处列出控制信号和说明):
| 信号名 | 方向 | 管脚 | 说明 |
|---|---|---|---|
| sys_clk | input | U18 | 系统时钟 50M |
| sys_rst_n | input | N16 | 复位,低有效 |
| lcd_rgb[23:0] | inout | U17/W16/…(每位一个脚) | RGB888 颜色数据(双向) |
| lcd_hs | output | N18 | 行同步 |
| lcd_vs | output | T20 | 场同步 |
| lcd_de | output | U20 | 数据使能 |
| lcd_bl | output | M20 | 背光控制 |
| lcd_clk | output | P19 | 像素时钟 |
| lcd_rst | output | L17 | 复位 |
约束语句示例(完整工程需对 lcd_rgb[0]~[23] 全部约束):
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN W18 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb[0]}]
# …… lcd_rgb[1]~[23] 同理,依次为 W19/R16/R17/W20/V20/P18/N17/V17/V18/T17/R18/Y18/Y19/P15/P16/V16/W16/T14/T15/Y17/Y16/T16/U17
set_property -dict {PACKAGE_PIN N18 IOSTANDARD LVCMOS33} [get_ports lcd_hs]
set_property -dict {PACKAGE_PIN T20 IOSTANDARD LVCMOS33} [get_ports lcd_vs]
set_property -dict {PACKAGE_PIN U20 IOSTANDARD LVCMOS33} [get_ports lcd_de]
set_property -dict {PACKAGE_PIN M20 IOSTANDARD LVCMOS33} [get_ports lcd_bl]
set_property -dict {PACKAGE_PIN P19 IOSTANDARD LVCMOS33} [get_ports lcd_clk]
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports lcd_rst]
上板步骤:将 FPC 排线一端接 LCD 模块 J1 接口(掀开黑色翻盖、蓝色面朝上插入、压下翻盖固定),另一端接开发板 RGB TFTLCD 接口;连接 JTAG 下载器与电源,下载程序后屏幕显示五色彩条即验证成功。

初学者容易踩的坑
- 时序参数记不住 8 个就抓瞎:HSPW/HBP/HOZVAL/HFP、VSPW/VBP/LINE/VFP,行单位是 CLK、场单位是"行时间",配置不同屏幕务必对照各自参数表,不能张冠李戴。
- lcd_rgb 是双向引脚:它既要输出像素数据,又要在上电时读屏幕 ID(R7/G7/B7),顶层必须用三态门做方向切换(
lcd_de ? lcd_rgb_o : 24'bz),忘了这步就读不到 ID。 - data_req 比 lcd_de 提前一拍:像素数据是时序逻辑生成,滞后一拍;如果直接用 lcd_de 请求数据,屏幕上会出现错位或第一条线异常。
- DE 模式下行场同步要拉高:
lcd_hs、lcd_vs直接赋 1,别按 HV 模式的思路去输出同步脉冲。 - 10.1 寸屏时钟只有近似值:1280×800 严格要 70MHz,计数分频得不到,须用 MMCM/PLL IP 核;本例程用 50MHz 是有意为之的简化,显示刷新率略低于 60Hz。
- FPC 排线方向别插反:蓝色面朝上、掀盖压紧,接触不良会导致花屏或无显示。
小结 & 下一步
本章建立了 RGB LCD 显示的完整概念链:像素 → RGB888 格式 → 行场扫描时序(HSPW/HBP/HFP、VSPW/VBP/VFP)→ DE 同步模式 → 像素时钟计算。工程上用"读 ID → 分频出像素时钟 → 行场计数器生成时序与坐标 → 按坐标填颜色"的套路,一次兼容五款屏幕。这套 h_cnt/v_cnt 扫描框架是所有显示实验的母版。
动手建议:完成拓展训练——把彩条从竖向改成横向显示(把判断条件从 pixel_xpos 换成 pixel_ypos 即可),体会坐标与颜色的映射关系。