说完了通信,我们为了后面的想讲图像处理的东西,所以先打一个基础,讲一讲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分辨率,并不是你就只传输这么多个像素点,在真实传输时会有一些无效的并不显示的像素点,在每一行的开始与结束,和每一帧的开始几行与结束前几行。如图只有最中间的部分才是显示的部分,而周边是不现实的部分。不现实的部分是为了适应人眼的视觉暂留所必需的,所以不能省略。
这样就带来了问题,我们如何确定一行结束或者是一阵结束的,所以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的逻辑