2505|2

859

帖子

3

TA的资源

版主

楼主
 

09、安路SparkRoad国产FPGA测评【实战篇】按键控制VGA显示 [复制链接]

  本帖最后由 1nnocent 于 2022-7-31 10:39 编辑

    现在调整一下测评计划,增加一个按键控制VGA显示的实战篇。

    该实验实现按键控制VGA的显示,SW18控制输出模式(VGA例程的四种显示模式),SW20 控制显示器的分辨率(1280*1024和1024*768两种切换)。本来想实现四种输出分辨率的,但是PLL不是万能的,有些通道输出的频率不是那么准确,各种分辨率的像素时钟误差有一定的要求:频率偏差不超过±0.5%,这个可以在“VESA和计算机显示监视器计时(DMT)行业标准和指南”中查看,文件在测评的第八篇。一共产生了五个像素时钟,最终只有两个符合要求,所以切换分辨率功能暂时只能实现两种分辨率的切换,后续有空再看看怎么多加点分辨率的切换种类(后续的工作也只是复制粘贴了,实现方式基本一致)。

    接下来讲解一下实现代码:

    首先是顶层模块:1、输入输出端口只需要在开发板原有VGA显示例程的基础上再加一个两位的按键输入接口,其他不变;2、当然还需要添加例程的按键消抖模块,后续需要根据按键的状态对显示模式和分辨率进行切换操作;3、PLL_OUT模块产生两个像素时钟,一个输出108MHz,一个输出65MHz(实际输出只有64.8MHz符合±0.5%的要求),两个像素时钟频率分别对应的分辨率为1280*1024、1024*768;4、最后是按照功能输出相应的显示状态。顶层模块代码(贴出来的代码tab键都没了,代码变成了左边对齐,看起来不是很美观是我设置的问题吗?插入代码的时候好像也没有设置的地方):

