FPGA|基于FPGA的VGA协议实现


目录

  • 1、VGA概述
    • 1.1、外部接口
    • 1.2、色彩原理
    • 1.3、扫描方式
    • 1.4、行场信号
  • 2、工程编写
    • 2.1、彩条输出
    • 2.2、点阵输出
    • 2.3、图片输出
  • 3、效果实现
    • 3.1、引脚绑定
    • 3.2、上板验证
  • 4、总结
  • 5、参考链接
【FPGA|基于FPGA的VGA协议实现】
1、VGA概述 VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及同步信号(水平和垂直信号)。
1.1、外部接口 实物图:
FPGA|基于FPGA的VGA协议实现
文章图片

电子原理图:
FPGA|基于FPGA的VGA协议实现
文章图片

由电路图可以看到,VGA并没有特殊的外部芯?,我们需要关注的其实只有5个信号:HS?同步信号,VS场同步信号,R红基?,G 绿基?,B蓝基?。
1.2、色彩原理 三基?是指通过其他颜?的混合?法得到的“基本?”由于?的?眼有感知红、绿、蓝三种不同颜?的锥体细胞,因此?彩空间通常可以由三种基本?来表达。这是?度学的最基本原理,即 三基?原理。三种基?是相互独?的,任何?种基?都不能有其它两种颜?合成。红绿蓝是三基?,这三种颜?合成的颜?范围最为?泛。我们的RGB信号真是三基?的运?,对这三个信号赋予不同的数值,混合起来便是不同的?彩。
FPGA|基于FPGA的VGA协议实现
文章图片

设计RGB信号时,既可以R信号、G信号和B信号独?的赋值,最后连到端?上,也可以直接?RGB当做?个整体信号,RGB信号在使
?时的位宽有三种常见格式。
  1. RGB_8,R:G:B = 3:3:2,即RGB332
  2. RGB_16,R:G:B = 5:6:5,即RGB565
  3. RGB_24,R:G:B = 8:8:8,即RGB888
1.3、扫描方式 GA显?器扫描?式分为逐?扫描和隔?扫描:逐?扫描是扫描从屏幕左上??点开始,从左像右逐点扫描,每扫描完??,电?束回 到屏幕的左边下??的起始位置,在这期间,CRT对电?束进?消隐,每?结束时,??同步信号进?同步;当扫描完所有的?,形成? 帧,?场同步信号进?场同步,并使扫描回到屏幕左上?,同时进?场消隐,开始下?帧。隔?扫描是指电?束扫描时每隔??扫?线,完成
?屏后在返回来扫描剩下的线,隔?扫描的显?器闪烁的厉害,会让使?者的眼睛疲劳。因此我们?般都采?逐?扫描的?式。
扫描原理如下所?:
FPGA|基于FPGA的VGA协议实现
文章图片

1.4、行场信号 FPGA|基于FPGA的VGA协议实现
文章图片

?开始看这个时序图可能看不懂,它是把?场信号绘制在同?张图?,说明?场信号的控制是相似的,只是时间参数不?样?已。如果展开的话,其实时序是这样的:
FPGA|基于FPGA的VGA协议实现
文章图片

若?个HS信号才组合?成?个VS,如果在?副图?中,那正确的时序表??式应该如下图这样:
FPGA|基于FPGA的VGA协议实现
文章图片

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开发板,不同开发板根据手册绑定不同的引脚。
FPGA|基于FPGA的VGA协议实现
文章图片

3.2、上板验证 效果:
FPGA|基于FPGA的VGA协议实现
文章图片

修改分辨率为800*600 时钟改为40MHz:
FPGA|基于FPGA的VGA协议实现
文章图片

可以看到图形整体左移,基本符合预期。
点阵:
FPGA|基于FPGA的VGA协议实现
文章图片

这里修改代码之后可以看见字符整体往左上方移动,基本符合预期。
FPGA|基于FPGA的VGA协议实现
文章图片

图片:
4、总结 VGA的实现在掌握VGA协议之后不算很难,主要是理解时序。还有确保硬件的问题,我最开始因为用的另一种开发板,外部接口有损,就算是编写正确工程也得不到想要的结果。之后换了开发板修改引脚,一下子就出来了。
5、参考链接 VGA协议
VGA接口
基于FPGA的VGA显示彩条、字符、图片

    推荐阅读