部分素材来自原子哥
一、LCD简介
基本原理:在两块平行玻璃板中填充液晶材料,通过电场控制液晶分子旋转从而达到透光和遮光的目的。
LCD屏幕重要参数:分辨率、像素格式、驱动时序
分辨率:
像素格式:
RGB:由红 绿 蓝三种颜色通道构成,这三种颜色的分量叠加决定实际颜色;通常,会给RGB图像加一个通道alpha,即透明度,这样就会有四个分量共同控制颜色。
YUV:YUV是编译true-color颜色空间(color space)的种类,Y'UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance或Luma),也就是灰阶值,“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
格式转换:
驱动时序:
VSYNC:场同步信号 一帧信号的起始标志(切换下一张图片的标志)
VBP:场同步后沿
VFP:场同步前沿
HSYNC:行同步信号 切换下一行的标志
HBP:行同步后沿
HFP:行同步前沿
(行场同步前后沿统称消隐时间)
LCD屏幕参数:注意行显示周期单位是CLK,场显示周期是行
LCD行同步时序(DE):
可以使用DE做数据有效信号,DE拉高时,给出DATA数据。
LCD帧同步时序(DE):
可以使用DE做数据有效信号,DE拉高时,对DE进行计数,第一次拉高给出第一行数据,第二次拉高给出第二行数据........以此类推。
LCD屏幕接口:
LCD_RST | LCD复位信号 |
LCD_BL | 背光 |
LCD_DE | 数据有效 |
二、实验任务
本节的实验任务是使用正点原子开发板上的RGB TFT-LCD接口,驱动RGB LCD液晶屏(支持目前推出的所有RGB LCD屏),并显示出彩条。
三、程序设计
1、框架设计
2、时序设计
(1)rd_id:
(2)clk_div:
(3) lcd_driver:
(4)lcd_display
(5)完整程序框架
3、波形时序图、代码编写
(1)rd_id:
`timescale 1ns / 1ps
module rd_id(
input sys_clk ,
input sys_rst_n ,
input [23:0] lcd_rgb ,
output reg [15:0] lcd_id
);
reg rd_flag;
always @(posedge sys_clk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
rd_flag <= 1'b0;
lcd_id <= 15'd0;
end
else begin
if (rd_flag == 1'b0) begin
rd_flag <= 1'b1;
case ({lcd_rgb[7],lcd_rgb[15],lcd_rgb[23]})//BGR
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: ;
endcase
end
end
end
endmodule
(2)clk_div:(使用4384屏幕,驱动时钟25M)
`timescale 1ns / 1ps
module clk_div(
input sys_clk ,
input sys_rst_n ,
input [15:0] lcd_id ,
output reg lcd_pclk
);
reg div_4_cnt;
reg clk_25M;
reg clk_12_5M;
//25Mhz时钟
always @(posedge sys_clk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
clk_25M <= 1'b0;
end
else begin
clk_25M <= !clk_25M;
end
end
//12.5Mhz时钟
//位宽为1,会自动溢出,无需手动清零
always @(posedge sys_clk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
div_4_cnt <= 1'b0;
clk_12_5M <= 1'b0;
end
else begin
if (div_4_cnt == 1'b1) begin
clk_12_5M <= !clk_12_5M;
end
else begin
clk_12_5M <= clk_12_5M;
end
end
end
//使用组合逻辑进行赋值
always @(*) begin
case (lcd_id)
16'h4342: lcd_pclk = clk_12_5M ;
16'h7084: lcd_pclk = clk_25M ;
16'h7016: lcd_pclk = sys_clk ;
16'h4384: lcd_pclk = clk_25M ;
16'h1018: lcd_pclk = sys_clk ;
default: lcd_pclk = 1'b0 ;
endcase
end
endmodule
(3) lcd_driver:
`timescale 1ns / 1ps
//LCD屏幕驱动
module lcd_driver(
input lcd_pclk , //时钟
input sys_rst_n , //复位,低电平有效
input [14:0] lcd_id , //LCD屏ID
input [23:0] pixel_data , //像素数据
output lcd_clk , //LCD 像素时钟
output lcd_hs , //LCD 行同步信号
output lcd_vs , //LCD 场同步信号
output lcd_bl , //LCD 背光控制信号
output reg lcd_de , //LCD 数据使能信号
output lcd_rst , //LCD 屏幕复位信号
output [23:0] lcd_rgb , //LCD RGB888颜色数据
output [10:0] pixel_xpos , //当前像素点横坐标
output [10:0] pixel_ypos , //当前像素点纵坐标
output reg [10:0] h_disp , //LCD屏水平分辨率
output reg [10:0] v_disp , //LCD屏垂直分辨率
output reg data_req //LCD屏幕 数据请求信号
);
// 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 [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_clk = lcd_pclk ;
assign lcd_hs = 1'b1 ;
assign lcd_vs = 1'b1 ;
assign lcd_bl = 1'b1 ;
assign lcd_rst = 1'b1 ;
assign lcd_rgb = lcd_de ? pixel_data : 24'd0;
//行场时序参数
always @(posedge lcd_pclk) 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
//行计数
always @(posedge lcd_pclk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
h_cnt <= 11'd0;
end
else begin
if (h_cnt == h_total - 1'd1) begin
h_cnt <= 11'd0;
end
else begin
h_cnt <= h_cnt + 1'b1;
end
end
end
//场计数
always @(posedge lcd_pclk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
v_cnt <= 11'b0;
end
else begin
if (h_cnt == h_total - 1'd1) begin
if (v_cnt == v_total - 1'b1) begin
v_cnt <= 11'd0;
end
else begin
v_cnt <= v_cnt +1'b1;
end
end
end
end
//数据请求信号,data_req
always @(posedge lcd_pclk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
data_req <= 1'b0;
end
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))begin
data_req <= 1'b1;
end
else begin
data_req <= 1'b0;
end
end
//阻塞赋值,会延迟一拍,因此lcd_de 比 data_req晚一拍
always @(posedge lcd_pclk or negedge sys_rst_n ) begin
if (!sys_rst_n) begin
lcd_de <= 1'b0;
end
else begin
lcd_de <= data_req;
end
end
//像素点坐标
assign pixel_xpos = data_req ? (h_cnt - (h_sync + h_back - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (v_cnt - (v_sync + v_back - 1'b1)) : 11'd0;
endmodule
(4)lcd_display:
`timescale 1ns / 1ps
module lcd_display(
input sys_clk ,
input sys_rst_n ,
input [10:0] pixel_xpos ,
input [10:0] pixel_ypos ,
input [10:0] h_disp ,
input [10:0] v_disp ,
output reg[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 sys_clk or negedge sys_rst_n) begin
if(!sys_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
(5)lcd_top:
三态门:
`timescale 1ns / 1ps
module LCD_RGB_colorbar(
input sys_clk ,
input sys_rst_n ,
output lcd_clk , //LCD 像素时钟
output lcd_hs , //LCD 行同步信号
output lcd_vs , //LCD 场同步信号
output lcd_bl , //LCD 背光控制信号
output reg lcd_de , //LCD 数据使能信号
output lcd_rst , //LCD 屏幕复位信号
inout [23:0] lcd_rgb //LCD RGB888颜色数据
);
wire [23:0] lcd_rgb_o ;
wire [23:0] lcd_rgb_i ;
wire [15:0] lcd_id ;
wire [10:0] pixel_xpos ;
wire [10:0] pixel_ypos ;
wire [10:0] h_disp ;
wire [10:0] v_disp ;
assign lcd_rgb = lcd_de ? lcd_rgb_o :{24{1'bz}} ;
assign lcd_rgb_i = lcd_rgb ;
//识别屏幕ID
rd_id u_rd_id(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.lcd_rgb (lcd_rgb_i),
.lcd_id (lcd_id )
);
//产生驱动屏幕时钟
clk_div u_clk_div(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.lcd_id (lcd_id ),
.lcd_pclk (lcd_pclk )
);
//LCD屏幕驱动
lcd_driver u_lcd_driver(
.lcd_pclk (lcd_pclk ), //时钟
.sys_rst_n (sys_rst_n ), //复位,低电平有效
.lcd_id (lcd_id ), //LCD屏ID
.pixel_data (pixel_data), //像素数据
.lcd_clk (lcd_clk ), //LCD 像素时钟
.lcd_hs (lcd_hs ), //LCD 行同步信号
.lcd_vs (lcd_vs ), //LCD 场同步信号
.lcd_bl (lcd_bl ), //LCD 背光控制信号
.lcd_de (lcd_de ), //LCD 数据使能信号
.lcd_rst (lcd_rst ), //LCD 屏幕复位信号
.lcd_rgb (lcd_rgb_o ), //LCD RGB888颜色数据
.pixel_xpos (pixel_xpos), //当前像素点横坐标
.pixel_ypos (pixel_ypos), //当前像素点纵坐标
.h_disp (h_disp ), //LCD屏水平分辨率 有效显示区域
.v_disp (v_disp ), //LCD屏垂直分辨率 有效显示区域
.data_req (data_req ) //LCD屏幕 数据请求信号
);
lcd_display u_lcd_display(
.sys_clk (lcd_pclk ),
.sys_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)
);
endmodule
4、仿真文件
`timescale 1ns / 1ps
module lcd_rgb_tb();
reg sys_clk ;
reg sys_rst_n ;
wire lcd_clk ;
wire lcd_hs ;
wire lcd_vs ;
wire lcd_bl ;
wire lcd_de ;
wire lcd_rst ;
wire [23:0] lcd_rgb ;
always #10 sys_clk = !sys_clk;
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 LCD_RGB_colorbar(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.lcd_clk (lcd_clk ), //LCD 像素时钟
.lcd_hs (lcd_hs ), //LCD 行同步信号
.lcd_vs (lcd_vs ), //LCD 场同步信号
.lcd_bl (lcd_bl ), //LCD 背光控制信号
.lcd_de (lcd_de ), //LCD 数据使能信号
.lcd_rst (lcd_rst ), //LCD 屏幕复位信号
.lcd_rgb (lcd_rgb ) //LCD RGB888颜色数据
);
endmodule