`timescale 1ns/1ns
module VGA_Demo
( 
	input  wire			clk_24m,
	input  wire			rst_n,
	input  wire	[1:0]	key,		// 输入按键
	
	//lcd interface
	output wire			vga_clk,	//lcd pixel clock			
	output wire			vga_hs,		//lcd horizontal sync 
	output wire			vga_vs,		//lcd vertical sync
    
	output wire [7:0]  	vga_r,		//lcd red data
	output wire [7:0]	vga_g,		//lcd green data
	output wire [7:0]	vga_b		//lcd blue data
);

wire	clk_vga;
wire	[11:0]	lcd_xpos;		//lcd horizontal coordinate
wire	[11:0]	lcd_ypos;		//lcd vertical coordinate
wire	[23:0]	lcd_data;		//lcd data
wire	Clk_Lock;
wire 	pll_clk1;
wire	pll_clk2;
wire	[1:0]key_pulse;
wire	vga_de;					//lcd data enable
reg		definiton;				// 分辨率切换寄存器

// 按键消抖模块
debounce #(
	.N          (2          ),
	.CNT_20MS   (19'h75601	)	//系统时钟24MHz,要延时20ms左右时间
)u_debounce
(
	.clk(clk_24m),
	.rst_n(rst_n),
	// key
	.key(key),
	.key_pulse(key_pulse)
); 

//sync global clock and reset signal 
PLL_OUT u_PLL_OUT(
	.refclk(clk_24m),
	.reset(~rst_n),
	.extlock(Clk_Lock),
	.clk0_out(pll_clk1),	// 108
	.clk1_out(pll_clk2)		// 64.8
);

wire [10:0]h_sync;
wire [10:0]h_back;
wire [10:0]h_disp;
wire [10:0]h_front;
wire [10:0]h_total;

wire [10:0]v_sync;
wire [10:0]v_back;
wire [10:0]v_disp;
wire [10:0]v_front;
wire [10:0]v_total;

// 根据分辨率寄存器选择相应的场信息和像素时钟
assign clk_vga 	= definiton ? pll_clk2 : pll_clk1;
assign h_sync 	= definiton ? 136 	: 112	;
assign h_back 	= definiton ? 160 	: 248	;
assign h_disp 	= definiton ? 1024 	: 1280	;
assign h_front 	= definiton ? 24 	: 48	;
assign h_total 	= definiton ? 1344 	: 1688	;
assign v_sync 	= definiton ? 6 	: 3		;
assign v_back 	= definiton ? 29 	: 38	;
assign v_disp 	= definiton ? 768 	: 1024	;
assign v_front 	= definiton ? 3 	: 1		;
assign v_total 	= definiton ? 806 	: 1066	;

// 根据按键key[0]的状态切换分辨率
always@(posedge clk_24m or negedge rst_n)begin
	if(!rst_n)begin 
		definiton <= 1'b0;
	end 
	else if(key_pulse[0])begin
		definiton <= definiton + 1'b1;
	end
	else 
		definiton <= definiton;
end 

//VGA driver timing
// 将选择好的行场信息传入模块
Driver u1_Driver(
// Input
	.clk			(clk_vga				),		
	.rst_n			(rst_n					),  
	.lcd_data		(lcd_data				),
// hs vs
	.h_sync(h_sync)		, 		// 行同步信号时间
	.h_back(h_back)		, 		// 行消隐后肩时间
	.h_disp(h_disp)		, 		// 行数据有效时间
	.h_front(h_front)	, 		// 行消隐前肩时间
	.h_total(h_total)	, 		// 行扫描总时间
			
	.v_sync(v_sync)		, 		// 列同步信号时间
	.v_back(v_back)		, 		// 列消隐后肩时间
	.v_disp(v_disp)		, 		// 列数据有效时间
	.v_front(v_front)	, 		// 列消隐前肩时间
	.v_total(v_total)  	,		// 列扫描总时间
// Output 	
	.lcd_dclk		(vga_clk				),		    	
	.lcd_hs			(vga_hs					),		
	.lcd_vs			(vga_vs					),
	.lcd_en			(vga_de					),		
	.lcd_rgb		({vga_r, vga_g ,vga_b}	),	
	.lcd_xpos		(lcd_xpos				),	
	.lcd_ypos		(lcd_ypos				)
);

//lcd data simulation
reg	[1:0]	display_cnt;	// 显示模式寄存器

// 扫描按键key[1],按键每次按下都切换输出模式
always@(posedge clk_24m or negedge rst_n)begin
	if(!rst_n)begin 
		display_cnt <= 2'd0;
	end 
	else if(key_pulse[1])begin 
		display_cnt <= display_cnt + 1'b1;
	end 
	else 
		display_cnt <= display_cnt;
end 

// 将显示模式寄存器传入模块
Display	u2_Display
(
// Input
	.clk			(clk_vga		),		
	.rst_n			(rst_n			), 	
	.lcd_xpos		(lcd_xpos		),	
	.lcd_ypos		(lcd_ypos		),
	.display_cnt	(display_cnt	),		// 显示模式寄存器
	
	.h_disp(h_disp),
	.v_disp(v_disp),
// Output 	
	.lcd_data		(lcd_data		)
);
endmodule

    其次是驱动模块(Driver.v):该模块与例程的差别是将原来的行场信息的常量参数改为了变量(因为此行场信息需要根据按键值进行切换),并由外部传入,模块内部再根据传入的行场信息产生相应的行场信号,用于驱动VGA显示。在改这个模块的时候编译器的替换功能省了不少功夫,代码如下:

`timescale 1ns/1ns
/* VGA参数配置表
************	clk		 	h_sync 		h_back 		h_disp 		h_front 	h_total 		v_sync 		v_back 		v_disp 		v_front 	v_total		*
640x480@60Hz	25.2MHz		96			48 			640 		16 			800 			2			33			480 		10			525		*
800x600@60Hz	40MHz		128			88 			800 		40 			1056			4			23			600 		1			628		*
1024x768@60Hz	65MHz		136			160 		1024 		24 			1344			6			29			768 		3			806		*
1280x720@60Hz	74.25MHz	40			220 		1280 		110			1650			5			20			720 		5			750		*
1280x1024@60Hz	108MHz		112			248 		1280 		48 			1688			3			38			1024		1			1066	*
1920x1080@60Hz	148.5MHz	44			148 		1920 		88 			2200			5			36			1080		4			1125	*
*/	
module Driver(
	input  wire			clk,			//VGA clock
	input  wire			rst_n,     		//sync reset
	input  wire	[23:0]	lcd_data,		//lcd data
	
	//input vs hs
	input [10:0]h_sync	, 		// 行同步信号时间
	input [10:0]h_back	, 		// 行消隐后肩时间
	input [10:0]h_disp	, 		// 行数据有效时间
	input [10:0]h_front	, 		// 行消隐前肩时间
	input [10:0]h_total	, 		// 行扫描总时间
			
	input [10:0]v_sync	, 		// 列同步信号时间
	input [10:0]v_back	, 		// 列消隐后肩时间
	input [10:0]v_disp	, 		// 列数据有效时间
	input [10:0]v_front	, 		// 列消隐前肩时间
	input [10:0]v_total , 		// 列扫描总时间
	
	//lcd interface
	output wire			lcd_dclk,   	//lcd pixel clock
	output wire			lcd_hs,	    	//lcd horizontal sync
	output wire			lcd_vs,	    	//lcd vertical sync
	output wire			lcd_en,			//lcd display enable
	output wire	[23:0]	lcd_rgb,		//lcd display data

	//user interface
	output wire	[11:0]	lcd_xpos,		//lcd horizontal coordinate
	output wire	[11:0]	lcd_ypos		//lcd vertical coordinate
);	
 
