FPGA图像音频通信接口:VGA

目录

VGA

1 VGA彩条显示实验

2、VGA方块移动实验

3 VGA字符显示实验


  

VGA是IBM公司在1987年推出的一种视频传输标准,具有分辨率高,显示速率快、颜色丰富等优点。VGA的全称是Video Graphics Array,即视频图形阵列,是一个使用模拟信号进行视频传输的标准。

VGA接口定义及各引脚功能说明如图 18.1.2所示,我们一般只用到其中的1(RED)、2(GREEN)、3(BLUE)、13(HSYNC)、14(VSYNC)信号。引脚1、2、3分别输出红、绿、蓝三原色模拟信号,电压变化范围为 0~0.714V,0V代表无色,0.714V 代表满色;引脚13、14输出TTL电平标准的行/场同步信号。

FPGA图像音频通信接口:VGA_第1张图片

 

在VGA视频传输标准中,视频图像被分解为红、绿、蓝三原色信号,经过数模转换之后,在行同步(HSYNC)和场同步(VSYNC)信号的同步下分别在三个独立通道传输。VGA在传输过程中的同步时序分为行时序和场时序,

FPGA图像音频通信接口:VGA_第2张图片

 

行同步时序

FPGA图像音频通信接口:VGA_第3张图片

 

场同步时序

一行或一场(又称一帧)数据都分为四个部分:低电平同步脉冲、显示后沿、有效数据段以及显示前沿。

行同步信号HSYNC在一个行扫描周期中完成一行图像的显示,其中在a段维持一段时间的低电平用于数据同步,其余时间拉高;在有效数据期间(c段),红绿蓝三原色数据通道上输出一行图像信号,其余时间数据无效。

场同步信号在一个场扫描周期中完成一帧图像的显示,不同的是行扫描周期的基本单位是像素点时钟,即完成一个像素点显示所需要的时间;而场扫描周期的基本单位是完成一行图像显示所需要的时间。

640*480@60表示每一行有640个像素,一帧有480行,每秒显示60帧图像

FPGA图像音频通信接口:VGA_第4张图片

 

不同分辨率的VGA时序参数

开拓者开发板上VGA接口,FPGA管脚输出的颜色数据位宽为16bit,数据格式为RGB565,即数据高5位表示红色,中间6位表示绿色,低5位表示蓝色。RGB565格式的数据一共可表示65536种颜色,此外常用的颜色数据格式还有RGB888,数据位宽越大,可以表示的颜色种类就越丰富。VGA传输的是模拟信号,因此需要对FPGA输出的RGB565颜色数据进行数模转换。可以通过专用的视频转换芯片(如ADV7123)来实现,也可以采用利用电阻匹配网络来实现数字信号到模拟信号的转换。

FPGA图像音频通信接口:VGA_第5张图片

 

VGA接口原理图

1 VGA彩条显示实验

实验任务

使用开拓者开发板上的VGA接口在显示器上显示彩条,要求分辨率为640*480,刷新速率为60hz。

程序设计

VGA时序包含三个要素:像素时钟、行场同步信号、以及图像数据,时钟分频模块负责产生像素时钟,VGA驱动模块产生行场同步信号,VGA显示模块输出图像数据。

FPGA图像音频通信接口:VGA_第6张图片

 

VGA彩条显示系统框图

由系统框图可知,FPGA部分包括四个模块,顶层模块(vga_colorbar)、时钟分频模块(vga_pll)、VGA显示模块(vga_display)以及VGA驱动模块(vga_driver)。其中在顶层模块中完成对另外三个模块的例化。

  时钟分频模块(vga_pll)通过调用锁相环(PLL) IP核来实现。根据实验任务要求的分辨率及刷新速率,通过图可以得知本次实验中VGA显示用到的像素时钟为25.175Mhz,因为分辨率不是很高,我们可以设置锁相环IP核让其输出25Mhz的时钟作为像素时钟

VGA驱动模块(vga_driver)在像素时钟的驱动下,根据VGA时序的参数输出行同步(vga_hs)、场同步(vga_vs)信号。同时VGA驱动模块还需要输出像素点的纵横坐标,供VGA显示模块(vga_display)调用,以绘制彩条图案。

