VGA时序图
1)行扫描时序图
a:行同步时期,扫描地址的复位
b:行消隐后肩,扫描地址转移后的稳定等待准备期
c:行显示时期,数据有效区域
d:行消隐前肩,扫描地址转移的准备
e:行扫描总时间,一行扫描的总时间
2)场扫描时序图
o:场同步时期,扫描地址的复位
p:场消隐后肩,扫描地址转移后的稳定等待准备期
q:场显示时期,数据有效区域
r:场消隐前肩,扫描地址转移的准备
s:场扫描总时间,一场扫描的总时间
3)VGA显示器扫描轨迹
常见的刷新率时序表
由于FPGA擅长计数电路这里采用像素表示法来设计驱动
FPGA硬件 测试时要将sys_pll中的输出频率改为25MHZ。
驱动电路的verilog设计(lcd_driver)
由亍目前液晶显示器的普及,而高于 60Hz 的刷新率对于液晶来说,没有任何意义,所以我们以 640*480 在 60Hz 的刷新率下为例。
本次我们采用的是ADV7123视频转换芯片来实现。
1)为便于移植,根据640*480 60hz分辨率下的参数,宏定义相关数据
//---------------------------------
// 640 * 480
`define H_FRONT 11'd16
`define H_SYNC 11'd96
`define H_BACK 11'd48
`define H_DISP 11'd640
`define H_TOTAL 11'd800
`define V_FRONT 11'd10
`define V_SYNC 11'd2
`define V_BACK 11'd33
`define V_DISP 11'd480
`define V_TOTAL 11'd525
2)行扫描单位hcnt计数
/*******************************************
SYNC--BACK--DISP--FRONT
*******************************************/
//------------------------------------------
//h_sync counter & generator
reg [10:0] hcnt;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
hcnt <= 11'd0;
else
begin
if(hcnt < `H_TOTAL - 1'b1) //line over
hcnt <= hcnt + 1'b1;
else
hcnt <= 11'd0;
end
end
assign lcd_hs = (hcnt <= `H_SYNC - 1'b1) ? 1'b0 : 1'b1;
//VGA行同步信号
3)列扫描单位vcnt计数
每扫描完一行,即hcnt完成H_TOTAL次计数后,vcnt进行自加。
//------------------------------------------
//v_sync counter & generator
reg [10:0] vcnt;
always@(posedge clk or negedge rst_n)
begin
if (!rst_n)
vcnt <= 11'b0;
else if(hcnt == `H_TOTAL - 1'b1) //line over
begin
if(vcnt < `V_TOTAL - 1'b1) //frame over
vcnt <= vcnt + 1'b1;
else
vcnt <= 11'd0;
end
end
assign lcd_vs = (vcnt <= `V_SYNC - 1'b1) ? 1'b0 : 1'b1;
//VGA场同步信号
4)ADV7123控制信号输出
为了实现数据在lcd_dclk上升沿有效,我们将clk翻转输出,已实现上升沿采样
lcd_blank作为显示空白信号,低电平有效。
设计中不需要lcd_sync信息,可以直接接地。
//------------------------------------------
//LCELL LCELL(.in(clk),.out(lcd_dclk));
assign lcd_dclk = ~clk;
assign lcd_blank = lcd_hs & lcd_vs;
assign lcd_sync = 1'b0;
5)有效显示使能信号输出
当使能信号有效时,接收外部输入的RGB数据lcd_data.
//-----------------------------------------
assign lcd_en = (hcnt >= `H_SYNC + `H_BACK && hcnt < `H_SYNC + `H_BACK + `H_DISP) &&
(vcnt >= `V_SYNC + `V_BACK && vcnt < `V_SYNC + `V_BACK + `V_DISP)
? 1'b1 : 1'b0;
assign lcd_rgb = lcd_en ? lcd_data : 24'h000000; //ffffff;
6)外部数据请求控制信号。
为了实现数据的稳定,lcd_request要提前一个时钟请求外部输入数据
同时,设计中实时显示下一时刻的扫描地址lcd_xpos、lcd_ypos,也要提前一个时钟输出,以保证外部数据输入的同步化。
lcd_xpos、lcd_ypos作为显示器有效显示区域的行列坐标计数值。
//------------------------------------------
//ahead x clock
localparam H_AHEAD = 11'd1;
assign lcd_request = (hcnt >= `H_SYNC + `H_BACK - H_AHEAD && hcnt < `H_SYNC + `H_BACK + `H_DISP - H_AHEAD) &&
(vcnt >= `V_SYNC + `V_BACK && vcnt < `V_SYNC + `V_BACK + `V_DISP)
? 1'b1 : 1'b0;
//lcd xpos & ypos
assign lcd_xpos = lcd_request ? (hcnt - (`H_SYNC + `H_BACK - H_AHEAD)) : 11'd0;
assign lcd_ypos = lcd_request ? (vcnt - (`V_SYNC + `V_BACK)) : 11'd0;
模拟VGA图像数据的输入
1)宏定义三原色组合的颜色如下
//define colors RGB--8|8|8
`define RED 24'hFF0000 /*11111111,00000000,00000000 */
`define GREEN 24'h00FF00 /*00000000,11111111,00000000 */
`define BLUE 24'h0000FF /*00000000,00000000,11111111 */
`define WHITE 24'hFFFFFF /*11111111,11111111,11111111 */
`define BLACK 24'h000000 /*00000000,00000000,00000000 */
`define YELLOW 24'hFFFF00 /*11111111,11111111,00000000 */
`define CYAN 24'hFF00FF /*11111111,00000000,11111111 */
`define ROYAL 24'h00FFFF /*00000000,11111111,11111111 */
这里要注意你所用的VGA驱动电路是RGB888还是RGB565,若是RGB565,三原色要改成下面样式,并把其他程序中lcd_data的位数改为16位:
笔者曾在这里犯过错误。
//define colors RGB--5|6|5
`define RED 16'hF800 /*11111111,00000000,00000000 */
`define GREEN 16'h07E8 /*00000000,11111111,00000000 */
`define BLUE 16'h0011 /*00000000,00000000,11111111 */
`define WHITE 16'hFFFF /*11111111,11111111,11111111 */
`define BLACK 16'h0000 /*00000000,00000000,00000000 */
`define YELLOW 16'hFFE0 /*11111111,11111111,00000000 */
`define CYAN 16'hF81F /*11111111,00000000,11111111 */
`define ROYAL 16'h07FF /*00000000,11111111,11111111 */
2)根据输入的行、列地址信号,输出三原色组合后得到的8条彩色。
//-------------------------------------------
`ifdef VGA_HORIZONTAL_COLOR
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
lcd_data <= 24'h0;
else
begin
if (lcd_ypos >= 0 && lcd_ypos < (`V_DISP/8)*1)
lcd_data <= `RED;
else if(lcd_ypos >= (`V_DISP/8)*1 && lcd_ypos < (`V_DISP/8)*2)
lcd_data <= `GREEN;
else if(lcd_ypos >= (`V_DISP/8)*2 && lcd_ypos < (`V_DISP/8)*3)
lcd_data <= `BLUE;
else if(lcd_ypos >= (`V_DISP/8)*3 && lcd_ypos < (`V_DISP/8)*4)
lcd_data <= `WHITE;
else if(lcd_ypos >= (`V_DISP/8)*4 && lcd_ypos < (`V_DISP/8)*5)
lcd_data <= `BLACK;
else if(lcd_ypos >= (`V_DISP/8)*5 && lcd_ypos < (`V_DISP/8)*6)
lcd_data <= `YELLOW;
else if(lcd_ypos >= (`V_DISP/8)*6 && lcd_ypos < (`V_DISP/8)*7)
lcd_data <= `CYAN;
else// if(lcd_ypos >= (`V_DISP/8)*7 && lcd_ypos < (`V_DISP/8)*8)
lcd_data <= `ROYAL;
end
end
`endif
不同分辨率的VGA驱动
lcd_para文件定义了四种VGA分辨率驱动,这里只需修改定义的注释就行,并把PLL锁相环中的频率改为相应的频率。
//`define VGA_640_480_60FPS_25MHz
//`define VGA_800_600_72FPS_50MHz
//`define VGA_1024_768_60FPS_65MHz
`define VGA_1280_1024_60FPS_105MHz
PLL修改方法为直接修改下面参数中的乘法除法因子:
altpll_component.clk0_divide_by = 10,
altpll_component.clk0_duty_cycle = 50,
altpll_component.clk0_multiply_by = 21,
altpll_component.clk0_phase_shift = "0",
altpll_component.compensate_clock = "CLK0",
altpll_component.inclk0_input_frequency = 20000,