localparam	H_AHEAD = 	12'd1;

reg [11:0] hcnt; 
reg [11:0] vcnt;
wire lcd_request;

/*******************************************
		SYNC--BACK--DISP--FRONT
*******************************************/ 
//h_sync counter & generator
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		hcnt <= 12'd0;
	else
	begin
        if(hcnt < h_total - 1'b1)		//line over			
            hcnt <= hcnt + 1'b1;
        else
            hcnt <= 12'd0;
	end
end 

assign	lcd_hs = (hcnt <= h_sync - 1'b1) ? 1'b0 : 1'b1; // line over flag

//v_sync counter & generator
always@(posedge clk or negedge rst_n)
begin
	if (!rst_n)
		vcnt <= 12'b0;
	else if(hcnt == h_total - 1'b1)	//line over
		begin
		if(vcnt == v_total - 1'b1)		//frame over
			vcnt <= 12'd0;
		else
			vcnt <= vcnt + 1'b1;
		end
end

assign	lcd_vs = (vcnt <= v_sync - 1'b1) ? 1'b0 : 1'b1; // frame over flag

// LED clock
assign	lcd_dclk = ~clk;

// Control Display
assign	lcd_en		=	(hcnt >= h_sync + h_back  && hcnt < h_sync + h_back + h_disp) &&
						(vcnt >= v_sync + v_back  && vcnt < v_sync + v_back + v_disp) 
						? 1'b1 : 1'b0;                   // Display Enable Signal
						
assign	lcd_rgb 	= 	lcd_en ? lcd_data : 24'h000000;	

//ahead x clock
assign	lcd_request	=	(hcnt >= h_sync + h_back - H_AHEAD && hcnt < h_sync + h_back + h_disp - H_AHEAD) &&
						(vcnt >= v_sync + v_back && vcnt < v_sync + v_back + v_disp) 
						? 1'b1 : 1'b0;
//lcd xpos & ypos
assign	lcd_xpos	= 	lcd_request ? (hcnt - (h_sync + h_back - H_AHEAD)) : 12'd0;
assign	lcd_ypos	= 	lcd_request ? (vcnt - (v_sync + v_back)) : 12'd0;

endmodule

该模块在顶层模块的参数传递代码,根据definition选择行场信号,definition根据按键状态进行切换,最后将参数传入模块(wire型行场信息可以不用都设置为11位,以节省资源,这里直接同意设置成11位)。另外在编写这个模块时碰到一个问题,就是在按键按下瞬间显示器闪屏一次,分辨率并没有改变。后来发现问题是在判断条件上出问题了之前是直接判断key_pulse[0]状态为一时切换分辨率和时钟,key_pulse[0]为一的状态其实只有按键按下的一瞬间,之后马上变为零,所以显示器仅仅闪屏一下,分辨率还是变为原来的了。后面是增加definition,key_pulse[0]来一次,definition就自加一次,再通过definition的状态来切换分辨率和行场信息,最后完美解决问题

 

 

 

 

 

 

    最后是显示模块(Display.v):该模块内部使用case语句对输出模式进行选择,与原来例程不同的地方是传入了输出模式控制信号display_cnt,并将四种模式组合到case语句中。

`timescale 1ns/1ns

// Define colors RGB--8|8|8
`define RED		24'hFF0000 
`define GREEN	24'h00FF00 
`define BLUE  	24'h0000FF 
`define WHITE 	24'hFFFFFF 
`define BLACK 	24'h000000 
`define YELLOW	24'hFFFF00 
`define CYAN  	24'hFF00FF 
`define ROYAL 	24'h00FFFF 

module Display( 
	input  wire	 		clk,	
	input  wire			rst_n,	
	input  wire	[11:0]	lcd_xpos,	//lcd horizontal coordinate
	input  wire	[11:0]	lcd_ypos,	//lcd vertical coordinate
	input  wire [1:0]	display_cnt,
	
	input wire [10:0]		h_disp,
	input wire [10:0]		v_disp,
	
	output reg  [23:0]	lcd_data	//lcd data
);

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin 
		lcd_data <= 24'h0;
	end 
	else 
		case(display_cnt)
			2'd0:begin
					if	(lcd_ypos >= 0 && lcd_ypos < (v_disp/8)*1)
						lcd_data <= `RED;
					else if(lcd_ypos >= (v_disp/8)*1 && lcd_ypos < (v_disp/8)*2)
						lcd_data <= `GREEN;
					else if(lcd_ypos >= (v_disp/8)*2 && lcd_ypos < (v_disp/8)*3)
						lcd_data <= `BLUE;
					else if(lcd_ypos >= (v_disp/8)*3 && lcd_ypos < (v_disp/8)*4)
						lcd_data <= `WHITE;
					else if(lcd_ypos >= (v_disp/8)*4 && lcd_ypos < (v_disp/8)*5)
						lcd_data <= `BLACK;
					else if(lcd_ypos >= (v_disp/8)*5 && lcd_ypos < (v_disp/8)*6)
						lcd_data <= `YELLOW;
					else if(lcd_ypos >= (v_disp/8)*6 && lcd_ypos < (v_disp/8)*7)
						lcd_data <= `CYAN;
					else
						lcd_data <= `ROYAL;
				end
			2'd1:begin
					if	(lcd_xpos >= 0 && lcd_xpos < (h_disp/8)*1)
						lcd_data <= `RED;
					else if(lcd_xpos >= (h_disp/8)*1 && lcd_xpos < (h_disp/8)*2)
						lcd_data <= `GREEN;
					else if(lcd_xpos >= (h_disp/8)*2 && lcd_xpos < (h_disp/8)*3)
						lcd_data <= `BLUE;
					else if(lcd_xpos >= (h_disp/8)*3 && lcd_xpos < (h_disp/8)*4)
						lcd_data <= `WHITE;
					else if(lcd_xpos >= (h_disp/8)*4 && lcd_xpos < (h_disp/8)*5)
						lcd_data <= `BLACK;
					else if(lcd_xpos >= (h_disp/8)*5 && lcd_xpos < (h_disp/8)*6)
						lcd_data <= `YELLOW;
					else if(lcd_xpos >= (h_disp/8)*6 && lcd_xpos < (h_disp/8)*7)
						lcd_data <= `CYAN;
					else
						lcd_data <= `ROYAL;
				end
			2'd2:begin
					lcd_data <= lcd_xpos * lcd_ypos;
				end
			2'd3:begin
					if(lcd_ypos < v_disp/2)
						lcd_data <= {lcd_ypos[7:0], lcd_ypos[7:0], lcd_ypos[7:0]};
					else
						lcd_data <= {lcd_xpos[7:0], lcd_xpos[7:0], lcd_xpos[7:0]};
				end
			default:lcd_data <= lcd_data;
		endcase
end 

endmodule

该模块在顶层文件中对display_cnt寄存器根据按键状态进行切换,并传入显示模块中。

 

     接下来是实验效果(开始时让显示器显示了分辨率:1024*768,随后切换四种播放模式;第二种分辨率也进行了显示,分辨率为:1280*1024,随后也切换了四种播放模式;最后是按了复位键进行复位):

VGA_KEY_trl

    源码:

VGA_KEY_trl.rar (2.12 MB, 下载次数: 21)

最新回复

最后的按键控制VGA显示效果还行   详情 回复 发表于 2022-8-6 22:09
点赞 关注
 
 

回复
举报

6855

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

最后的按键控制VGA显示效果还行

点评

后面看看能不能多几个分辨率的控制,以及显示模式的切换,主要是想拿来当个信号源,以后当测试的信号源,这样测试就不用拔电脑主机的显示器来当输入源了  详情 回复 发表于 2022-8-7 16:12
 
 
 

回复

859

帖子

3

TA的资源

版主

板凳
 
Jacktang 发表于 2022-8-6 22:09 最后的按键控制VGA显示效果还行

后面看看能不能多几个分辨率的控制,以及显示模式的切换,主要是想拿来当个信号源,以后当测试的信号源,这样测试就不用拔电脑主机的显示器来当输入源了

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表