1. 实验目的:
为了理解VGA的工作模式,并且进行vga一部分的模板开发,方便后续图像处理显示的需求。这里通过对vga时序进行分析,进行简单的vga图像控制。实验阶段进行的是640*480@60hz的分辨率和刷新率,后续因为需要适应显示器的规格。会在模块化的阶段进行参数的重新分配,变成可编辑参数模块。
2. 实验步骤
2.1 实现vga的彩条显示
2.2 实现vga的白块位移
2.3 实现vga的模块化处理(后续进行)
3. 实验原理:
3.1 VGA接口介绍:
我们的开发板上VGA输出的是8bits,这是因为我们选用了256色的VGA接口,并且使用了一个DAC芯片,对FPGA输出的数字信号进行数模转换。
VGA输出的是模拟信号,因此在比较高的分辨率的时候,往往会出出现边缘模糊,失真的情况,在这些情况下,我们就会使用hdmi等等数字信号的接口。一般情况下VGA输出的最高分辨为1080p.
3.2 VGA显像介绍:
VGA显示图像使用扫描的方式,从第一行的第一个像素开始,逐渐填充,第一行第一个、第一行第二个,第二行第一个、第二行第二,第n行最后一个。
通过这种方式构成一帧完整的图像,当扫描速度足够快,加之人眼的视觉暂留特性,我们会看到一幅完整的图片,而不是一个个闪烁的像素点。这就是VGA 显示的原理。
VGA有两个非常重要的信号,一个是行同步信号(HSYNC),另一个是场同步信号(VSYNC)。通过这两个信号完成一帧图像的像素点扫描
也就是当HSYNC,VSYNC的高电平来临,VGA显示器就知道,我要开始接收这一行或列要显示的像素信息的,当下一个高电平来临时,那么这一行的图像信息就传输完成了,其中真正要显示的图像信息就蕴含在这一周期中。
具体参数如下:
我们可以看到,我们要显示一个图像的时候,还会包含后沿,前沿这些部分。这些都是要考虑到我们的计数器中去的。
-这里我们要使用的是640*480@60 具体参数如下表格所示
下面我们就开始具体代码的编写。具体对参数的一些图像绘制如下
4. 代码实现:
4.1 vga_ctrl:
这段代码实现的是vga在显示器上三个彩条的显示,之前忘了保存显示的照片,这里就只展示我们代码部分。
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : vga_ctrl.v
// Create : 2024-01-15 09:48:57
// Revise : 2024-01-17 11:30:20
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module vga_ctrl(
input wire i_vclk,
input wire i_rst,
output wire o_hsync, //水平也就是行
output wire o_vsync, //垂直也就是列
output wire [7:0] o_rgb
);
//output logic
reg hsync;
reg vsync;
reg [7:0] rgb;
assign o_hsync = hsync;
assign o_vsync = vsync;
assign o_rgb = rgb;
reg [11:0] h_cnt; //列
reg [11:0] cnt_v; //行
//hv cnt
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
h_cnt <= 'd0;
end
else if (h_cnt == 'd799) begin
h_cnt <= 'd0;
end
else begin
h_cnt <= h_cnt + 'd1;
end
end
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
cnt_v <= 'd0;
end
else if (cnt_v == 'd524 && h_cnt == 'd799) begin
cnt_v <= 'd0;
end
else if(h_cnt == 'd799)begin
cnt_v <= cnt_v + 'd1;
end
else begin
cnt_v <= cnt_v;
end
end
//sync
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
vsync <= 1'b0;
end
else if(h_cnt == 'd799) begin
vsync <= 1'b1;
end
else if(h_cnt == 'd95)begin
vsync <= 1'b0;
end
else begin
vsync <= vsync;
end
end
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
hsync <= 1'b0;
end
else if (cnt_v == 'd524 && h_cnt == 'd799) begin
hsync <= 1'b1;
end
else if (cnt_v == 'd1 && h_cnt == 'd799)begin
hsync <= 1'b0;
end
else begin
hsync <= hsync;
end
end
//rgb
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
rgb <= 8'b000_000_00;
end
else if (cnt_v >= 'd35 && cnt_v <= 'd194 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b111_000_00;
end
else if (cnt_v >= 'd195 && cnt_v <= 'd354 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b000_111_00;
end
else if (cnt_v >= 'd355 && cnt_v <= 'd514 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b000_000_11;
end
else begin
rgb <= 8'b000_000_00;
end
end
endmodule
4.2 vga_shift:
这段代码显示的是在彩条的基础上增加了白框的移动,按照我们的分辨率来说,我们的刷新频率是60hz,因此我们每一帧都对白框进行一个像素点的位移,等到碰到边框的时候再朝反方向位移。最后实现一个类似于屏幕保护界面的效果。这里也一样仅展示代码
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : vga_shift.v
// Create : 2024-01-16 13:25:01
// Revise : 2024-01-17 11:37:03
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module vga_shift(
input wire i_vclk,
input wire i_rst,
output wire o_hsync,
output wire o_vsync,
output wire [7:0] o_rgb
);
//output logic
reg hsync;
reg vsync;
reg [7:0] rgb;
assign o_hsync = hsync;
assign o_vsync = vsync;
assign o_rgb = rgb;
reg [11:0] h_cnt; //列
reg [11:0] v_cnt; //行
reg x_dir; //x轴的行进方向 0向右 1向左
reg y_dir; //y轴的行进反向 0向下 1向上
reg [9:0] x_pixel; //x轴坐标像素
reg [9:0] y_pixel; //y轴坐标像素
//x direction
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
x_dir <= 1'b0;
end
else if (x_pixel == 'd439 && v_cnt == 'd524 && h_cnt == 'd799) begin
x_dir <= 1'b1;
end
else if (x_pixel == 'd0 && v_cnt == 'd524 && h_cnt == 'd799) begin
x_dir <= 1'b0;
end
else begin
x_dir <= x_dir;
end
end
//y direction
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
y_dir <= 1'b0;
end
else if (x_pixel == 'd279 && v_cnt == 'd524 && h_cnt == 'd799) begin
y_dir <= 1'b1;
end
else if (x_pixel == 'd0 && v_cnt == 'd524 && h_cnt == 'd799) begin
y_dir <= 1'b0;
end
else begin
y_dir <= x_dir;
end
end
//x_pixel
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
x_pixel <= 'd0;
end
else if (x_dir == 1'b0 && v_cnt == 'd524 && h_cnt == 'd799) begin
x_pixel <= x_pixel + 'd1;
end
else if (x_dir == 1'b1 && v_cnt == 'd524 && h_cnt == 'd799) begin
x_pixel <= x_pixel - 'd1;
end
else begin
x_pixel <= x_pixel;
end
end
//y_pixel
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
y_pixel <= 'd0;
end
else if (y_dir == 1'b0 && v_cnt == 'd524 && h_cnt == 'd799) begin
y_pixel <= y_pixel + 'd1;
end
else if (y_dir == 1'b1 && v_cnt == 'd524 && h_cnt == 'd799) begin
y_pixel <= y_pixel - 'd1;
end
else begin
y_pixel <= y_pixel;
end
end
//hv cnt
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
h_cnt <= 'd0;
end
else if (h_cnt == 'd799) begin
h_cnt <= 'd0;
end
else begin
h_cnt <= h_cnt + 'd1;
end
end
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
v_cnt <= 'd0;
end
else if (v_cnt == 'd524 && h_cnt == 'd799) begin
v_cnt <= 'd0;
end
else if(h_cnt == 'd799)begin
v_cnt <= v_cnt + 'd1;
end
else begin
v_cnt <= v_cnt;
end
end
//sync
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
vsync <= 1'b0;
end
else if(h_cnt == 'd799) begin
vsync <= 1'b1;
end
else if(h_cnt == 'd95)begin
vsync <= 1'b0;
end
else begin
vsync <= vsync;
end
end
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
hsync <= 1'b0;
end
else if (v_cnt == 'd524 && h_cnt == 'd799) begin
hsync <= 1'b1;
end
else if (v_cnt == 'd1 && h_cnt == 'd799)begin
hsync <= 1'b0;
end
else begin
hsync <= hsync;
end
end
//rgb
always @(posedge i_vclk) begin
if (i_rst == 1'b1) begin
rgb <= 8'b000_000_00;
end
else if(v_cnt >= ('d35 + y_pixel) && v_cnt <= ('d234 + y_pixel) && h_cnt >= ('d144 + x_pixel) && v_cnt <= ('d343 + x_pixel)) begin
rgb <= 8'b111_111_11;
end
else if (v_cnt >= 'd35 && v_cnt <= 'd194 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b111_000_00;
end
else if (v_cnt >= 'd195 && v_cnt <= 'd354 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b000_111_00;
end
else if (v_cnt >= 'd355 && v_cnt <= 'd514 && h_cnt >= 'd144 && h_cnt <= 'd783) begin
rgb <= 8'b000_000_11;
end
else begin
rgb <= 8'b000_000_00;
end
end
endmodule
4.3 top_vga:
需要注意的是,我们的刷新率为60hz每帧我们需要传输640*480个像素的数据,算上我们的边界和同步也就是说,我们一张图片需要在1/60s内完成800*525个周期 = 25200000cycle/s 换算成时钟频率也就是25.2mhz,因此我们要完成图像60hz的显示我们至少需要25.2mhz的时钟,如果不能达到这个时钟频率的话,我们的刷新率就会达不到标准。所以在顶层,我们需要使用一个PLL来分频一个25mhz的时钟。我们板子上的晶振产生的时钟是50mhz,所有我们需要进行一次分频。
下面展示代码:文章来源:https://www.toymoban.com/news/detail-810949.html
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File : top_vga.v
// Create : 2024-01-15 13:54:16
// Revise : 2024-01-16 16:28:11
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module top_vga(
input wire i_sclk,
input wire i_rst_n,
output wire o_hsync,
output wire o_vsync,
output wire [7:0] o_rgb
);
//module instance
wire clk_25mhz;
wire clk_50mhz;
wire hsync;
wire vsync;
wire [7:0] rgb;
wire rst;
assign rst = ~i_rst_n;
//PLL
clk_gen_inst inst_clk(
// Clock out ports
.clk_50mhz(clk_50mhz), // output clk_50mhz
.clk_25mhz(clk_25mhz), // output clk_25mhz
// Clock in ports
.clk_in1(i_sclk) // input clk_in1
);
//vga instance
vga_ctrl inst_vga_ctrl(
.i_vclk (clk_25mhz),
.i_rst (rst),
.o_hsync (hsync),
.o_vsync (vsync),
.o_rgb (rgb)
);
vga_shift inst_vga_shift(
.i_vclk (clk_25mhz),
.i_rst (rst),
.o_hsync (hsync),
.o_vsync (vsync),
.o_rgb (rgb)
);
endmodule
5. 实验总结:
在这个实验中,使用的是256色的vga,代码虽然实现了目的,但是缺点也很大,缺点在于只能实现对应分辨率和刷新率的功能,因此后面要有模块化开发的思维,方便后期的维护。或者说留下相应的接口,可以方便后续上位机可以直接更改分辨率和RGB内容。VGA是一个比较低速的一个视频接口,后续的文章中要实现hdmi的接口模拟,来实现比较高的分辨率和刷新率,来捕捉摄像头的一些实时画面。文章来源地址https://www.toymoban.com/news/detail-810949.html
到了这里,关于【FPGA开发】VGA彩条显示及VGA白块位移的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!