VGA显示图像学习笔记
一、VGA显示原理
首先,我们需要先了解一下屏幕图像显示中的分辨率/像素,先了解有关像素的知识,日常生活中所描述的分辨率,比如640×480,1920×1080等等,这指的都是屏幕显示区域的像素值,即,宽有640个像素,长有480个像素,而每一个像素都是由R、G、B三种原色组成的,这三种颜色,按照亮度,每一种颜色都可以分为0~255共256个等级,0表示亮度为0%,255表示亮度为100%,数值代表的是颜色的亮度,三种颜色的不同亮度相互组合成立了大千世界的各种颜色,而在数字电路中,每一种颜色我们都可以定义为位宽为8比特的数据。所以每一个像素的数据位宽即为3×8,即位宽为24。
其次,需要了解一下VGA显示的扫描方式,如图所示。常见的彩色显示器一般由CRT(阴极射线管)构成,色彩是由RGB三基色组成,显示是用逐行扫描的方式解决,阴极射线枪发出的电子束打在涂有荧光粉的的荧光屏上,产生RGB三基色,合成一个彩色像素,扫描从屏幕左上方开始,从左到右,从上到下进行扫描,每扫描完一行,电子束都回到屏幕的下一行左边的起始位置,在这期间,CRT对电子束进行消隐,每行结束时,用行同步信号进行行同步,扫描完所有行,用场同步信号进行场同步,并使扫描回到屏幕的左上方,同时进行场消隐,预备下一场的扫描。随着显示技术的,出现了液晶显示器,液晶显示器的成像原理与CRT 不同,液晶显示器是通过对液晶像素点单元施加电压与否,来实现液晶单元的透明程度,并添加三色滤光片、分别使R、G、B 这三种光线透过滤光片,最后通过3 个像素点合成一个彩色像素点,从而实现彩色显示。由于液晶技术晚于CRT 显示技术诞生,为了能够兼容传统的显示接口,液晶显示器通过内部电路实现了对VGA 接口的完全兼容。因此,在使用显示器时,只要该显示器带有标准的VGA 接口,就不用去关注其成像原理,直接使用标准的VGA 时序即可驱动。RGB 接口的TFT 屏,扫描方式与VGA 完全一致。不同之处只是在于,VGA 显示器是接收模拟信号,而TFT 屏则省略了这一过程,直接接受数字信号。例如,在驱动VGA 时首先产生对应像素的颜色数字信号编码,再使用数模转换电路(例如ADV7123)将数字转换为模拟信号,然后通过VGA 线缆将模拟信号传输到VGA 显示器上进行显示。而TFT 屏则直接省略了数模转换这一过程,直接将接收到的数字信号进行显示。因此在控制器设计端,并没有任何区别,
只是需要适当的根据不同的分辨率修改相对应的一些参数即可。
VGA显示图像模块划分
根据VGA的显示原理,结合待显示的内容,我们对VGA显示进行模块划分,需要根据任务要求(譬如我这次是做一个200×200的图片显示)划分模块,如图所示,VGA_control即为VGA控制器,这个模块是输出VGA的行场控制信号,数据使能信号,显示数据,VGA/TFT工作时钟以及扫描位置信号,image_extract是桥接VGA_control和ROM IP,VGA_control输出扫描位置信号至image_extract,其产生数据读取的地址,从ROM IP中读取数据,并传输至VGA_控制器中,由其转化为待显示的RGB数据。pll是时钟分频IP,用来生成不同的时钟频率,由于我此次使用显示屏分辨率是800×480,开发板的时钟频率为50M,而以每秒60帧显示的话,VGA/TFT的工作时钟则为33M,VGA_top是用来协调各个模块。
VGA控制器模块
完整源码及注释如图所示
module VGA_control(
input rst_n ,
input clk ,
input [23:0] data ,//输入的图像数据
output reg [23:0] VGA_RGB ,//输出的图像数据
output reg VGA_BLK ,//数据有效信号
output reg data_req ,
output reg VGA_HS ,//行同步信号
output reg VGA_VS ,//场同步信号
output reg [11:0] h_count , //当前行扫描位置坐标
output reg [11:0] v_count //当前场扫描位置坐标
);
`include "vga_parameter.v"
localparam hsync_end = `H_Total_Time;
localparam hs_end = `H_Sync_Time;
localparam hdata_begin = `H_Sync_Time + `H_Back_Porch + `H_Left_Border;
localparam hdata_end = `H_Sync_Time + `H_Left_Border + `H_Back_Porch + `H_Data_Time;
localparam vsync_end = `V_Total_Time;
localparam vs_end = `V_Sync_Time;
localparam vdata_begin = `V_Sync_Time + `V_Back_Porch + `V_Top_Border;
localparam vdata_end = `V_Sync_Time + `V_Back_Porch + `V_Top_Border + `V_Data_Time;
reg [11:0] HS_cnt; //行扫描计数器
reg [11:0] VS_cnt; //场扫描计数器
//行扫描计数器
always@(posedge clk or negedge rst_n) begin
if (!rst_n) begin
HS_cnt <= 0;
end
else if(HS_cnt >= hsync_end - 1) begin
HS_cnt <= 0;
end
else begin
HS_cnt <= HS_cnt + 1'b1;
end
end
always@(posedge clk or negedge rst_n) begin
if (!rst_n) begin
VGA_HS <= 0 ;
end
else if (HS_cnt >= hs_end) begin
VGA_HS <= 1'b1;
end
else begin
VGA_HS <= 0;
end
end
//场扫描计数器
always@(posedge clk or negedge rst_n) begin
if (!rst_n) begin
VS_cnt <= 0;
end
else if(HS_cnt == hsync_end - 1 ) begin
if(VS_cnt >= vsync_end - 1)
VS_cnt <= 0;
else
VS_cnt <= VS_cnt + 1'd1;
end
else begin
VS_cnt <= VS_cnt;
end
end
always@(posedge clk or negedge rst_n) begin
if (!rst_n) begin
VGA_VS <= 0 ;
end
else if (VS_cnt >= vs_end) begin
VGA_VS <= 1'b1;
end
else begin
VGA_VS <= 0;
end
end
//如果不加这个data_req,那么VGA_RGB输出得数据总是会比data慢一拍,加了data_req,并且在tb中让data_req低电平得时候,就把数据给进来
//这样子就能消除VGA_BLK慢了一拍得影响
always@(posedge clk or negedge rst_n) begin
if (!rst_n)
data_req <= 0 ;
else if (HS_cnt >= hdata_begin - 1)
if (HS_cnt < hdata_end - 1)
if(VS_cnt >= vdata_begin)
if(VS_cnt < vdata_end)
data_req <= 1'b1;
else
data_req <= 0;
else
data_req <= 0;
else
data_req <= 0;
else
data_req <= 0;
end
always@(posedge clk or negedge rst_n) begin
if (!rst_n)
VGA_BLK <= 0 ;
else
VGA_BLK <= data_req;
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
VGA_RGB <= 0;
else if(data_req <= 1'b1)
VGA_RGB <= data;
else
VGA_RGB <= 0;
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
h_count <= 0;
else if (data_req == 1'b1)
h_count <= HS_cnt - hdata_begin;
else
h_count <= 0;
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
v_count <= 0;
else if (data_req == 1'b1)
v_count <= VS_cnt - vdata_begin;
else
v_count <= 0;
end
endmodule
image_extract模块
完整源码及其注释如图所示
module image_extract(
input clk_ctrl,
input reset_n,
input disp_data_req,
input [11:0] visible_hcount ,
input [11:0] visible_vcount ,
input Frame_Begin,
input [15:0] rom_data,
input [15:0] disp_back_color,
output [15:0] disp_data,
output reg [15:0] rom_addra
);
parameter H_Visible_area = 800;
parameter V_Visible_area = 480;
parameter IMG_WIDTH = 200;
parameter IMG_HEIGHT = 200;
parameter img_disp_hbegin = (H_Visible_area - IMG_WIDTH)/2;
parameter img_disp_vbegin = (V_Visible_area - IMG_HEIGHT)/2;
wire img_h_disp;
wire img_v_disp;
wire img_disp;
wire [15:0]hcount_max;
//判断设置的显示的起始位置是否会导致显示超出范围
//assign h_exceed = img_disp_hbegin + IMG_WIDTH > H_Visible_area - 1'b1;
//assign v_exceed = img_disp_vbegin + IMG_HEIGHT > V_Visible_area - 1'b1;
//不同的设置情况,显示区域做不同的处理
assign img_h_disp = (visible_hcount >= img_disp_hbegin && visible_hcount < img_disp_hbegin + IMG_WIDTH);
assign img_v_disp = (visible_vcount >= img_disp_vbegin && visible_vcount < img_disp_vbegin + IMG_HEIGHT);
assign img_disp = disp_data_req && img_h_disp && img_v_disp;
always@(posedge clk_ctrl or negedge reset_n)
begin
if(!reset_n)
rom_addra <= 16'd0;
else if(Frame_Begin)
rom_addra <= 16'd0;
else if(img_disp)begin
rom_addra <= rom_addra + 1'b1;
end
else
rom_addra <= rom_addra;
end
assign disp_data = img_disp ? rom_data : disp_back_color;
endmodule
VGA_top模块
module TFT_top(
input clk50M,
input reset_n,
output [15:0] tft_rgb,
output tft_hs,
output tft_vs,
output tft_blk,//数据使能
output tft_bl,//背光控制
output tft_clk
);
wire pll_locked;
wire clk_ctrl;
wire tft_reset_n;
wire [15:0] rom_addra;
wire [15:0] disp_data;
wire [15:0] rom_data;
wire disp_data_req;
wire [11:0] visible_hcount;
wire [11:0] visible_vcount;
wire [15:0]tft_rgb;
wire Frame_Begin;
assign tft_reset_n = pll_locked;
assign tft_bl=1'b1;
parameter DISP_BACK_COLOR = 116'hFFFF;
pll pll
(
// Clock out ports
.clk_out1(clk_ctrl ), // output clk_out1
// Status and control signals
.resetn (reset_n ), // input reset,active low
.locked (pll_locked ), // output locked
// Clock in ports
.clk_in1 (clk50M ) // input clk_in1
);
rom_image rom_image (
.clka (clk_ctrl ), // input wire clka
.addra (rom_addra ), // input wire [16 : 0] addra
.douta (rom_data ) // output wire [15 : 0] douta
);
image_extract image_extract(
.clk_ctrl (clk_ctrl ),
.reset_n (tft_reset_n ),
.disp_back_color(DISP_BACK_COLOR),
.rom_addra (rom_addra ),
.rom_data (rom_data ),
.Frame_Begin (Frame_Begin ),
.disp_data_req (disp_data_req ),
.visible_hcount (visible_hcount ),
.visible_vcount (visible_vcount ),
.disp_data (disp_data )
);
TFT_driver TFT_driver(
.ClkDisp(clk_ctrl),
.rst_n(tft_reset_n),
.data(disp_data),
.data_req(disp_data_req),
.h_count(visible_hcount),
.v_count(visible_vcount),
.TFT_HS(tft_hs),
.TFT_VS(tft_vs),
.TFT_RGB(tft_rgb),
.Frame_Begin(Frame_Begin),
.TFT_BLK(tft_blk),
.TFT_PCLK(tft_clk)
);
endmodule
板级测试效果
VGA/TFT显示图像