FPGA图像音频通信接口:VGA_第7张图片

 

顶层模块原理图

顶层模块的代码如下:

module vga_colorbar(

     input sys_clk,

      input sys_rst_n,

      //VGA接口

      output  vga_hs,//行同步信号

      output  vga_vs,//场同步信号

      output  [15:0] vga_rgb//红绿蓝三原色输出

);

//wire define

wire   vga_clk_w;//PLL分频得到25Mhz时钟

wire   locked_w; //lock信号拉高表示PLL输出稳定信号 

wire   rst_n_w;  //内部复位信号

wire [15:0] pixel_data_w;//像素点数据

wire [9:0] pixel_xpos_w;//像素点横坐标

wire [9:0] pixel_ypos_w;//像素点纵坐标

//PLL输出稳定后,停止复位

assign rst_n_w=sys_rst_n&&locked_w;

//例化时钟分频模块

pll u_pll(

    .areset(~sys_rst_n),

    .inclk0(sys_clk),

    .c0(vga_clk_w),//VGA时钟 25M

    .locked(locked_w)

);

vga_driver u_vga_driver(

    .vga_clk(vga_clk_w),

     .rst_n(rst_n_w),

     .vga_hs(vga_hs),

     .vga_vs(vga_vs),

     .vga_rgb(vga_rgb),

     .pixel_data(pixel_data_w),

     .pixel_xpos(pixel_xpos_w),

     .pixel_ypos(pixel_ypos_w)

);

vga_display u_vga_display(

    .vga_clk(vga_clk_w),

     .rst_n(rst_n_w),

     .pixel_xpos(pixel_xpos_w),

     .pixel_ypos(pixel_ypos_w),

     .pixel_data(pixel_data_w)

);

endmodule

利用IP核进行时钟分频时, 系统上电复位后PLL输出的25Mhz时钟需要经过一段时间才能到达稳定状态。 在PLL输出稳定后,标志信号locked拉高。

由于VGA驱动模块及显示模块均由PLL输出的像素时钟驱动,因此在PLL输出稳定之前,其余模块应保持复位状态。通过将系统复位信号sys_rst_n和PLL输出稳定标志信号locked进行“与”操作,得到内部复位信号rst_n_w。将该信号作为VGA驱动模块及显示模块的复位信号,可避免由于系统复位后像素时钟不稳定造成的VGA时序错误。

VGA驱动模块代码如下

module vga_driver(

    input vga_clk,

     input rst_n,

     //VGA接口

     output    vga_hs, //行同步信号

     output    vga_vs, //场同步信号

     output [15:0] vga_rgb,//红绿蓝三原色输出

     

     input [15:0] pixel_data,//像素点数据

     output [9:0] pixel_xpos,//像素点横坐标

     output [9:0] pixel_ypos //像素点纵坐标

);

//parameter define  //定义了分辨率为640*480@60VGA时序中的各个参数

parameter H_SYNC=10'd96;//行同步

parameter H_BACK=10'd48;//行后沿显示

parameter H_DISP=10'd640;//行有效数据

parameter H_FRONT=10'd16;//行显示前沿

parameter H_TOTAL=10'd800;//行扫描周期

parameter V_SYNC=10'd2;//场同步

parameter V_BACK=10'd33;//场显示后沿

parameter V_DISP=10'd480;//场有效数据

parameter V_FRONT=10'd10;//场显示前沿

parameter V_TOTAL=10'd525;//场扫描周期

//reg define

reg [9:0] cnt_h;

reg [9:0] cnt_v;

//wire define

wire  vga_en;

wire  data_req;

//VGA行场同步信号

assign vga_hs=(cnt_h<H_SYNC)?1'b0:1'b1;

assign vga_vs=(cnt_v<V_SYNC)?1'b0:1'b1;

//RGB565数据输出使能信号

assign vga_en=(((cnt_h>=H_SYNC+H_BACK)&&(cnt_h<H_SYNC+H_BACK+H_DISP))

              &&((cnt_v>=V_SYNC+V_BACK)&&(cnt_v<V_SYNC+V_BACK+V_DISP)))

                  ?1'b1:1'b0;

