TFT_LCD液晶屏驱动
第42章、TFT_LCD液晶屏驱动
【42.1 理论】
1. TFT_LCD简介
【液晶显示器】:
简称 LCD (Liquid Crystal Display),相对于上一代 CRT 显示器(阴极射线管显示器),LCD 显示器具有功耗低、体积小、承载的信息量大及不伤眼的优点,因而它成为了现在的主流电子显示设备,其中包括电视、电脑显示器、手机屏幕及各种嵌入式设备的显示器。
【液晶】:
一种介于固体和液体之间的特殊物质,它是一种有机化合物,常态下呈液态, 但是它的分子排列却和固体晶体一样非常规则,因此取名液晶。如果给液晶施加电场,会 改变它的分子排列,从而改变光线的传播方向,配合偏振光片,它就具有控制光线透过率 的作用,再配合彩色滤光片,改变加给液晶电压大小,就能改变某一颜色透光量的多少, 下图的就是绿色显示结构。利用这种原理,做出可控红、绿、蓝光输出强度的显示结构,把三种显示结构组成一个显示单位,通过控制红绿蓝的强度,可以使该单位混合输出不同的色彩,这样的一个显示单位就是 像素。
【常见的LCD按物理结构】:
① 扭曲向列型(TN-Twisted NemaTIc)
② 超扭曲向列型(STN-Super TN)
③ 双层超扭曲向列型(DSTN-Dual Scan Tortuosity Nomograph)
④ 薄膜晶体管型(TFT-Thin Film Transistor),TFT-LCD就是其中的一种。
TN-LCD、STN-LCD和DSYN-LCD的显示原理都相同,只是液晶分子的扭曲角度不同。而TFT-LCD则采用与TN系列LCD截然不同的显示方式。
【TFT-LCD】:
全称Thin Film Transistor-Liquid Crystal Display,译为薄膜晶体管液晶显示器。其中TFT就是Thin Film Transistor的简称,指的是薄膜晶体管(矩阵),可以“主动的”对屏幕上的各个独立的像素进行控制,这也就是所谓的主动矩阵TFT(active matrix TFT)的来历。
图像产生的基本原理:显示屏由许多可以发出任意颜色光线的像素组成,只要控制各个像素显示相应的颜色就能达到目的了。在TFT LCD中一般采用背光技术,为了能精确地控制每一个像素的颜色和亮度就需要在每一个像素之后安装一个类似百叶窗的半导体开关,以此做到完全的单独的控制一个像素点,液晶材料被夹在TFT玻璃层和颜色过滤层之间,通过改变刺激液晶的电压值就可以控制最后出现的光线强度与色彩。
2. RGB接口TFT-LCD时序
对于RGB接口TFT-LCD显示屏,它的图像显示的同步模式有两种,分别为HV同步模式、DE同步模式。不同的同步模式对应不同的时序。
【HV同步模式】
HV同步模式下,图像的显示只需要行同步信号(hsync)和场同步信号(vsync)来确定显示时序。此时,RGB接口的TFT-LCD液晶显示屏的显示时序和VGA时序标准类似。
(1) RGB接口TFT-LCD显示屏 行同步 时序:
图中RGB代表图像信息,HSync表示行同步信号,HSync自上一个上升沿起到下一个上升沿止为一个完整周期,称之为 行扫描周期,一个完整的行扫描周期,包含4部分:同步、后沿、有效图像、前沿,基本单位为pixel,即一个像素时钟周期。在一个完整的行扫描周期中,RGB图像信息在 HSync行同步信号 的同步下完成一行图像的显示,RGB图像信息在有效图像阶段,图像信息有效,其他阶段图像信息无效;HSync行同步信号在同步阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的同步阶段,HSync行扫描信号拉高,其他阶段拉低,周而复始。
(2) RGB接口TFT-LCD显示屏 场同步 时序:
图中RGB代表图像信息,VSync表示场同步信号,VSync自上一个上升沿起到下一个上升沿止为一个完整周期,称之为 场扫描周期,一个完整的场扫描周期,包含4部分:同步、后沿、有效图像、前沿,基本单位为一个完整的行扫描周期。在一个完整的行扫描周期中,RGB图像信息在VSync行同步信号的同步下完成一帧图像的显示,RGB图像信息在有效图像阶段,图像信息有效,其他阶段图像信息无效;VSync行同步信号在同步阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的同步阶段,VSync行扫描信号拉高,其他阶段拉低,周而复始。
(3)RGB接口TFT_LCD时序图
将行、场同步时序相结合,构成RGB接口TFT-LCD时序图,图中的红色区域表示在一个完整的行扫描周期中,RGB图像信息只在此区域有效;黄色区域表示在一个完整的场扫描周期中,RGB图像信息只在此区域有效,两者相交的橙色区域,就是RGB接口TFT-LCD显示屏的图像显示区域。
【DE同步模式】
DE同步模式下,图像的显示只需要 数据使能信号 确定显示时序,不需要 行场同步信号。DE同步模式下的TFT图像显示时序图如下。
① 当数据使能信号为高电平时,表示TFT显示屏扫描到了有效显示区域,此时输入到TFT显示屏的图像信息能够显示出来;
② 当数据使能信号为低电平时,表示TFT显示屏未扫描到有效显示区域。
对于两种不同的同步模式, DE同步模式一般使用在大尺寸屏幕,小尺寸屏幕多使用HV同步模式。HV同步模式地出现早于DE同步模式,当今的大部分显示屏均支持HV和DE两种同步模式。
【注】相比于vga显示,每个行场扫描周期少了左右、上下边框四个部分
行扫描信号:0-40,41-42,43-522(480),523-52441
场扫描信号:0-9,10-11,12-283(272),284-285
【42.2 实战】
设计编写RGB接口TFT-LCD液晶显示屏驱动,在4.3寸(480*272) TFT-LCD显示屏上横向依次显示等宽多色彩条,显示颜色自左向右依次为红、橙、黄、绿、青、蓝、紫、黑、白、灰,图像像素格式为RGB565,帧率为60Hz。
征途Pro开发板TFT_LCD接口部分原理图:
由图可知,TFT_LCD液晶屏与VGA相同,均使用 RGB565 的图像格式,位宽为16bit,输出信号还包括行同步信号hsync和场同步信号vsync;
与VGA不同的是,输出信号还有时钟信号、使能信号、背光控制信号;(此外还有用于触摸控制的4路信号,本实验中并未涉及,不再介绍)。
【注】在本实验工程中,输出信号中包含 HV同步模式 下需要的行、场同步信号(hsync、vsync)和 DE同步模式 下的 tft_de信号。
若要使用HV同步模式进行图像显示,可注释掉 tft_de信号;
若要使用DE同步模式进行图像显示,可注释掉 行、场同步信号。
【整体设计】
TFT彩条显示工作流程:
(1) 系统上电后,板卡传入系统时钟(sys_clk)和复位信号(sys_rst_n)到顶层模块;
(2) 时钟:系统时钟直接传入 时钟生成模块(clk_gen),分频 产生TFT显示屏工作时钟( tft_clk_9m),作为图像数据生成模块(tft_pic)和VGA时序控制模块(tft_ctrl)的工作时钟;
(3) 图像信息:图像数据生成模块 以TFT显示时序控制模块传入的 像素点坐标(pix_x,pix_y)为约束条件,生成待显示彩条图像的 色彩信息(pix_data);
(4) rgb信息:图像数据生成模块 生成的 彩条图像色彩信息 传入 TFT时序控制模块,在模块内部使用 使能信号 滤除掉非图像显示有效区域的图像数据,产生 RGB色彩信息(rgb_tft),在行、场同步信号(hsync、vsync)或 数据使能信号(tft_de)的同步作用下,将RGB色彩信息扫描显示到TFT显示屏,显示出彩条图像。
【时钟生成模块(clk_gen)】
本次实验工程中,TFT显示屏为4.3寸,分辨率为480*272,帧率为60HZ,得时钟频率为9MHz,而板卡晶振传入时钟频率为50MHz。时钟生成模块的作用就是将50MHz晶振时钟分频为9MHz的TFT显示屏工作时钟。
module clk_gen( input sys_clk , input sys_rst_n , output tft_clk , output locked );
clk_9 clk_9_inst01 (
.inclk0 ( sys_clk ),
.areset (~sys_rst_n ),.c0 ( tft_clk ), .locked ( locked )
);
endmodule
【TFT显示时序控制模块(tft_ctrl)】
输入信号:
tft_clk_9m,频率为9MHz,为模块工作时钟,由分频模块产生并输入;
sys_rst_n,为顶层模块的rst_n信号输入,低电平有效;
pix_data,为彩条图像像素点色彩信息,由图像数据生成模块产生并传入,在TFT_LCD显示器有效图像显示区域赋值给信号RGB图像色彩信息(rgb_tft)。
输出信号:
pix_x、pix_y,TFT_LCD有效显示区域像素点坐标,由TFT_LCD时序控制模块生并传入图像数据生成模块;
hsync、vsync,为TFT_LCD行、场同步信号 ,通过TFT_LCD接口传输给TFT_LCD显示屏;
rgb_tft,为显示器要显示的图像色彩信息,传输给TFT_LCD显示器;
tft_bl,为TFT显示屏背光信号;
tft_clk,为TFT显示屏工作时钟;
tft_de,为TFT显示使能信号。
第一部分:行同步信号(hsync)、场同步信号(vsync)
在(480*272@60)显示模式下,一个行扫描周期为 525个像素时钟周期,一个场扫描周期为286个行扫描周期。设计行场计数器h_cnt、v_cnt,绘制出行场扫描信号。
第二部分:图像显示有效信号(rgb_valid)
行扫描信号:0-40,41-42,43-522(480),523-524
场扫描信号:0-9,10-11,12-283(272),284-285
定义图像显示有效信号(rgb_valid),当cnt_h、cnt_v分别计数到43-522、12-283时,rgb_valid赋值高电平,否则赋值低电平。
第三部分:图像信息请求信号(pix_data_req)、TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)
用像素点坐标(pix_x,pix_y) 实现 (0,0) – (480,272)的坐标计数,读者可能会想到使用已经声明的图像显示有效信号(rgb_valid),但在此处不能使用该信号。
因为本次实验是TFT显示屏多色彩条的显示,图像数据生成模块tft_pic需要以坐标(pix_x,pix_y)为约束条件对pix_data信号进行赋值,只能使用时序逻辑的赋值方式,那么pix_data的赋值时刻会滞后条件满足时刻一个时钟周期,显示图像会出现问题。为了解决这一问题,我们需要声明新的图像数据请求信号pix_data_req,该信号要超前图像显示有效信号(rgb_valid)一个时钟周期,以抵消pix_data时序逻辑赋值带来的问题。
第四部分:RGB色彩信息(rgb_tft)波形绘制思路
TFT显示屏图像显示是在行、场同步信号的作用下,在有效显示区域写入正确图像数据,将图像色彩信息以扫描显示的方式显示出来。
第五部分: TFT显示数据使能信号(tft_de)
数据使能信号为 DE同步模式下的 图像显示同步信号,只在有效图像显示区域为高电平,其他时刻为低电平。tft_de信号的波形变化和rgb_valid信号相同,所以tft_de信号可由rgb_valid信号使用组合逻辑进行赋值。
第六部分: TFT显示屏工作时钟(tft_clk)、TFT显示屏背光信号(tft_bl)
TFT显示屏与VGA显示器不同,TFT显示屏的正常工作离不开时钟信号,且输入TFT显示屏的时钟信号,要与行场信号或数据使能信号的同步时钟相同,否者会出现图像显示的错误。
TFT显示屏背光信号的作用是控制显示屏背光,高电平时打开显示器背光,低电平时关闭背光,在本实验工程使用复位信号sys_rst_n信号为背光信号赋值。
module tft_ctrl( input tft_clk_9m , input tft_rst_n , input [15:0] pix_data ,//pix_data即显示图像的信息,rgb565模式共16位
output hsync , output vsync , output [8:0] pix_x ,//480*272,9位足够 output [8:0] pix_y , output [15:0] rgb_tft ,//rgb565模式,共16位 output tft_bl , output tft_de , output tft_clk
);
/----------------------------tft扫描参数定义----------------------------------/
//定义行、场信号节点参数:同步、后沿、有效数据、前沿、总周期 (相较于vga、hdmi无边框)
parameter H_SYNC = 10'd41 , //行同步
H_BACK = 10'd2 , //行时序后沿
H_VALID = 10'd480 , //行有效数据
H_FRONT = 10'd2 , //行时序前沿
H_TOTAL = 10'd525 ; //行扫描周期,一个周期525个像素点
parameter V_SYNC = 10'd10 , //场同步
V_BACK = 10'd2 , //场时序后沿
V_VALID = 10'd272 , //场有效数据
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd286 ; //场扫描周期,一个周期286/----------------------------行场计数器:h_cnt、v_cnt----------------------------------/
//行、场计数器(h_cnt、v_cnt):用来生成行、场扫描信号,行计数器h_cnt,计数(0-799),共10位
reg [9:0] h_cnt ;
always@(posedge tft_clk_9m or negedge tft_rst_n)begin
if(!tft_rst_n)
h_cnt <= 10'b0 ;
else if(h_cnt == H_TOTAL-1'b1)
h_cnt <= 10'b0 ;
else
h_cnt <= h_cnt + 10'b1 ;
end
reg [9:0] v_cnt ;
always@(posedge tft_clk_9m or negedge tft_rst_n)begin
if(!tft_rst_n)
v_cnt <= 10'b0 ;
else if((v_cnt == V_TOTAL-1'b1) && (h_cnt == H_TOTAL - 1'b1))//计数到整幅图片的最后一个像素
v_cnt <= 10'b0 ;
else if(h_cnt==H_TOTAL - 1'b1)//每当行计数满则向下一行
v_cnt <= v_cnt + 10'b1 ;
else
v_cnt <= v_cnt ;
end/----------------------------1、行场同步信号(hsync、vsync):由计数器确定行、场同步信号hsync、vsync----------------------/
//行、场同步信号(hsync、vsync):只有行、场同步信号为高,其余皆为低 ,表明行场扫描信号的过程/进程
wire hysnc ;
wire vysnc ;
//assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
assign hsync = (h_cnt >= 0) && (h_cnt < 41) ;
assign vsync = (v_cnt >= 0) && (v_cnt < 10) ;/----------------------------2、区域有效标志信号(rgb_valid):计数器确定,680480,底边的大小----------------------------------*/
wire rgb_valid ;
assign rgb_valid = (((h_cnt >= H_SYNC + H_BACK )&&(h_cnt < H_SYNC + H_BACK + H_VALID))
&&((v_cnt >= V_SYNC + V_BACK )&&(v_cnt < V_SYNC + V_BACK + V_VALID)));/---------------------------3、色彩请求信号(pix_data_req):计数器确定,在行扫描信号上超前pic_valid信号一个时钟---------------/
wire pix_data_req ;
assign pix_data_req = (((h_cnt >= H_SYNC + H_BACK - 1'b1 )&&(h_cnt < H_SYNC + H_BACK + H_VALID - 1'b1))
&&((v_cnt >= V_SYNC + V_BACK )&&(v_cnt < V_SYNC + V_BACK + V_VALID)));/-----------------------------4、像素点坐标:请求信号和计数器确定,坐标即距有效显示区域边界的值-----------------------------/
assign pix_x = (pix_data_req == 1'b1)?(h_cnt - H_SYNC - H_BACK -1'b1):9'd511 ;//行场计数器的值计算出相应像素点的坐标位置
assign pix_y = (pix_data_req == 1'b1)?(v_cnt - V_SYNC - V_BACK ) :9'd511 ;//最大值511/-----------------------------5、输出pic_rgb:将vga_pic模块生成的图形样式(pix_data)赋值给pic_rgb,并显示-----------------------------/
assign rgb_tft = (rgb_valid == 1'b1)?pix_data : 16'b0 ;//tft_clk,tft_de,tft_bl:TFT像素时钟、数据使能、背光信号
assign tft_clk = tft_clk_9m ;
assign tft_de = rgb_valid ;
assign tft_bl = tft_rst_n ;
endmodule
【图像数据生成模块(tft_pic)】
输入信号:
tft_clk_9m,频率为9MHz,为TFT显示屏工作时钟,由分频模块产生并输入;
sys_rst_n,顶层模块的rst_n信号输入,低电平有效;
(pix_x,pix_y),TFT显示屏有效显示区域像素点坐标,由TFT时序控制模块生并输入。
输出信号:
pix_data为彩条图像像素点色彩信息,在TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)约束下生成,传输到TFT时序控制模块。
根据输入像素点坐标(pix_x,pix_y),在有效显示区域,将pix_x计数范围十等分,在不同的计数部分给pix_data赋值对应的色彩信息,因为采用时序逻辑的赋值方式,pix_data滞后pix_x、pix_y信号一个时钟周期。
module tft_pic( input tft_clk_9m , input tft_rst_n , input [8:0] pix_x , input [8:0] pix_y ,
output reg [15:0] pix_data
);
/-----------------------定义参数: 显示区域及颜色------------------------------/
//定义图像有效显示区域(640*480)
parameter H_VALID = 10'd640 ,
V_VALID = 10'd480 ;
//定义需要的颜色
parameter RED = 16'hF800 ,
ORANGE = 16'hFC00 ,
YELLOW = 16'hFFE0 ,
GREEN = 16'h07E0 ,
CYAN = 16'h07FF ,
BLUE = 16'h001F ,
PURPPLE = 16'hF81F ,
BLACK = 16'h0000 ,
WHITE = 16'hFFFF ,
GRAY = 16'hD69A ;/----------------------区域分割编号:用横坐标值将图片有效显示区域分成10个部分----------------/
//图片有效显示区域即为vga信号有效区域,用 pix_x 在 800 的周期内找到对应的 640 即可
assign ONE = ((pix_x >= 0 ) && (pix_x < H_VALID/10 ));
assign TWO = ((pix_x >= H_VALID/10 ) && (pix_x < H_VALID/102 ));
assign THREE = ((pix_x >= H_VALID/102) && (pix_x < H_VALID/103 ));
assign FOUR = ((pix_x >= H_VALID/103) && (pix_x < H_VALID/104 ));
assign FIVE = ((pix_x >= H_VALID/104) && (pix_x < H_VALID/105 ));
assign SIX = ((pix_x >= H_VALID/105) && (pix_x < H_VALID/106 ));
assign SEVEN = ((pix_x >= H_VALID/106) && (pix_x < H_VALID/107 ));
assign EIGHT = ((pix_x >= H_VALID/107) && (pix_x < H_VALID/108 ));
assign NINE = ((pix_x >= H_VALID/108) && (pix_x < H_VALID/109 ));
assign TEN = ((pix_x >= H_VALID/109) && (pix_x < H_VALID ));/---------------------区域赋值:将10个区域进行相应10种颜色赋值,输出pix_data--------------------------/
wire [3:0] pic_area;
always@(posedge tft_clk_9m or negedge tft_rst_n)begin
if(!tft_rst_n)
pix_data <= 15'b0 ;
else begin
case(pic_area)
ONE : pix_data <= RED ;
TWO : pix_data <= ORANGE ;
THREE : pix_data <= YELLOW ;
FOUR : pix_data <= GREEN ;
FIVE : pix_data <= CYAN ;
SIX : pix_data <= BLUE ;
SEVEN : pix_data <= PURPPLE ;
EIGHT : pix_data <= BLACK ;
NINE : pix_data <= WHITE ;
TEN : pix_data <= GRAY ;
default : pix_data <= 16'b0 ;
endcase
end
end/*
//通过pix_x把图像有效显示部分(640480)分成10份,再给pix_data赋值
always@(posedge tft_clk_9m or negedge tft_rst_n)begin
if(!tft_rst_n)
pix_data <= 0 ;
else if((pix_x >= 0 ) && (pix_x < H_VALID/10 ))
pix_data <= RED ;
else if((pix_x >= H_VALID/10 ) && (pix_x < H_VALID/102 ))
pix_data <= ORANGE ;
else if((pix_x >= H_VALID/102) && (pix_x < H_VALID/103 ))
pix_data <= YELLOW ;
else if((pix_x >= H_VALID/103) && (pix_x < H_VALID/104 ))
pix_data <= GREEN ;
else if((pix_x >= H_VALID/104) && (pix_x < H_VALID/105 ))
pix_data <= CYAN ;
else if((pix_x >= H_VALID/105) && (pix_x < H_VALID/106 ))
pix_data <= BLUE ;
else if((pix_x >= H_VALID/106) && (pix_x < H_VALID/107 ))
pix_data <= PURPPLE ;
else if((pix_x >= H_VALID/107) && (pix_x < H_VALID/108 ))
pix_data <= BLACK ;
else if((pix_x >= H_VALID/108) && (pix_x < H_VALID/109 ))
pix_data <= WHITE ;
else if((pix_x >= H_VALID/10*9) && (pix_x < H_VALID ))
pix_data <= GRAY ;
end*/
endmodule
【顶层模块(top_tft_colorbar)】
module top_tft_colorbar( input sys_clk , input sys_rst_n ,
output hsync , output vsync , output [15:0] rgb , output tft_bl , output tft_clk , output tft_de
);
// wire tft_clk ; wire locked ; wire pix_x ; wire pix_y ; wire pix_data ; wire tft_rst_n ;
assign tft_rst_n = (sys_rst_n && locked);
//25MHZ时钟生成模块
clk_gen clk_gen_inst01(
. sys_clk (sys_clk ) ,
. sys_rst_n (~sys_rst_n ) ,. c0 (tft_clk_9m ) , . locked (locked )
);
//图像样式确定模块
tft_pic tft_pic_inst01(
. tft_clk_9m (tft_clk_9m ) ,
. tft_rst_n (tft_rst_n ) ,
. pix_x (pix_x ) ,
. pix_y (pix_y ) ,. pix_data (pix_data )
);
//vga时序控制模块,输出像素点坐标,并根据图像样式输出行场信号
tft_ctrl tft_ctrl_inst01(
. tft_clk_9m ( tft_clk_9m) ,
. tft_rst_n ( tft_rst_n ) ,
. pix_data ( pix_data ) ,//pix_data即显示图像的信息,rgb565模式共16位. hsync ( hsync ) , . vsync ( vsync ) , . pix_x ( pix_x ) ,//480*272,9位足够 . pix_y ( pix_y ) , . rgb_tft ( rgb ) ,//rgb565模式,共16位 . tft_bl ( tft_bl ) , . tft_de ( tft_de ) , . tft_clk ( tft_clk )
);
endmodule
`timescale 1ns/1ns
module tb_top_tft_colorbar();
reg sys_clk ;
reg sys_rst_n ;
wire hsync ;
wire vsync ;
wire [15:0] rgb ;
wire tft_bl ;
wire tft_clk ;
wire tft_de ;
top_tft_colorbar top_tft_colorbar_inst01(
. sys_clk ( sys_clk ) ,
. sys_rst_n ( sys_rst_n) ,
. hsync ( hsync ) ,
. vsync ( vsync ) ,
. rgb ( rgb ) ,
. tft_bl ( tft_bl ) ,
. tft_clk ( tft_clk ) ,
. tft_de ( tft_de )
);
initial begin
sys_clk = 1'b1 ;
sys_rst_n = 1'b0 ;
#200
sys_rst_n = 1'b1 ;
end
always #10 sys_clk = ~sys_clk ;
endmodule