目录
- 1、VGA概述
-
- 1.1、外部接口
- 1.2、色彩原理
- 1.3、扫描方式
- 1.4、行场信号
- 2、工程编写
-
- 2.1、彩条输出
- 2.2、点阵输出
- 2.3、图片输出
- 3、效果实现
-
- 3.1、引脚绑定
- 3.2、上板验证
- 4、总结
- 5、参考链接
1、VGA概述 VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。
1.1、外部接口 实物图:
文章图片
电子原理图:
文章图片
由电路图可以看到,VGA并没有特殊的外部芯?,我们需要关注的其实只有5个信号:HS?同步信号,VS场同步信号,R红基?,G 绿基?,B蓝基?。
1.2、色彩原理 三基?是指通过其他颜?的混合?法得到的“基本?”由于?的?眼有感知红、绿、蓝三种不同颜?的锥体细胞,因此?彩空间通常可以由三种基本?来表达。这是?度学的最基本原理,即 三基?原理。三种基?是相互独?的,任何?种基?都不能有其它两种颜?合成。红绿蓝是三基?,这三种颜?合成的颜?范围最为?泛。我们的RGB信号真是三基?的运?,对这三个信号赋予不同的数值,混合起来便是不同的?彩。
文章图片
设计RGB信号时,既可以R信号、G信号和B信号独?的赋值,最后连到端?上,也可以直接?RGB当做?个整体信号,RGB信号在使
?时的位宽有三种常见格式。
- RGB_8,R:G:B = 3:3:2,即RGB332
- RGB_16,R:G:B = 5:6:5,即RGB565
- RGB_24,R:G:B = 8:8:8,即RGB888
?屏后在返回来扫描剩下的线,隔?扫描的显?器闪烁的厉害,会让使?者的眼睛疲劳。因此我们?般都采?逐?扫描的?式。
扫描原理如下所?:
文章图片
1.4、行场信号
文章图片
?开始看这个时序图可能看不懂,它是把?场信号绘制在同?张图?,说明?场信号的控制是相似的,只是时间参数不?样?已。如果展开的话,其实时序是这样的:
文章图片
若?个HS信号才组合?成?个VS,如果在?副图?中,那正确的时序表??式应该如下图这样:
文章图片
SYNC是“信号同步”,Back proch和Left border常常加在?起称为“显?后沿”,Addressable video为“显?区域”,Right porder和Front porch常常加在?起称为“显?前沿”,?个时序其实就是先拉??段较短的“信号同步”时间,然后拉低?段很长的时间,这就是?个回合。同时需要注意,其实也可以完全相反。即先拉低?段时间“信号同步”时间,然后拉??段很长的时间。
2、工程编写 参数定义:
`define vga_640_480
//`define vga_800_600`ifdef vga_640_480
`defineh_right_border8
`defineh_front_porch8
`defineh_sync_time96
`defineh_back_porch40
`defineh_left_border8
`defineh_data_time640
`defineh_total_time800`definev_bottom_border 8
`definev_front_porch2
`definev_sync_time2
`definev_back_porch25
`definev_top_border8
`definev_data_time480
`definev_total_time525
`elsif vga_1280_720
`defineh_right_border0
`defineh_front_porch110
`defineh_sync_time40
`defineh_back_porch220
`defineh_left_border0
`defineh_data_time1280
`defineh_total_time1650`definev_bottom_border 0
`definev_front_porch5
`definev_sync_time5
`definev_back_porch20
`definev_top_border0
`definev_data_time720
`definev_total_time750
`elsif vga_1920_1080
`defineh_right_border0
`defineh_front_porch88
`defineh_sync_time44
`defineh_back_porch148
`defineh_left_border0
`defineh_data_time1920
`defineh_total_time2200`definev_bottom_border 0
`definev_front_porch4
`definev_sync_time5
`definev_back_porch36
`definev_top_border0
`definev_data_time1080
`definev_total_time1125
`elsif vga_800_600
`defineh_right_border0
`defineh_front_porch40
`defineh_sync_time128
`defineh_back_porch88
`defineh_left_border0
`defineh_data_time800
`defineh_total_time1056`definev_bottom_border 0
`definev_front_porch1
`definev_sync_time4
`definev_back_porch23
`definev_top_border0
`definev_data_time600
`definev_total_time628
`endif
VGA驱动:
`include "vga_par.v" module vga_ctrl(
inputwireclk,//VGA时钟25.2MHz
inputwirerst_n,//复位信号
inputwire [23:00]data_dis,//outputreg [10:00]h_addr,//数据有效显示区域行地址
outputreg [10:00]v_addr,//数据有效显示区域场地址outputreghsync,//
outputregvsync,//outputreg[07:00]vga_r,//
outputreg[07:00]vga_g,//
outputreg[07:00]vga_b, //
outputregvga_blk,//vga消隐信号
outputwirevga_clk//
);
//参数定义parameterh_sync_sta =1,
h_sync_sto = `h_sync_time,
h_data_sta = `h_left_border+ `h_front_porch +`h_sync_time,
h_data_sto = `h_left_border+ `h_front_porch +`h_sync_time + `h_data_time,v_sync_sta = 1,
v_sync_sto = `v_sync_time,
v_data_sta = `v_top_border + `v_back_porch +`v_sync_time,
v_data_sto = `v_top_border + `v_back_porch +`v_sync_time + `v_data_time;
//信号定义
reg[11:0]cnt_h_addr;
//行地址计数器
wireadd_h_addr;
wireend_h_addr;
reg[11:0]cnt_v_addr;
//场地址计数器
wireadd_v_addr;
wireend_v_addr;
//
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_h_addr <= 12'd0;
end
else if (add_h_addr) begin
if (end_h_addr) begin
cnt_h_addr <= 12'd0;
end
else begin
cnt_h_addr <= cnt_h_addr + 12'd1;
end
end
else begin
cnt_h_addr <= cnt_h_addr;
end
endassign add_h_addr = 1'b1;
assign end_h_addr = add_h_addr && cnt_h_addr >= `h_total_time - 1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_v_addr <= 12'd0;
end
else if (add_v_addr) begin
if (end_v_addr) begin
cnt_v_addr <= 12'd0;
end
else begin
cnt_v_addr <= cnt_v_addr + 12'd1;
end
end
else begin
cnt_v_addr <= cnt_v_addr;
end
endassign add_v_addr = end_h_addr;
assign end_v_addr = add_v_addr && cnt_v_addr >= `v_total_time - 1;
//行场同步信号
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
hsync <= 1'b1;
end
else if(cnt_h_addr == h_sync_sta -1) begin
hsync <= 1'd0;
end
else if(cnt_h_addr == h_sync_sto -1)begin
hsync <= 1'b1;
end
else begin
hsync <= hsync;
end
endalways @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
vsync <= 1'b1;
end
else if(cnt_v_addr == v_sync_sta -1) begin
vsync <= 1'd0;
end
else if(cnt_v_addr == v_sync_sto -1)begin
vsync <= 1'b1;
end
else begin
vsync <= vsync;
end
endassign vga_clk = ~clk;
//数据有效显示区域定义
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
h_addr <= 11'd0;
end
else if((cnt_h_addr >= h_data_sta ) && (cnt_h_addr <= h_data_sto) )begin
h_addr <= cnt_h_addr - h_data_sta;
end
else begin
h_addr <= 11'd0;
end
endalways @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
v_addr <= 11'd0;
end
else if((cnt_v_addr >= v_data_sta ) && (cnt_v_addr <= v_data_sto))begin
v_addr <= cnt_v_addr - v_data_sta;
end
else begin
v_addr <= 11'd0;
end
end//显示数据
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
vga_r<= 8'd0;
vga_g<= 8'd0;
vga_b<= 8'd0;
vga_blk <= 1'b0;
end
else if((cnt_h_addr >= h_data_sta -1)
&&(cnt_h_addr <= h_data_sto -1)
&&(cnt_v_addr >= v_data_sta -1)
&&(cnt_v_addr <= v_data_sto -1))begin
vga_r <= data_dis[23-:08];
vga_g <= data_dis[15-:08];
vga_b <= data_dis[07-:08];
vga_blk <= 1'b1;
end
else begin
vga_r <= 8'd0;
vga_g <= 8'd0;
vga_b <= 8'd0;
vga_blk <= 1'b0;
end
end//assign sync = 1'b0;
endmodule
顶层文件:
module vga_top(
inputwireclk,
inputwirerst_n,
outputwirehsync,//
outputwirevsync,
outputwire[07:00]vga_r,//
outputwire[07:00]vga_g,//
outputwire[07:00]vga_b,//
outputwirevga_blk,
outputwirevga_clk//
);
wire [10:00]h_addr;
wire [10:00]v_addr;
wire [23:00]data_dis;
pll1 pll1_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( vga_clk ),
.c1 ( clk1 )
);
data_gen u_data_gen(
.clk(vga_clk),//VGA时钟25.2MHz
.rst_n(rst_n),//复位信号
.h_addr(h_addr),//数据有效显示区域行地址
.v_addr(v_addr),//数据有效显示区域场地址
.vga_blk(vga_blk),
.data_dis(data_dis )//
);
vga_ctrl u_vga_ctrl(
.clk(vga_clk),//VGA时钟25.2MHz
.rst_n(rst_n),//复位信号
.data_dis(data_dis ),//
.h_addr(h_addr),//数据有效显示区域行地址
.v_addr(v_addr),//数据有效显示区域场地址
.hsync(hsync),//
.vsync(vsync),//
.vga_r(vga_r),//
.vga_g(vga_g),//
.vga_b(vga_b), //
.vga_blk(vga_blk)
);
endmodule
2.1、彩条输出
module data_gen(
inputwireclk,//VGA时钟25.2MHz
inputwirerst_n,//复位信号
inputwire [10:00]h_addr,//数据有效显示区域行地址
inputwire [10:00]v_addr,//数据有效显示区域场地址
inputwirevga_blk,outputreg [23:00]data_dis//
);
parameter
BLACK= 24'H000000,
RED= 24'HFF0000,
GREEN= 24'H00FF00,
BLUE= 24'H0000FF,
YELLOW= 24'HFFFF00,
SKY_BULE= 24'H00FFFF,
PURPLE= 24'HFF00FF,
GRAY= 24'HC0C0C0,
WHITE= 24'HFFFFFF;
parameter
h_vld= 640,
v_vld= 480,pic_w=272,
pic_h=16,x_start=(h_vld - pic_w >>1 ) -1,
y_start=(v_vld - pic_h >>1 ) -1;
reg [10:00] pix_x,pix_y;
reg [ 271:0 ] char_line[ 15:0 ];
//272*16
reg [15:00]rom_address;
//彩条
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_dis <= WHITE;
end
elsebegin
case (h_addr)
0:data_dis <= BLACK;
80:data_dis <= RED;
160:data_dis <= GREEN;
240:data_dis <= BLUE;
320:data_dis <= YELLOW;
400:data_dis <= SKY_BULE;
480:data_dis <= PURPLE;
560:data_dis <= GRAY;
default :data_dis <= data_dis;
endcase
end
end
endmodule
2.2、点阵输出
//初始化显示文字
always@( posedge clk or negedge rst_n ) begin
if ( !rst_n ) begin
char_line[ 0 ]= 272'h010010010000000000000000000000000000000000000000000000000000;
char_line[ 1 ]= 272'h010021000804000000000000000000000000000000000000000000000000;
char_line[ 2 ]= 272'h028011fc7f78000000000000000000000000000000000000000000000000;
char_line[ 3 ]= 272'h04401200004018003c000800380018007e0018001800180008003c007e00;
char_line[ 4 ]= 272'h082085f82240240042003800440024004200240024002400380042004200;
char_line[ 5 ]= 272'h101041081440400042000800420042000400420040004200080042000400;
char_line[ 6 ]= 272'h2fe84948ff7e400002000800420042000400420040004200080042000400;
char_line[ 7 ]= 272'hc106092808485c000400080042004200080042005c004200080002000800;
char_line[ 8 ]= 272'h010017fe0848620018000800460042000800420062004200080004000800;
char_line[ 9 ]= 272'h3ff811087f484200040008003a0042001000420042004200080008001000;
char_line[ 10 ] = 272'h0100e2480848420002000800020042001000420042004200080010001000;
char_line[ 11 ] = 272'h111022282a48420042000800020042001000420042004200080020001000;
char_line[ 12 ] = 272'h110823fc4948220042000800240024001000240022002400080042001000;
char_line[ 13 ] = 272'h2104200888881c003c003e0018001800100018001c0018003e007e001000;
char_line[ 14 ] = 272'h450420502888000000000000000000000000000000000000000000000000;
char_line[ 15 ] = 272'h020000201108000000000000000000000000000000000000000000000000;
end
endalways @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pix_x <= 11'd0;
pix_y <= 11'd0;
end
else if ((h_addr >= x_start && h_addr < x_start +pic_w)
&&(v_addr >= y_start && v_addr < y_start + pic_h)) begin
pix_x <= h_addr - x_start;
pix_y <= v_addr - y_start;
end
else begin
pix_x <= 11'h7ff;
pix_y <= 11'h7ff;
end
endalways @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_dis <= BLACK;
end
else if (pix_x != 11'h7FF && pix_y != 11'h7FF) begin
if(char_line[pix_y][271 - pix_x]== 1'b1)begin
data_dis <= WHITE;
end
else begin
data_dis <= BLUE;
end
end
else begin
data_dis <= data_dis;
end
end
2.3、图片输出
*/
//ROM地址计数器
always @( posedge vga_clk or negedge rst_n ) begin
if ( !rst_n ) begin
rom_address <= 0;
end
else if ( flag_clear_rom_address ) begin //计数满清零
rom_address <= 0;
end
else if ( flag_enable_out2 ) begin//在有效区域内+1
rom_address <= rom_address + 1;
end
else begin//无效区域保持
rom_address <= rom_address;
end
end
assign flag_clear_rom_address = rom_address == 50267;
rom rom_inst (
.address ( rom_address ),
.clock ( clk ),
.q ( rom_data)
);
3、效果实现 3.1、引脚绑定 这里我实验用的是
EP4CE115F29C7
开发板,不同开发板根据手册绑定不同的引脚。文章图片
3.2、上板验证 效果:
文章图片
修改分辨率为800*600 时钟改为40MHz:
文章图片
可以看到图形整体左移,基本符合预期。
点阵:
文章图片
这里修改代码之后可以看见字符整体往左上方移动,基本符合预期。
文章图片
图片:
4、总结 VGA的实现在掌握VGA协议之后不算很难,主要是理解时序。还有确保硬件的问题,我最开始因为用的另一种开发板,外部接口有损,就算是编写正确工程也得不到想要的结果。之后换了开发板修改引脚,一下子就出来了。
5、参考链接 VGA协议
VGA接口
基于FPGA的VGA显示彩条、字符、图片
推荐阅读
- 数字ic|乘法器verilog
- FPGA|基于FPGA的ds18b20温度传感器使用
- 硬件经验|Lightning 转 USB Type-A/Type-C 思路
- 基础知识|【FPGA基础篇】底层结构组成
- fpga开发|TDC进位链
- HDLBITS学习笔记|HDLBITS笔记37(testbench错误检测集合1)
- VerilogHDL|FPGA 内部详细架构
- XILINX FPGA底层硬件资源
- DDR3|FPGA-DDR总线电源硬件设计技巧-Fly-by走线阻抗