//RGB565数据输出

assign vga_rgb=vga_en?pixel_data:16'd0;

//像素点颜色数据输入请求信号

assign data_req=(((cnt_h>=H_SYNC+H_BACK-1'b1)&&(cnt_h<H_SYNC+H_BACK+H_DISP-1'b1))

              &&((cnt_v>=V_SYNC+V_BACK)&&(cnt_v<V_SYNC+V_BACK+V_DISP)))

                  ?1'b1:1'b0;      

//像素点坐标

assign pixel_xpos=data_req?(cnt_h-(H_SYNC+H_BACK-1'b1)):10'd0;

assign pixel_ypos=data_req?(cnt_v-(V_SYNC+V_BACK-1'b1)):10'd0;   

//行计数器对像素时钟计数

always @(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         cnt_h<=10'd0;

     else begin

         if(cnt_h<H_TOTAL-1'b1)

              cnt_h<=cnt_h+1'b1;

          else

          cnt_h<=10'd0;

     end

end

//场计数器对行计数,

//扫描一个行后cnt_v1,计满一个行扫描周期后清零并重新计数

always@(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         cnt_v<=10'd0;

     else if(cnt_h==H_TOTAL-1'b1) begin

         if(cnt_v<V_TOTAL-1'b1)

              cnt_v<=cnt_v+1'b1;

          else

              cnt_v<=10'd0;

     end

end

endmodule

由于坐标输出后下一个时钟周期才能接收到像素点的颜色数据,因此数据请求信号data_req比数据输出使能信号vga_en提前一个时钟周期。

VGA显示模块的代码

module vga_display(

    input vga_clk,//VGA驱动时钟

     input rst_n,//复位信号

     

     input [9:0] pixel_xpos,//像素点横坐标

     input [9:0] pixel_ypos,//像素点纵坐标

     output reg [15:0] pixel_data //像素点数据

);

parameter H_DISP=10'd640;   //分辨率一行

parameter V_DISP=10'd480;   //分辨率一列

parameter WHITE=16'b11111_111111_11111;//RGB565白色

parameter BLACK=16'd00000_000000_00000;//RGB565黑色

parameter RED=16'd11111_000000_00000;//红色

parameter GREEN=16'b00000_111111_00000;//绿色

parameter BLUE=16'b00000_000000_11111;//蓝色

//根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条

always @(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         pixel_data<=16'd0;

     else begin

         if((pixel_xpos>=0)&&(pixel_xpos<(H_DISP/5)*1))

              pixel_data<=WHITE;

          else if((pixel_xpos>=(H_DISP/5)*1)&&(pixel_xpos<(H_DISP/5)*2))

              pixel_data<=BLACK;

          else if((pixel_xpos>=(H_DISP/5)*2)&&(pixel_xpos<(H_DISP/5)*3))

              pixel_data<=RED;

          else if((pixel_xpos>=(H_DISP/5)*3)&&(pixel_xpos<(H_DISP/5)*4))

              pixel_data<=GREEN;

          else

              pixel_data<=BLUE;

         

     end

end

endmodule

2、VGA方块移动实验

实验任务:

   使用开拓者开发板上的VGA接口在显示器上显示一个不停移动的方块,要求方块移动到边界处时能够改变移动方向。显示分辨率为640*480@60;

程序设计:

   在“VGA彩条显示实验”中,我们利用VGA驱动模块输出的像素点的横坐标,在VGA显示模块中完成了彩条图案的绘制。而在本次实验中,为了完成方块的显示,需要同时使用像素点的横坐标和纵坐标来绘制方块所在的矩形区域,另外还需要知道矩形区域左上角的顶点坐标。由于VGA显示的图像在行场同步信号的同步下不停的刷新,因此只要连续改变方块左上角顶点的坐标,并在新的坐标点处重新绘制方块,即可实现方块移动的效果。

VGA显示模块代码如下:

module vga_display(

    input vga_clk,//VGA驱动时钟

     input rst_n,//复位信号

     

     input [9:0] pixel_xpos,//像素点横坐标

     input [9:0] pixel_ypos,//像素点纵坐标

     output reg [15:0] pixel_data //像素点数据

);

parameter H_DISP=10'd640;   //分辨率一行

parameter V_DISP=10'd480;   //分辨率一列

parameter SIDE_W=10'd40;  //边框宽度

parameter BLOCK_W=10'd40; //方框宽度

parameter WHITE=16'b11111_111111_11111;//背景颜色白色

parameter BLACK=16'd00000_000000_00000;//方块颜色黑色

parameter BLUE=16'b00000_000000_11111; //边框颜色蓝色

//reg define

reg [9:0] block_x;   //方块左上角横坐标

reg [9:0] block_y;   //方块左上角纵坐标

reg [21:0] div_cnt;  //时钟分频计数器

reg       h_direct;  //方块水平移动方向,1:右移,0:左移

reg       v_direct;  //方块竖直移动方向,1:向下,0:向上

//wire define

wire move_en;  //方块移动使能信号,频率为100hz

assign move_en=(div_cnt==22'd250000-1'b1)?1'b1:1'b0;

//通过对vga驱动时钟计数,实现时钟分频,频率为100hz

always @(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         div_cnt<=22'd0;

     else begin

         if(div_cnt<22'd25000-1'b1)

             div_cnt<=div_cnt+1'b1;

          else

             div_cnt<=22'd0;   //计数达10ms清零

     end

end

//当方块移动到边界时,改变移动方向

always @(posedge vga_clk or negedge rst_n) begin

    if(!rst_n) begin

         h_direct<=1'b1; //方块初始水平向右移动

          v_direct<=1'b1; //方块初始竖直向下移动

     end

     else begin

         if(block_x==SIDE_W-1'b1)  //到达左边界时,水平向右

              h_direct<=1'b1;

          else if(block_x==H_DISP-SIDE_W-BLOCK_W)  //到达右边界时,水平向左

              h_direct<=1'b0;

          else

              h_direct<=h_direct;

          if(block_y==SIDE_W-1'b1) //到达上边界时,竖直向下

              v_direct<=1'b1;

          else if(block_y==H_DISP-SIDE_W-BLOCK_W)   //到达下边界时,竖直向上

             v_direct<=1'b0;

         else

             v_direct<=v_direct;   

     end

end

//根据方块移动方向,改变其纵横坐标

always@(posedge vga_clk or negedge rst_n) begin

    if(!rst_n) begin

         block_x<=22'd100;//方块初始位置横坐标

          block_y<=22'd100;//方块初始位置纵坐标

     end

     else if(move_en) begin

         if(h_direct)

              block_x<=block_x+1'b1;//方块向右移动

          else

              block_y<=block_y+1'b1;//方块向左移动

          if(v_direct)

              block_y<=block_y+1'b1;//方向向下移动

          else

              block_y<=block_y-1'b1;//方向向上移动

     end

     else begin

         block_x<=block_x;

          block_y<=block_y;

     end

end

//给不同的区域绘制不同的颜色

always@(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         pixel_data<=BLACK;

     else begin

         if((pixel_xpos<SIDE_W)||(pixel_xpos>=H_DISP-SIDE_W)

          ||(pixel_ypos<SIDE_W)||(pixel_ypos>=V_DISP-SIDE_W))

              pixel_data<=BLUE;//绘制边框为蓝色

         else if((pixel_xpos>=block_x)||(pixel_xpos<block_x+BLOCK_W)

          ||(pixel_ypos>=block_y)||(pixel_ypos<block_y+BLOCK_W))

              pixel_data<=BLACK;//绘制边框为黑色

          else

              pixel_data<=WHITE;//绘制背景为白色

     end

end

endmodule

由于VGA驱动时钟相对于方块移动速度而言过快,我们通过计数器对时钟计数,得到一个频率为100hz的脉冲信号move_en,用它作为使能信号来控制方块的移动。根据方块的移动方向,在使能信号move_en到来时改变其左上角顶点的纵横坐标值。当move_en的频率为100hz时,方块每秒钟在水平和竖直方向上分别移动100个像素点的距离,也可以通过调整move_en的频率,来加快或减慢方块移动的速度。

3 VGA字符显示实验

实验任务:

实验任务是使用开拓者开发板上的VGA接口在显示器的屏幕中心位置显示汉字“天津科技大学”。显示分辨率为640*480,刷新速率为60hz,每个汉字的大小为16*16。

字符(包括汉字、字母和符号等)的本质都是点阵,在VGA屏幕上体现为字符显示区域内像素点的集合。字符的大小决定了字符显示区域内像素点的数目,而字符的样式(字体、颜色等)则决定了各像素点的颜色值。因此,在显示字符之前,我们需要先指定字符的大小、样式,然后获取该字符的点阵,这个过程我们称之为“提取字模”,或简称“取模”。

一般使用0和1的组合来描述字符的点阵排列:点阵中每个像素点用一位(1 bit)数据来表示, 其中用于表征字符的像素点用数字1来表示, 其他的像素点作为背景用数字0来表示。“每行显示数据”是以字节(Byte)为单位的,而一个字节的数据为8个bit,即可以表示一行点阵中的8个像素点。

实验程序如下:

module vga_display(

    input vga_clk,//VGA驱动时钟

     input rst_n,//复位信号

     

     input [9:0] pixel_xpos,//像素点横坐标

     input [9:0] pixel_ypos,//像素点纵坐标

     output reg [15:0] pixel_data //像素点数据

);

parameter H_DISP=10'd640;   //分辨率一行

parameter V_DISP=10'd480;   //分辨率一列

parameter POS_X=10'd288;  //字符区域起始点横坐标

parameter POS_Y=10'd232; //字符区域起始点纵坐标

parameter WIDTH=10'd96;//字符区域宽度

parameter HEIGHT=10'd16;//字符区域高度

parameter RED=16'b11111_000000_00000;//字符颜色红色

parameter BLACK=16'd00000_000000_00000;//屏幕颜色黑色

parameter BLUE=16'b00000_000000_11111; //字符域背景颜色蓝色

//reg define

reg [95:0] char [15:0]; //二维字符数组

//wire define

wire [9:0] x_cnt;

wire [9:0] y_cnt;

assign x_cnt=pixel_xpos-POS_X;//像素点相对于字符区域起始点水平坐标

assign y_cnt=pixel_ypos-POS_Y;//像素点相对于字符区域起始点竖直坐标

//给字符数组赋值,显示汉字天津科技大学,汉字大小为16*16

always @(posedge vga_clk) begin

    char[0]<=96'h000000400810102001002208;

     char[1]<=96'h3FF820401D10102001001108;

     char[2]<=96'h010013F8F090102001001110;

     char[3]<=96'h01001048109013FE01000020;

     char[4]<=96'h010087FE1010FC2001007FFE;

     char[5]<=96'h01004048FD101020FFFE4002;

     char[6]<=96'hFFFE4BF81090102001008004;

     char[7]<=96'h01000840389015FC01001FE0;

     char[8]<=96'h028010403410188402800040;

     char[9]<=96'h028013F8501E308802800180;

     char[10]<=96'h0440E04053F0D0480440FFFE;

     char[11]<=96'h044020409010105004400100;

     char[12]<=96'h082027FC1010102008200100;

     char[13]<=96'h101020401010105010100100;

     char[14]<=96'h200820401010518820080500;

     char[15]<=96'hC006004010102606C0060200;

     

end

//给不同的区域绘制不同的颜色

always@(posedge vga_clk or negedge rst_n) begin

    if(!rst_n)

         pixel_data<=BLACK;

     else begin

         if((pixel_xpos>=POS_X)||(pixel_xpos<POS_X+WIDTH)

          ||(pixel_ypos>=POS_Y)||(pixel_ypos<POS_Y+HEIGHT))begin

              if(char[y_cnt][10'd95-x_cnt])

                  pixel_data<=RED;//绘制字符为蓝色

             else

                  pixel_data<=BLUE;//绘制字符区域背景为黑色

            end

          else

              pixel_data<=BLACK;//绘制屏幕为黑色

     end

end

endmodule

你可能感兴趣的:(fpga开发,音视频)