参考文章网址:
【开源骚客】:基于FPGA的摄像头视频显示系统——bilibili;
FPGA零基础学习:VGA协议驱动设计 - 知乎 (zhihu.com);
该文提及VGA的应用领域、母头公头的输入信号、借此解释VGA的原理,可先查看该文理解。
VGA(Video Graphics Array)视频图形阵列是 IBM 于1987年提出的一个使用模拟信号的电脑显示标准。VGA具有分辨率高、显示速率快、颜色丰富等优点。VGA 接口不但是CRT 显示设备的标准接口,同样也是 LCD 液晶显示设备的标准接口,具有广泛的应用范围。
用途:不仅用于接入CRT显示设备,同时可使用于 LCD 液晶显示设备的接入;
图片在数字设备中都是使用三基色组成:R(red)|G(green)|B(blue);
工作原理:
VGA的扫描方式是逐行扫描,也可以隔行扫描,但建议逐行扫描,即从显示器上像素点从第一扫到最后一个,再继续向下,到达右下角的最后一个点:
设备上查看原理图:以黑金Xlinx的AX309开发板举例,其原理图的VGA部分即:(其他开发可查询相关的VGA部分原理图)
VGA部分为右边,左边为R,G,B三部分的具体比例模块,正是使用这些比例来调配出65536种自然界人眼可辨认的颜色:
RED部分有五根引脚,GREEN有六根引脚,BLUE部分有五根引脚;
然后查看:VGA的通用驱动时序标准,咱们写时序逻辑设计时必须参照这个标准来写:
VESA标准及显示器时序标准.pdf (book118.com)
这里使用视频教学老师的800X600像素,里面有很多情况,可先尝试使用800X600像素再选择其他形式;
这是信号不同状态时所占的大概像素数,从上文我们已经可以得出每一行都是由像素扫描来完成的,像素即一个单位。
上面也已经说了,我们是由逐行扫描形式进行,那么每一行都要经历以下的各阶段变化,而每阶段大概占用多少的像素数在上面有所显示。
有提到,Hsync是行的同步信号,高电平时为一行的开始;(128像素点)
Vsync是场同步信号,为一幅图的新开始;
Addr_time为真实的有效区域;(占用800像素点)
由此可得设计思路:我们可以设计一个行计数器,一个场计数器:且上述说明中每个像素点扫描的时间都是40Mhz,两个计数器的时钟都是40Mhz;
定义两个计数器,使用40Mhz让它进行工作;举例我们的正式行显示(addr_time)的前面有128+88pixels才开始显示内容,即在217pixels时开始显示。
以行举例:
[0-215]赋值全0;
[216-1016]显示我们需要的颜色;
列操作相同。
来到ISE Design14.7,创建一个新工程:以下为具体的模块及封装,在说明书pdf的模块下都是有显示的。
该界面展示的是项目的路径信息;
驱动模块代码:
module vga_drive(
//system signals
input clk,
input rst_n,
//vga_drive
output wire H_sync,//行阵列
output wire V_sync,//位阵列
output wire [15:0] rgb //565的十六根RGB引脚(详见pdf)
);
/*define parameter and internal signals*/
localparam H_TOTAL_TIME = 1056 ;//整个时序链
localparam H_ADDR_TIME = 800 ;//正式可用的部分
localparam H_SYNC_TIME = 128 ;//正式
localparam H_BACK_PORCH = 88 ;//结束的边界
localparam V_TOTAL_TIME = 628 ;//场的
localparam V_ADDR_TIME = 600 ;
localparam V_SYNC_TIME = 4 ;
localparam V_BACK_PORCH = 23 ;//都对应pdf上的参数哦
//定义两个计数器:行计数器和场计数器
reg [11:0] cnt_h;//位宽查看时序参数,行总时间为1056pixels,使用计数器换算为11bits
reg [9:0] cnt_v;//换算为10bits;
//后面的参数也写进去
/*main code*/
always@(posedge clk or negedge rst_n)begin//行时序逻辑
if(!rst_n)
cnt_h <= 'd0;
end
else if(cnt_h>=H_TOTAL_TIME)
cnt_h <= 'd0;
end
else
cnt_h <= cnt_h+1'b1;
end
always@(posedge clk or negedge rst_n)begin//场的时序逻辑,根据行来计数
//与行的不一样,因为场
//的新触发条件是所有的行完成且场条件触发
//所以为(cnt_v>=V_TOTAL_TIME&&cnt_h>=H_TOTAL_TIME)
//这样的逻辑才对
if(!rst_n)
cnt_v <= 'd0;
end
else if(cnt_v>=V_TOTAL_TIME&&cnt_h>=H_TOTAL_TIME)
cnt_v <= 'd0;
end
else if(cnt_h>=H_TOTAL_TIME) //行满之后才进行场计数
cnt_v <= cnt_v+1'b1;
end
//显示颜色部分
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
vga <= 'd0;
//场有效语句、行有效语句,参考例文的正方模块
else if(cnt_v >=(V_SYNC_TIME + V_BACK_PORCH)&&(cnt_v <= V_BACK_PORCH_+V_SYNC_TIME+V_ADDR_TIME))
if(cnt_h >=(H_SYNC_TIME+H_BACK_PORCH)&&(cnt_h<=H_BACK_PORCH+200+H_SYNC_TIME))
vga <= 16'h0fff;
else if(cnt_h >=(H_SYNC_TIME+H_BACK_PORCH+200))&&(cnt_h<=H_BACK_PORCH+400+H_SYNC_TIME))//注意>=和<号的逻辑区间
vga <= 16'hf0ff;
else if(cnt_h >=(H_SYNC_TIME+H_BACK_PORCH+400))&&(cnt_h<=H_BACK_PORCH+600+H_SYNC_TIME))
vga <= 16'hff0f;
else if(cnt_h >=(H_SYNC_TIME+H_BACK_PORCH+600))&&(cnt_h<=H_BACK_PORCH+800+H_SYNC_TIME))
vga <= 16'hff0f;
else
vga <= 16'h0000;
end
else vga <= 16'h0000;
/*赋值部分*/
assign h_sync = (cnt_h < H_SYNC_TIME)?1'b1:1'b0;
assign v_sync = (cnt_v < V_SYNC_TIME)?1'b1:1'b0;
endmodule
顶层模块代码中,需要添加驱动模块的例化语句;
这样一个能显示颜色的vga驱动模块的代码编写就完成了。