详情可以见LCD篇
LCD彩条显示——FPGA学习笔记10_依次显示红绿蓝三个通道-CSDN博客
一、VTC简介
Video Timing Controller 缩写 VTC 是我们在所有涉及 FPGA 图像、 FPGA 视频类方案中经常用到的一种用于产生视频时序的控制器。 本课以 VGA 为切入点, 学习视频传输的基本知识和相关概念, 以及视频时序的控制器的相关内容
常见视频接口:
刷新率:
刷新率(FPS, Frames Per Second) , 单位为 Hz, 即每秒传输图像的帧数(帧数/s) 。 每秒钟帧数 (FPS) 愈多,一帧是静止的图象; 快速连续地显示不同帧便形成了运动的假象, 每秒钟帧数愈多, 所显示的动作就会愈流畅。 例如: 60Hz 的刷新率刷也就是指屏幕一秒内只扫描 60 次, 即 60 帧/秒
像素时钟:
二、VTC控制器系统框图
VTC代码编写:
`timescale 1ns / 1ps
module VTC#(
parameter H_ActiveSize = 1980 , //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
parameter H_FrameSize = 1920+88+44+148 , //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
parameter H_SyncStart = 1920+88 , //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
parameter H_SyncEnd = 1920+88+44 , //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分
parameter V_ActiveSize = 1080 , //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
parameter V_FrameSize = 1080+4+5+36 , //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
parameter V_SyncStart = 1080+4 , //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
parameter V_SyncEnd = 1080+4+5 //视频时间参数,场同步结束,即多少场数后停止产生场同步信号,之后就是场有效数据部分
)
(
input I_vtc_rstn , //系统复位
input I_vtc_clk , //系统时钟
output O_vtc_vs , //场同步输出
output O_vtc_hs , //行同步输出
output O_vtc_de_valid , //视频数据有效
output O_vtc_user , //满足stream时序产生 user 信号,用于帧同步
output O_vtc_last //满足stream时序产生 later 信号,用于每行结束
);
reg [11:0] hcnt = 12'd0 ; //视频水平方向,列计数器,寄存器
reg [11:0] vcnt = 12'd0 ; //视频垂直方向,行计数器,寄存器
reg [2 :0] rst_cnt = 3'd0 ; //复位计数器,寄存器
wire rst_sync = rst_cnt[2] ; //同步复位
//同步复位
always @(posedge I_vtc_clk or negedge I_vtc_rstn ) begin
if (!I_vtc_rstn) begin
rst_cnt <= 3'd0;
end
else if(rst_cnt[2] == 1'b0)begin
rst_cnt <= rst_cnt + 1'b1 ;
end
end
//视频水平方向,列计数器
always @(posedge I_vtc_clk) begin
if (rst_sync == 1'b0) begin
hcnt <= 12'd0;
end
else if(hcnt < (H_FrameSize - 1'b1))begin
hcnt <= hcnt + 1'b1;
end
else begin
hcnt <= 12'd0;
end
end
//视频锤垂直方向计数器
always @(posedge I_vtc_clk) begin
if (rst_sync == 1'b0) begin
vcnt <= 12'd0;
end
else if(hcnt == (H_FrameSize - 1'b1))begin
if (vcnt == (V_FrameSize - 1'b1)) begin
vcnt <= 12'd0;
end
else begin
vcnt <= vcnt + 1'b1;
end
end
end
wire hs_valid = hcnt < H_ActiveSize ; //行信号有效像素部分
wire vs_valid = vcnt < V_ActiveSize ; //场信号有效像素部分
wire vtc_hs = (hcnt >= H_SyncStart && hcnt < H_SyncEnd) ; //产生hs,行同步信号
wire vtc_vs = (vcnt > V_SyncStart && vcnt <= V_SyncEnd) ; //产生vs,场同步信号
wire vtc_de = hs_valid && vs_valid ; //只有当视频水平方向,列有效和视频垂直方向,行同时有效,视频数据部分才是有效
//********************** video stream video rgb ***************************
//如果是输入RGB时序,那么转为stream时序
reg vtc_vs_r1 ;
reg vtc_hs_r1 ;
reg vtc_de_r1 ;
reg vtc_user_r1 ,vtc_user_r2 ;
reg vtc_valid_r1,vtc_valid_r2 ;
reg vtc_last_r2 ;
reg vs_start ;
always @(posedge I_vtc_clk )begin
if(rst_sync == 1'b0) //复位
vs_start <= 1'b0;
else if(vtc_user_r1)//清除VS帧同步
vs_start <= 1'b0;
else if(vtc_vs && vtc_vs_r1==1'b0)//当vtc_vs发生上升沿跳变代表一帧开始
vs_start <= 1'b1;
end
always @(posedge I_vtc_clk )begin
vtc_vs_r1 <= vtc_vs ;
vtc_hs_r1 <= vtc_hs ;
vtc_user_r1 <= ~vtc_user_r1 & vs_start & vtc_de ; //vtc_user延迟1拍
vtc_last_r2 <= ~vtc_de & vtc_valid_r1 ; //产生stream video last 延迟于数据输入2拍
vtc_valid_r1 <= vtc_de ; //vtc_valid延迟1拍
vtc_valid_r2 <= vtc_valid_r1 ; //vtc_valid对输入信号延迟2拍,以和vtc_last_r2信号配套同步
vtc_user_r2 <= vtc_user_r1 ; //vtc_user 对输入信号延迟2拍,以和vtc_last_r2信号配套同步
end
assign O_vtc_vs = vtc_vs_r1 ;
assign O_vtc_hs = vtc_hs_r1 ;
assign O_vtc_de_valid = vtc_valid_r2 ;
assign O_vtc_user = vtc_user_r2 ;
assign O_vtc_last = vtc_last_r2 ;
endmodule
三、TPG简介
TPG(video_test_pattern generator) 视频测试模式发生器用于产生测试数据, 对视频数据通路测试。 本课设计一个图像数据发生器, 该模块能够产生不同颜色和样式的图像数据, 并按顺序将 RGB 图像数据发送到有效显示区域。
1、RGB像素格式
RGB 是一种颜色标准, 俗称三基色模式, 是通过对红(R)、 绿(G)、 蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的, 是运用最广的颜色系统之一。 像素点就相当于一个 RGB 灯, 通过控制 R、 G、B 这三种颜色的亮度就可以显示出各种各样的色彩。 在显示器发明之后, 从黑白显示器发展到彩色显示器, 人们开始使用发出不同颜色的光的荧光粉(CRT, 等离子体显示器) , 或者不同颜色的滤色片(LCD) , 或者不同颜色的半导体发光器件( OLED 和 LED 大型全彩显示牌) 来形成色彩, 不管采用何种技术, 都以红(R)、 绿(G)、 蓝(B)三个颜色作为基本的发光单元, 通过控制光强度, 组合出了人类视力所能感知的所有颜色。
(1)索引格式
索引格式是比较老的格式, 随着越来越高的视觉需求, 索引格式基本不再被使用了, 这里只做简单介绍。 索引格式中 bit 存储的值并非是实际的 R、 G、 B 值, 而是对应点的像素在调色板中的索引, 即在图像文件中划出一个区域存放一个调色板来存储图像中的每一种颜色, 这个像素的颜色对应到调色板的第几号颜色
RGB1: 每个像素用 1 个 bit 表示, 能够表示的索引范围是 0-1, 共 2 种颜色, 即调色板中包含 2 中颜色。
RGB4: 每个像素用 4 个 bit 表示, 能够表示的索引范围是 0-15, 共 16 种颜色, 即调色板中包含 16 中颜色。
RGB8: 每个像素用 8 个 bit 表示, 能够表示的索引范围是 0-255, 共 256 种颜色, 即调色板中包含 256 中颜色。
(2)像素格式
RGB 像素格式不同于索引格式, 索引格式 bit 存储的是每一个像素的色彩索引, 而像素格式中的 bit 用来存储像素点中 R、 G、 B 三个颜色的值.RGB888: 也叫 RGB24, 一个像素点由 3 个字节组成, 共 8bit*3=24bit。 其中 bit23~bit16 是 RED 通道, 红色对应的值就是 24’ hFF0000; bit15~bit8 是 GREEN 通道, 绿色对应的值就是 24’ h00FF00; bit7~bit0 是 BLUE 通道, 蓝色对应的值为 24’ h0000FF, 使用 RGB888 格式, 理论上可以产生 1670 万种色彩。RGB888 像素格式如下图所示:
四、程序设计
本次实验通过测试视频发生器模块, 产生测试图形, 即彩条、 渐变、 纯色、 棋方格, 将相应测试图形的 RGB数据依次放入有效的显示区域内。 实验包含 3 个模块, VTC 视频时序控制器模块、 TPG 视频测试模式发生器模块、用户控制模块。 以下给出系统框图, 关于 VTC 视频时序控制器驱动的详细描述请看前面的实验, 我们主要学习 TPG视频测试模式发生器驱动。
系统框图:
I_tpg_ck : 视频格式的像素时钟
I_tpg_vs : 视频格式的场同步信号
I_tpg_hs : 视频格式的行同步信号
I_tpg_de : 视频格式的有效数据阶段
O_tpg_vs : 同 I_tpg_vs
O_tpg_hs : 同 I_tpg_hs
O_tpg_de : 同 I_tpg_de
O_tpg_data : rgb 格式数据输出
TPG 代码编写
`timescale 1ns / 1ps
module TPG(
input I_tpg_clk , //视频格式的像素时钟
input I_tpg_rstn , //系统复位
input I_tpg_vs , //视频格式的场同步信号
input I_tpg_hs , //视频格式的行同步信号
input I_tpg_de , //视频格式的有效数据阶段
output O_tpg_vs , //同 I_tpg_vs
output O_tpg_hs , //同 I_tpg_hs
output O_tpg_de , //同 I_tpg_de
output [23:0] O_tpg_data //rgb 格式数据输出
);
reg tpg_vs_r = 1'b0 ; //对vs信号寄存
reg tpg_hs_r = 1'b0 ; //对hs信号寄存
reg [7 :0] grid_data = 8'd0 ; //grid棋方格寄存器
reg [23:0] color_bar = 24'd0 ; //RGB 彩条寄存器
reg [10:0] dis_mode = 11'd0 ; //显示模式寄存器
reg [7 :0] r_reg = 8'd0 ; //红寄存器
reg [7 :0] g_reg = 8'd0 ; //绿寄存器
reg [7 :0] b_reg = 8'd0 ; //蓝寄存器
always @(posedge I_tpg_clk)begin
tpg_vs_r <= I_tpg_vs; //对vs信号寄存一次
tpg_hs_r <= I_tpg_hs; //对hs信号寄存一次
end
reg [11:0]v_cnt = 12'd0; //视频垂直方向,行计数器
reg [11:0]h_cnt = 12'd0; //视频水平方向,列计数器
//h_cnt计数器模块
always @(posedge I_tpg_clk)begin
h_cnt <= I_tpg_de ? h_cnt + 1'b1 : 12'd0; //计数行有效像素,当de无效,重置 h_cnt=0
end
//v_cnt计数器模块
always @(posedge I_tpg_clk)begin
if(I_tpg_vs) begin//通过vs产生同步复位
v_cnt <= 12'd0; //重置v_cnt=0
end
else begin
v_cnt <= ((!tpg_hs_r)&&I_tpg_hs) ? v_cnt + 1'b1 : v_cnt; //hs信号的上升沿,v_cnt计数,这种方式可以不管hs有效是高电平还是低电平的情况,v_cnt 视频垂直方向,行计数器,计数行数量
end
end
//显示模式切换
always @(posedge I_tpg_clk)begin
if(I_tpg_rstn==1'b0)
dis_mode <= 0;
else begin
dis_mode <= ((!tpg_vs_r)&&I_tpg_vs) ? dis_mode + 1'b1 : dis_mode;
end
end
//grid_data发生器
always @(posedge I_tpg_clk)begin
grid_data <= ((v_cnt[4]==1'b1) ^ (h_cnt[4]==1'b1)) ? 8'h00 : 8'hff; //方格大小16*16,黑白交替
end
//RGB彩条发生器
always @(posedge I_tpg_clk)
begin
if(h_cnt==260)
color_bar <= 24'hff0000;//红
else if(h_cnt==420)
color_bar <= 24'h00ff00;//绿
else if(h_cnt==580)
color_bar <= 24'h0000ff;//蓝
else if(h_cnt==740)
color_bar <= 24'hff00ff;//紫
else if(h_cnt==900)
color_bar <= 24'hffff00;//黄
else if(h_cnt==1060)
color_bar <= 24'h00ffff;//青蓝
else if(h_cnt==1220)
color_bar <= 24'hffffff;//白
else if(h_cnt==1380)
color_bar <= 24'h000000;//黑
else
color_bar <= color_bar;
end
//测试图形输出
always @(posedge I_tpg_clk)begin
case(dis_mode[10:7])//截取高位,控制切换显示速度
4'd0:begin
r_reg <= 0;
b_reg <= 0;
g_reg <= 0;
end
4'd1:begin
r_reg <= 8'b11111111; //白
g_reg <= 8'b11111111;
b_reg <= 8'b11111111;
end
4'd2,4'd3:begin//连续两个状态输出相同图形
r_reg <= 8'b11111111; //红
g_reg <= 0;
b_reg <= 0;
end
4'd4,4'd5:begin//连续两个状态输出相同图形
r_reg <= 0; //绿
g_reg <= 8'b11111111;
b_reg <= 0;
end
4'd6:begin
r_reg <= 0; //蓝
g_reg <= 0;
b_reg <= 8'b11111111;
end
4'd7,4'd8:begin //连续两个状态输出相同图形
r_reg <= grid_data; //方格
g_reg <= grid_data;
b_reg <= grid_data;
end
4'd9:begin
r_reg <= h_cnt[7:0]; //水平渐变
g_reg <= h_cnt[7:0];
b_reg <= h_cnt[7:0];
end
4'd10,4'd11:begin //连续两个状态输出相同图形
r_reg <= v_cnt[7:0]; //垂直渐变
g_reg <= v_cnt[7:0];
b_reg <= v_cnt[7:0];
end
4'd12:begin
r_reg <= v_cnt[7:0]; //红垂直渐变
g_reg <= 0;
b_reg <= 0;
end
4'd13:begin
r_reg <= 0; //绿垂直渐变
g_reg <= h_cnt[7:0];
b_reg <= 0;
end
4'd14:begin
r_reg <= 0; //蓝垂直渐变
g_reg <= 0;
b_reg <= h_cnt[7:0];
end
4'd15:begin
r_reg <= color_bar[23:16]; //彩条
g_reg <= color_bar[15:8];
b_reg <= color_bar[7:0];
end
endcase
end
assign O_tpg_data = {r_reg,g_reg,b_reg} ; //测试图形RGB数据输出
assign O_tpg_vs = I_tpg_vs ; //VS同步信号
assign O_tpg_hs = I_tpg_hs ; //HS同步信号
assign O_tpg_de = I_tpg_de ; //DE数据有效信号
endmodule
五、LCD驱动编写
`timescale 1ns / 1ps
module LCD(
input I_sysclk ,
input I_rst_n ,
output lcd_clk ,
output lcd_hs ,
output lcd_vs ,
output lcd_bl ,
output lcd_de ,
output lcd_rst ,
inout [23:0] lcd_rgb
);
wire [7:0] O_rgb_r ;
wire [7:0] O_rgb_g ;
wire [7:0] O_rgb_b ;
assign lcd_rgb = ( lcd_de == 1'b1 ) ? {{O_rgb_r[7:0],O_rgb_g[7:0],O_rgb_b[7:0]}} : {24{1'bz}} ;
assign lcd_bl = 1'b1 ;
assign lcd_rst = 1'b1 ;
assign lcd_clk = clk_33M3 ;
//LCD驱动时钟
clk_wiz_0 instance_name
(
.clk_25M (clk_25M ) ,
.clk_33M3 (clk_33M3 ) ,
.clk_in1 (I_sysclk )
);
video_lcd u_video_lcd(
.I_vid_clk (clk_33M3 ) , //系统时钟
.I_vid_rstn (I_rst_n ) , //系统复位输入
.O_vid_hs (lcd_hs ) , //hs信号
.O_vid_vs (lcd_vs ) , //vs信号
.O_vid_de (lcd_de ) , //视频数据有效信号
.O_rgb_r (O_rgb_r ) , // RGB-红
.O_rgb_g (O_rgb_g ) , // RGB-绿
.O_rgb_b (O_rgb_b ) // RGB-蓝
);
endmodule