【FPGA开发】VGA彩条显示及VGA白块位移

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,所有我们需要进行一次分频。

下面展示代码:


// -----------------------------------------------------------------------------
// 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的接口模拟,来实现比较高的分辨率和刷新率,来捕捉摄像头的一些实时画面。

  • 31
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值