trustintruth · 2020年05月25日

FPGA初始——VGA

说完了通信,我们为了后面的想讲图像处理的东西,所以先打一个基础,讲一讲VGA接口。首先预告一下,这是初始部分倒数第二个模块讲解,最后一个模块将带着大家手撸一个较简单的CPU,分上下三次将CPU做完成。同时有小伙伴说只有代码还不太会用工具,我们在做CPU的时候再带大家熟悉Xilinx最常用的两款ISE和Vivado,如果有时间简单写一下Quartus。欢迎大家持续关注我们。
作者:Trustintruth
来源:https://zhuanlan.zhihu.com/p/94561644



话说回来,我们先说这次分享的VGA。在我们曾经用的老款台式机显示器接口就是VGA,如下图这样

就像我们之前说的通信协议,主机与显示器之间还是需要一种特定的“说话方式”来确定显示器听得懂你的数据,并且能按照你想要的顺序在显示器上显示出来。

首先我们屏幕的画面是由一个一个小像素组成的,每个像素就是屏幕上的一个点。而这个像素点可以表示的颜色种类数就由每个像素点的深度决定,如果深度为1,那么他只有1位,现实的颜色数只有2^1个,也就是2个,0代表黑色,1代表白色,也就是所谓2值图像。可是这样只有一个灰色图像,为了得到彩色图像,VGA接口为RGB数据通路,即R(red)有它对应的数据深度,G(green),B(blue)也有他对应的数据深度。而有这三种颜色(光学三原色)就可以组成各种各样的颜色。而我们一般讲的RGB565也就是R深度B深度是5,G深度是6,整体数据为16位,前5位是R,中间6位是G,最后5位是B。

像素点的排列最后组成了显示屏上的画面,但是在显示屏显示的时候并不是一起显示的,而是从左到右,从上到下依次扫描现实的。由于人眼会有视觉暂停,所以扫描速度足够快,就可以达到我们看到的是一整幅图片。同样每一个像素点的发送与接收需要用到时钟,而时钟频率是这样计算的:

如果你平时玩电脑时遇到过黑屏,黑屏是就会有这样的一行字“60HZ”这个频率指的就是每帧的频率,而根据一帧的频率和一阵所具有的像素点(例如使用分辨率为640x480的)就可以计算出每个像素点传输的频率了。

需要注意的是,比如你选择的是640x480分辨率,并不是你就只传输这么多个像素点,在真实传输时会有一些无效的并不显示的像素点,在每一行的开始与结束,和每一帧的开始几行与结束前几行。如图只有最中间的部分才是显示的部分,而周边是不现实的部分。不现实的部分是为了适应人眼的视觉暂留所必需的,所以不能省略。


519.jpg
这样就带来了问题,我们如何确定一行结束或者是一阵结束的,所以VGA中有两个确定一行和一帧结束的信号,即为行信号和场信号,行信号是一行开始的信号,场信号是一帧开始的信号,如上面那张图中所示的,一帧开始时常信号拉高,拉高的时间有我们的时序决定,之后拉低,该脉冲表示我们进入新的一行

根据上边时序和时序数据我们可以得到各部分经过多少各脉冲。根据以上时序就可以设计。

我们首先设置一个计数器,在设置行场信号中的各个阶段的时间常数,在到达常数时间时(例如计数器记到800,也就是一行结束了,下一行开始,这时就拉高行信号,重新计数,记到行信号拉高时间参数时就再次拉低行信号,等待计数器再记到800就可以)

讲的道理很多其实设计很简单,下面只给大家时序控制的部分的代码

module vga_time(
// output 
hsignal,         //行同步信号
vsignal,         //场同步信号
vga_visual,      //可视区
x,               //定位可视区的横坐标  x   max = 640
y,               //定位可视区的纵坐标  y   max = 480

// input
clk_25MHZ,
rst_n);          //异步复位

//input 
input  clk_25MHZ;
input  rst_n;

//output 
output  hsignal;
output  vsignal;
output reg vga_visual;
output reg [10:0] x;      //2^10 = 1024
output reg [10:0] y;

//reg and wire and parameter 
parameter hmax = 11'd800;                            //TB    10'd50;    // 11'd1056;
parameter hsp  = 11'd96 ;                            //      10'd10;    // 10'd128 ;
parameter hbp  = 11'd144;    //96 + 48               //      10'd20;    // 10'd216;
parameter hv   = 11'd784;    //800 - 16              //      10'd40;    // 11'd1016;
parameter vmax = 11'd521;                            //      10'd60;    // 10'd628; 
parameter vsp  = 11'd2;                              //      10'd10;    // 10'd4;
parameter vbp  = 11'd27 ;    //2 + 29                //      10'd30;    // 10'd27 ; 
parameter vv   = 11'd511;    //628 - 1               //      10'd50;    // 10'd627;

reg [10:0] h_cnt;
reg [10:0] v_cnt;

///////////////////////h_cnt//////////////////
always @ ( posedge clk_25MHZ ) begin
    if( !rst_n ) begin
         h_cnt  <= 10'b0;
      end
    else if( h_cnt  < hmax ) begin
        h_cnt  <= h_cnt + 10'b1;
      end     
    else begin
         h_cnt  <= 10'b1;
      end 
end 

assign hsignal = ( h_cnt <= hsp )? 1'b0: 1'b1 ;

//////////////////v_cnt//////////////////////////
always @ ( posedge clk_25MHZ ) begin 
    if( !rst_n ) begin
        v_cnt <= 10'b0;
      end 
    else if( h_cnt == hmax ) begin
       if( v_cnt < vmax ) begin 
           v_cnt <= v_cnt + 10'b1;
           end
        else 
           v_cnt <= 10'b1;
      end     
  end 

assign vsignal = ( v_cnt <= vsp ) ? 1'b0 : 1'b1 ;

////////////////vag_visual//////////////////////
always @ ( * ) begin
    if((h_cnt > hbp )&&( h_cnt <= hv )&&( v_cnt > vbp )&&( v_cnt <= vv ))
        vga_visual = 1'b1;
    else 
        vga_visual = 1'b0;
  end 

always @ ( * ) begin
     if( vga_visual ) begin
         x <= h_cnt - hbp - 1;
         y <= v_cnt - vbp - 1;   //x,y 的有效值从0开始
       end 
     else begin
         x <= 1'b0;
         y <= 1'b0;
       end
  end
endmodule


推荐阅读

关注此系列,请关注专栏FPGA的逻辑
3 阅读 269
推荐阅读
0 条评论
关注数
1248
内容数
110
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
Arm中国学堂公众号
关注Arm中国学堂
实时获取免费 Arm 教学资源信息
Arm中国招聘公众号
关注Arm中国招聘
实时获取 Arm 中国职位信息