一、RGB_LCD简介与实验任务
1.1 RGB_LCD简介
TFT-LCD,即薄膜晶体管液晶显示屏。它显示的每个像素点都是由集成在液晶后面的薄膜晶体管独立驱动。使用LCD需要从使用的角度去关注 LCD 的几个重要点:
1.分辨率: LCD 显示器分辨率一般分为 720P、1080P、2K 或 4K等。RGB 灯由 R(红色)、G(绿色)和 B(蓝色)这三种颜色组成的。如:1080P 的意思就是一个 LCD 屏幕上的像素数量是 19201080 个,也就是这个屏幕一列 1080 个像素点,一共 1920 列。
2.像素格式:RGB888像素格式一个 R、G、B 这三部分分别使用8bit 的数据,一个像素点就是 8bit3=24bit,一个像素点 3 个字节。像素点格式还有 RGB565,只需要两个字节,但在色彩鲜艳度上较差一些。本实验采用的LCD屏幕的像素格式为RGB888。
3.LCD 屏幕接口:LCD 屏幕或者说显示器有很多种接口,比如在显示器上常见的 VGA、HDMI、DP 等等,开发板支持RGB 接口的 LCD 和 HDMI 接口的显示器。本次实验采用的是 RGB LCD 接口:R[7:0]、G[7:0]和 B[7:0]是 24 位数据,DE、VSYNC、HSYNC 和PCLK 是四个控制信号。LCD_R7/G7/B7 则用来设置 LCD的 ID。本次实验采用ATK-7016 RGB LCD模块,LCD的 ID = 010 即可。
4.LCD 时间参数。一帧图像也是由一行一行组成的。HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了。VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了。假如一个 LCD 的分辨率为 1024600,那么其扫
描如图 28.1.6 所示:
5.RGB LCD 屏幕时序:
RGB LCD 的行显示时序如下图所示:
**HSYNC:**行同步信号,当此信号有效的时候就表示开始显示新的一行数据。
**HSPW:**行同步信号宽度,也就是 HSYNC 信号持续时间。
**HBP:**行显示后沿(或后肩)。
HOZVAL:行有效显示区域,即显示一行数据所需的时间,假如屏幕分辨率为 1024600,那么HOZVAL 就是 1024,单位为 CLK。
HFP:行显示前沿(或前肩)。
所以显示一行所需要的时间:HSPW + HBP + HOZVAL + HFP。
RGB LCD 的帧显示时序如下图所示:
**VSYNC:**帧(场)同步信号,当此信号有效的时候就表示开始显示新的一帧数据
**VSPW:**帧同步信号宽度。
VBP:帧显示后沿(或后肩)。
LINE:帧有效显示区域,即显示一帧数据所需的时间。
VFP:帧显示前沿(或前肩)。
所以显示一帧所需要的时间:VSPW+VBP+LINE+VFP
最终的计算公式:T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
两种数据同步方式,一种是行场同步模式(HV Mode),另一种是数据使能同步模式(DE Mode)。
当选择HV行场同步模式时,LCD 接口的时序与 VGA 接口的时序图非常相似,只是参数不同。行同步信号(HSYNC)和场同步信号(VSYNC)作为数据的同步信号,此时数据使能信号(DE)必须为低电平。
当选择 DE 同步模式时,LCD 的 DE 信号作为数据的有效信号。只有同时扫描到帧有效显示区域和行有效显示区域时,DE 信号才有效(高电平)。当选择 DE 同步模式时,此时行场同步信号 VS 和 HS 必须为高电平。
本实验采用 DE 同步的方式驱动 LCD 液晶屏。
6.像素时钟
本次实验显示一帧图像所需要的时钟数就是:N(CLK)= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160) = 635 * 1344 = 853440
显示一帧图像需要 853440 个时钟数,那么显示 60 帧就是:853440 * 60 = 51206400≈51.2M,所以像素时钟就是 51.2MHz。不是必须严格采用51.2M,所以我们采用50MHZ。
1.2 实验任务
本节的实验任务是使用达芬奇开发板上的 RGB-LCD 接口,驱动 RGB-LCD 液晶屏(支持目前推出的所有 RGB-LCD 屏),并显示出彩条。
二、程序设计
2.1 总体模块设计
FPGA 部分包括五个模块,顶层模块(lcd_rgb_colorbar)、读取 ID 模块(rd_id)、时钟分频模块(clk_div)、LCD 显示模块(lcd_display)以及 LCD 驱动模块(lcd_driver),在顶层模块中完成对其余模块的例化。
RTL代码如下图所示:
module lcd_rgb_colorbar(
input sys_clk,
input sys_rst_n,
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复位
inout [23:0] lcd_rgb
);
//wire define
wire [15:0] lcd_id ; //LCD屏ID
wire lcd_pclk ; //LCD像素时钟
wire [10:0] pixel_xpos; //当前像素点横坐标
wire [10:0] pixel_ypos; //当前像素点纵坐标
wire [10:0] h_disp ; //LCD屏水平分辨率
wire [10:0] v_disp ; //LCD屏垂直分辨率
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
endmodule
2.2 读 ID 模块设计
读ID模块框图如下所示:
波形图如下:
RTL代码如下:
module id_rd(
input clk,
input rst_n,
input [23:0] lcd_rgb,
output reg [15:0] lcd_id
);
reg rd_flag; //判断是否已读
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
lcd_id <= 16'd0;
rd_flag <= 1'b0;
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;
3'b001 : lcd_id <= 16'h7084;
3'b010 : lcd_id <= 16'h7016;
3'b100 : lcd_id <= 16'h4384;
3'b101 : lcd_id <= 16'h1018;
default: lcd_id <= 16'h0;
endcase
end
else
lcd_id <= lcd_id;
end
end
endmodule
2.3 时钟分频模块设计
分频模块根据输入的 LCD ID,来输出不同频率的像素时钟 lcd_pclk。时钟分频模块如下所示:
波形图如下:
RTL代码如下:
module clk_div(
input clk,
input rst_n,
input [15:0] lcd_id,
output reg lcd_pclk
);
wire clk_25m;
wire clk_12_5m;
clk_wiz_0 instance_name
(
// Clock out ports
.clk_25m(clk_25m), // output clk_25m
.clk_12_5m(clk_12_5m), // output clk_12_5m
// Status and control signals
.reset(rst_n), // input reset
.locked( ), // output locked
// Clock in ports
.clk_in1(clk)); // input clk_in1
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;
end
endmodule
2.4 LCD 驱动模块设计
LCD 驱动模块设计:
RTL代码设计:
module lcd_driver(
input lcd_pclk,
input rst_n,
input [15:0] 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,
output reg [10:0] v_disp,
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
);
//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; //场扫描周期
// 7' 800*480
parameter H_SYNC_7084 = 11'd128; //行同步
parameter H_BACK_7084 = 11'd88; //行显示后沿
parameter H_DISP_7084 = 11'd800; //行有效数据
parameter H_FRONT_7084 = 11'd40; //行显示前沿
parameter H_TOTAL_7084 = 11'd1056; //行扫描周期
parameter V_SYNC_7084 = 11'd2; //场同步
parameter V_BACK_7084 = 11'd33; //场显示后沿
parameter V_DISP_7084 = 11'd480; //场有效数据
parameter V_FRONT_7084 = 11'd10; //场显示前沿
parameter V_TOTAL_7084 = 11'd525; //场扫描周期
// 7' 1024*600
parameter H_SYNC_7016 = 11'd20; //行同步
parameter H_BACK_7016 = 11'd140; //行显示后沿
parameter H_DISP_7016 = 11'd1024; //行有效数据
parameter H_FRONT_7016 = 11'd160; //行显示前沿
parameter H_TOTAL_7016 = 11'd1344; //行扫描周期
parameter V_SYNC_7016 = 11'd3; //场同步
parameter V_BACK_7016 = 11'd20; //场显示后沿
parameter V_DISP_7016 = 11'd600; //场有效数据
parameter V_FRONT_7016 = 11'd12; //场显示前沿
parameter V_TOTAL_7016 = 11'd635; //场扫描周期
// 10.1' 1280*800
parameter H_SYNC_1018 = 11'd10; //行同步
parameter H_BACK_1018 = 11'd80; //行显示后沿
parameter H_DISP_1018 = 11'd1280; //行有效数据
parameter H_FRONT_1018 = 11'd70; //行显示前沿
parameter H_TOTAL_1018 = 11'd1440; //行扫描周期
parameter V_SYNC_1018 = 11'd3; //场同步
parameter V_BACK_1018 = 11'd10; //场显示后沿
parameter V_DISP_1018 = 11'd800; //场有效数据
parameter V_FRONT_1018 = 11'd10; //场显示前沿
parameter V_TOTAL_1018 = 11'd823; //场扫描周期
// 4.3' 800*480
parameter H_SYNC_4384 = 11'd128; //行同步
parameter H_BACK_4384 = 11'd88; //行显示后沿
parameter H_DISP_4384 = 11'd800; //行有效数据
parameter H_FRONT_4384 = 11'd40; //行显示前沿
parameter H_TOTAL_4384 = 11'd1056; //行扫描周期
parameter V_SYNC_4384 = 11'd2; //场同步
parameter V_BACK_4384 = 11'd33; //场显示后沿
parameter V_DISP_4384 = 11'd480; //场有效数据
parameter V_FRONT_4384 = 11'd10; //场显示前沿
parameter V_TOTAL_4384 = 11'd525; //场扫描周期
//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 ;
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复位
assign lcd_rgb = lcd_de ? pixel_data : 24'd0;
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
16'h7084 : begin
h_sync = H_SYNC_7084;
h_back = H_BACK_7084;
h_disp = H_DISP_7084;
h_total = H_TOTAL_7084;
v_sync = V_SYNC_7084;
v_back = V_BACK_7084;
v_disp = V_DISP_7084;
v_total = V_TOTAL_7084;
end
16'h7016 : begin
h_sync = H_SYNC_7016;
h_back = H_BACK_7016;
h_disp = H_DISP_7016;
h_total = H_TOTAL_7016;
v_sync = V_SYNC_7016;
v_back = V_BACK_7016;
v_disp = V_DISP_7016;
v_total = V_TOTAL_7016;
end
16'h4384 : begin
h_sync = H_SYNC_4384;
h_back = H_BACK_4384;
h_disp = H_DISP_4384;
h_total = H_TOTAL_4384;
v_sync = V_SYNC_4384;
v_back = V_BACK_4384;
v_disp = V_DISP_4384;
v_total = V_TOTAL_4384;
end
16'h1018 : begin
h_sync = H_SYNC_1018;
h_back = H_BACK_1018;
h_disp = H_DISP_1018;
h_total = H_TOTAL_1018;
v_sync = V_SYNC_1018;
v_back = V_BACK_1018;
v_disp = V_DISP_1018;
v_total = V_TOTAL_1018;
end
default : 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
endcase
end
//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 + 2'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 + h_disp))
pixel_ypos <= v_cnt + 1'd1 - y_sync - y_back;
else
pixel_ypos <= 11'd0;
end
//数据使能信号
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
lcd_de <= 1'b0;
else
lcd_de <= data_req;
end
//请求像素点颜色数据输入
always@ (posedge lcd_pclk or negedge rst_n) begin
if(!rst_n)
data_req<=1'b0;
else if((h_cnt >= h_sync + h_back - 2'd2) && (h_cnt < h_sync + h_back + h_disp - 2'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 - 1'b1)
h_cnt <= 11'd0;
else
h_cnt <= h_cnt + 1'b1;
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 - 1'b1) begin
if(v_cnt == v_total - 1'b1)
v_cnt <= 11'd0;
else
v_cnt <= v_cnt + 1'b1;
end
end
end
endmodule
2.5 LCD 显示模块设计
LCD 显示模块设计如下图所示:
RTL代码如下所示:
module lcd_display_colorbar(
input lcd_pclk,
input rst_n,
input pixel_xpos,
input pixel_ypos,
input h_disp,
input v_disp,
output [23:0] pixel_data
);
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