RGB LCD 彩条显示实验 —1
TFT-LCD 的全称是 Thin Film Transistor-Liquid Crystal Display,即薄膜晶体管液晶显示屏,它显示的每个像素点都是由集成在液晶后面的薄膜晶体管独立驱动,因此 TFT-LCD 具有较高的响应速度以及较好的图像质量。
我们对于 LCD屏幕其实并不需要掌握其 实现的具体原理 我们只需要从使用的角度了解几个重点的东西
1080p 是 1920 x 1080
2K 是 2560 x 1440
4K 是 3840 x 2160
上面讲了一个像素点就相当于一个 RGB 小灯,通过控制 R、G、B 这三种颜色的亮度就可以显示出各种各样的色彩。那该如何控制 R、G、B 这三种颜色的显示亮度呢?一般一个 R、G、B 这三部分分别使用8bit 的数据,那么一个像素点就是 8bit*3=24bit,也就是说一个像素点 3 个字节,这种像素格式称为 RGB888。当然常用的像素点格式还有 RGB565,只需要两个字节,但在色彩鲜艳度上较差一些。我们领航者开发板上的 RGB TFT-LCD 接口采用的 RGB888 的像素格式,共需要 24 位,每一位对应 RGB 的颜色分量如下图所示:
一个像素点占用 3 个字节,其中 bit23~bit16 是 RED 通道,bit15~bit8 是 GREEN 通道,bit7~bit0 是 BLUE 通道。所以红色对应的值就是 24’hFF0000,绿色对应的值就是 24’h00FF00,蓝色对应的值为 24’h0000FF。通过调节 R、G、B 的比例可以产生其它的颜色,比如 24’hFFFF00 就是黄色,24’h000000就是黑色,24’hFFFFFF 就是白色
RGB LCD 接口的信号线
R[7:0]、G[7:0]和 B[7:0]是 24 位数据,DE、VSYNC、HSYNC 和PCLK 是四个控制信号
本次实验用到了 ATK-7016 RGB LCD模块
其实我们在之前的SOC 实验中 做过这样子的 大体设计
我们再复习一下
我们来看一下 LCD 是怎么扫描显示一帧图像的。一帧图像也是由一行一行组成的。HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图 29.1.6 的最左边。VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了
RGB LCD 屏幕时序
HSYNC:行同步信号,当此信号有效的时候就表示开始显示新的一行数据,查阅所使用的 LCD 数据手
册可以知道此信号是低电平有效还是高电平有效,图 29.1.7 为低电平有效。
HSPW:行同步信号宽度,也就是 HSYNC 信号持续时间。HSYNC 信号不是一个脉冲,而是需要持续
一段时间才是有效的,单位为 CLK。
HBP:行显示后沿(或后肩),单位是 CLK。
HOZVAL:行有效显示区域,即显示一行数据所需的时间,假如屏幕分辨率为 1024*600,那么 HOZVAL
就是 1024,单位为 CLK。
HFP:行显示前沿(或前肩),单位是 CLK。
当 HSYNC 信号发出以后,需要等待 HSPW+HBP 个 CLK 时间才会接收到真正有效的像素数据。当显示完一行数据以后需要等待 HFP 个 CLK 时间才能发出下一个 HSYNC 信号,所以显示一行所需要的时间就是:HSPW + HBP + HOZVAL + HFP。
VSYNC:帧(场)同步信号,当此信号有效的时候就表示开始显示新的一帧数据,查阅所使用的 LCD
数据手册可以知道此信号是低电平有效还是高电平有效,图 29.1.8 为低电平有效。
VSPW:帧同步信号宽度,也就是 VSYNC 信号持续时间,单位为 1 行的时间。
VBP:帧显示后沿(或后肩),单位为 1 行的时间。
LINE:帧有效显示区域,即显示一帧数据所需的时间,假如屏幕分辨率为 1024*600,那么 LINE 就是
600 行的时间。
VFP:帧显示前沿(或前肩),单位为 1 行的时间。
显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP 个行时间,最终的计算公式:
T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
因此我们在配置一款 RGB LCD 屏的时候需要知道这几个参数:HSPW(行同步)、HBP(行显示后沿)、
HOZVAL(行有效显示区域)、HFP(行显示前沿)、VSPW(场同步)、VBP(场显示后沿)、LINE(场有效
显示区域)和 VFP(场显示后沿)。
RGB LCD 液晶屏一般有两种数据同步方式,一种是行场同步模式(HV Mode),另一种是数据使能同步模式(DE Mode)。当选择行场同步模式时,LCD 接口的时序与 VGA 接口的时序图非常相似,只是参数不同。如图 29.1.7 和图 29.1.8 中的行同步信号(HSYNC)和场同步信号(VSYNC)作为数据的同步信号,此时数据使能信号(DE)必须为低电平。当选择 DE 同步模式时,LCD 的 DE 信号作为数据的有效信号,如图 29.1.7 和图 29.1.8 的 DE 信号所示。只有同时扫描到帧有效显示区域和行有效显示区域时,DE 信号才有效(高电平)。当选择 DE 同步模式时,此时行场同步信号 VS 和 HS 必须为高电平。由于 RGB LCD 液晶屏一般都支持 DE 模式,因此本章我们采用 DE 同步的方式驱动 LCD 液晶屏。
像素时钟 :
像素时钟就是 RGB LCD 的时钟信号,以 ATK7016 这款屏幕为例,显示一帧图像所需要的时钟数就是:
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。
我们选用 50MHz
领航者开发板 RGB TFT-LCD 接口原理图
从上图中可以看到,FPGA 管脚输出的颜色数据位宽为 24bit,数据格式为 RGB888,即数据高 8 位表示红色,中间 8 位表示绿色,低 8 位表示蓝色。由于这 24 位数据不仅仅作为输出给 LCD 屏的颜色数据,同时 LCD_R7、LCD_G7 和 LCD_B7 也用来获取 LCD 屏的 ID,因此这 24 位颜色数据对 ZYNQ 开发板来说,是一个双向的引脚。另外,RGBLCD 模块支持触摸功能,图中以字母 T 开头的 5 个信号(T_PEN、T_SCK 等)与模块上的触摸芯片相连接。由于本次实验不涉及触摸功能的实现,因此这些信号并未用到。需要说明的是,LCD 液晶屏有一个复位信号(LCD_RST),当 LCD_RST 为低电平时,可对 LCD 屏进行复位。
实验任务 :
使用正点原子 ZYNQ 开发板上的 RGB TFT-LCD 接口,驱动 RGB LCD 液晶屏(支持目前推出的所有 RGB LCD 屏),并显示出彩条。
下面是我自己改写的代码
我觉得他把本应该 分开写的 输出和 控制模块全部写到一个led_driver 模块中 不妥当 我把它拆分成前控制模块 和 单个输出模块
因为 更好的给大家比照学习的机会 ( 内心os : 不想写 testbench
所以我的变量名字都是用同样的写法
下面展示我自己的完整的逻辑框图
可能写的不明白 但是 初步介绍一下
第一个 Read 模块
主要是读取 参数 确定 究竟用的是什么屏幕
第二个是 clk 时钟分频模块
主要是 读取 ID之后 将根据 ID 的 不同把不同的 频率 分配完成 输出给下一级
下面一个是我自己的独立于它的LCD驱动模块
我这个模块的主要作用是 接收型号的传递之后告诉我下一级display 该怎么显示 显示什么样的东西
再下一个是显示模块
就是传入数据究竟是怎么样的一种格式
最后一个部分是输出模块 out
我们先来写read模块
我们读模块 ID 读取 ID 模块根据输入的 lcd_rgb 值来寄存 LCD 屏的 ID,lcd_rgb[7](B7)、lcd_rgb[15](G7)和 lcd_rgb[23](R7)分别对应 M2、M1 和 M0。
除此之外,为了方便将 LCD 的 ID 和分辨率对应起来,这里对 M2、M1 和 M0 的值做了一个译码。如3’b000 译码成 16’h4342,表示当前连接的是 4.3 寸屏,分辨率为 480*272。
还有一件事 区分一下 高位和低位
其中 bit23~bit16 是 RED 通道,bit15~bit8 是 GREEN 通道,bit7~bit0 是 BLUE 通道。
ok
代码和结构在最后统一展示
下面介绍时钟模块
时钟模块 主要在于接收上游发送的lcd_id 之后 对应输出 不同的时钟频率
我们现在按照要求 要输出一个 2分频和4分频
超链接一下 我上次 写的 分频器的章节
因为偶数频率相对来说好做一点 对于奇数频率 大家可以相对的去学习一下
偶数频率N 在于 取 N/2 - 1 的 clk 反转
ok
下一步 我这里和正点原子不同了
我希望我这个模块 只是用来将 不同的 分辨率 lcd 究竟该如何作画 区分开来
我们了解到了原理可以知道 红色框框是 整个屏幕 而 黑色的是 框框的 画布 我们会在上面进行作画 注意左上角的是 端点(0,0)
整个总长其实包括了 下面的几个部分
而 画布的长度其实就是其中数据 显示的 HOZVAL 假设 1024*600 其实就是1024
而总列长 我们可以通过 利用帧同步信号进行计算
每完成1行到头 + 1 加到多少 就是多少
这里有一个误区
因为这一的单位是 1行的时间 所以我们不去细究数据 的 大小 可能数据量上会小一点
所以 我们每走完一整行 再控制这个列的数字 + 1 这才是 我们需要注意到的 因为单位是 一整行
注意点说完了 下面开始代码构造 都放到了下面统一讲述了
下面演示主要代码
----------------------------------------------------------------------------read_id .v
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标志
//*****************************************************
//** main code
//*****************************************************
//获取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
----------------------------------------------------------------------------clk.v
module clk_id(
input clk ,
input rst_n ,
input [15:0] lcd_id ,
output reg lcd_pclk
);
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;
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_set.v
module lcd_set(
input clk ,
input rst_n ,
input [15:0] lcd_id ,
output [10:0] pixel_xpos , // x 轴 对于点的移动坐标
output [10:0] pixel_ypos , // y 轴 对于点的移动坐标
output reg [10:0] h_disp , // it is 480*272 - 480
output reg [10:0] v_disp // - 272
);
// ============================================ \\
// we define signal and parameter \\
// ============================================ \\
// next is copy
// 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; //场扫描周期
// 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 ;
// ================================================== \\
// next is main code \\
// ================================================== \\
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 y 其实是为了下一步 display 而用的 我们这里的 配置的 计数
// 是用来显示 总长度的计数 而 x y 的计数 是总长度计数 要减去 头尾的多余
always@ (posedge clk 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 clk 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
// 接下来 我们配置 x y 坐标
// 我不太懂它 正点原子的什么鬼 我不知道为什么对于 data_reg 他非要 做一个减1 的操作
// 也许是上面的那个说 data_reg 其实是有接口的但是要比 lcd_reg 少一个 clk 也许是这个原因
// copy 一下
//请求像素点颜色数据输入
assign data_req = ((h_cnt >= h_sync + h_back - 1'b1) && (h_cnt < h_sync + h_back + h_disp - 1'b1)
&& (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp))
? 1'b1 : 1'b0;
//像素点坐标
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
----------------------------------------------------------------------------------- display.v
module lcd_display(
input lcd_pclk ,
input 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 define
parameter WHITE = 24'hFFFFFF; //白色
parameter BLACK = 24'h000000; //黑色
parameter RED = 24'hFF0000; //红色
parameter GREEN = 24'h00FF00; //绿色
parameter BLUE = 24'h0000FF; //蓝
// ========================================== \\
// next is main code \\
//==============================================\\
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
-------------------------------------------------------------------------------------- lastout.v
module last_out(
input lcd_pclk ,
input rst_n ,
input [15:0] lcd_id ,
input [23:0] pixel_data ,
output 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; //场扫描周期
// 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 ;
//wire define
wire lcd_en;
reg [10:0] h_disp ;
reg [10:0] v_disp ;
// 全是copy
//行场时序参数
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
//行计数器对像素时钟计数
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
// 这里要开始对最终的 结果进行说明了
// lcd_de //LCD 数据使能信号
// lcd_hs //LCD 行同步信号
// lcd_vs //LCD 场同步信号
// lcd_bl //LCD 背光控制信号
// lcd_clk //LCD 像素时钟
// lcd_rst //LCD复位
// lcd_rgb //LCD RGB888颜色数据
assign lcd_de = lcd_en; //LCD数据有效信号
//使能RGB888数据输出
assign lcd_en = ((h_cnt >= h_sync + h_back) && (h_cnt < h_sync + h_back + h_disp)
&& (v_cnt >= v_sync + v_back) && (v_cnt < v_sync + v_back + v_disp))
? 1'b1 : 1'b0;
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_en ? pixel_data : 24'd0;
endmodule
---------------------------------------------------------------------------- top.v
module dig_top (
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颜色数据
);
// ============================================ \\
// Next is define and signal parameter \\
// ============================================ \\
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;
// ================================================ \\
// next is main code \\
//================================================ \\
rd_id u_read_id(
.clk ( sys_clk ),
.rst_n ( sys_rst_n ),
.lcd_rgb ( lcd_rgb ),
.lcd_id ( lcd_id )
);
clk_id u_clk_id(
.clk ( sys_clk ),
.rst_n ( sys_rst_n ),
.lcd_id ( lcd_id ),
.lcd_pclk ( lcd_pclk )
);
lcd_set u_lcd_set(
.clk ( lcd_pclk ),
.rst_n ( sys_rst_n ),
.lcd_id ( lcd_id ),
.pixel_xpos ( pixel_xpos ),
.pixel_ypos ( pixel_ypos ),
.h_disp ( h_disp ),
.v_disp ( v_disp )
);
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 )
);
last_out u_last_out(
.lcd_pclk ( lcd_pclk ),
.rst_n ( sys_rst_n ),
.lcd_id ( lcd_id ),
.pixel_data ( pixel_data ),
.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 )
);
endmodule
// ================================================ testbench.v
`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;
assign lcd_rgb = 24'h0;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
end
dig_top u_dig_top(
.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_rst ( lcd_rst ),
.lcd_rgb ( lcd_rgb )
);
endmodule