2321 views|2 replies

820

Posts

2

Resources
The OP
 

09. Anlu SparkRoad domestic FPGA evaluation [practical] key control VGA display [Copy link]

 This post was last edited by 1nnocent on 2022-7-31 10:39

Now let's adjust the evaluation plan and add a practical section on how to control the VGA display with one button.

This experiment realizes the key control of VGA display, SW18 controls the output mode (four display modes of VGA routine), and SW20 controls the resolution of the display (1280*1024 and 1024*768 are switched). I originally wanted to achieve four output resolutions, but PLL is not omnipotent. The output frequency of some channels is not so accurate. There are certain requirements for the pixel clock error of various resolutions: the frequency deviation does not exceed ±0.5%. This can be viewed in " VESA and Computer Display Monitor Timing (DMT) Industry Standards and Guidelines ", which is in the eighth article of the evaluation. A total of five pixel clocks were generated, and only two met the requirements in the end, so the resolution switching function can only achieve two resolutions for the time being. I will look at how to add more resolution switching types later (the subsequent work is just copy and paste, and the implementation method is basically the same).

Next, let's explain the implementation code:

First is the top-level module: 1. The input and output ports only need to add a two-bit key input interface based on the original VGA display routine of the development board, and the rest remain unchanged; 2. Of course, the key de-bouncing module of the routine needs to be added, and the display mode and resolution need to be switched according to the state of the key later; 3. The PLL_OUT module generates two pixel clocks, one outputs 108MHz and the other outputs 65MHz (the actual output is only 64.8MHz, which meets the requirement of ±0.5%). The two pixel clock frequencies correspond to resolutions of 1280*1024 and 1024*768 respectively; 4. Finally, the corresponding display status is output according to the function. Top-level module code (the tab key of the posted code is gone, and the code becomes left-aligned. It doesn’t look very beautiful. Is it a problem with my settings? There seems to be no setting when inserting the code):

`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

The second is the driver module (Driver.v): The difference between this module and the routine is that the constant parameters of the original line field information are changed to variables (because this line field information needs to be switched according to the key value), and are passed in from the outside. The module generates the corresponding line field signal based on the passed in line field information to drive the VGA display. When modifying this module, the replacement function of the compiler saves a lot of effort. The code is as follows:

`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

This module passes the parameter code of the top-level module, selects the row and field signal according to the definition, switches the definition according to the key status, and finally passes the parameters to the module (the wire type row and field information does not need to be set to 11 bits to save resources, and it is directly agreed to set it to 11 bits here). In addition, a problem was encountered when writing this module, that is, the display flashed once when the key was pressed, and the resolution did not change. Later, it was discovered that the problem was in the judgment condition. Before, the resolution and clock were switched when the key_pulse[0] state was directly judged to be one. The state of key_pulse[0] being one actually only lasted for the moment when the key was pressed, and then it immediately became zero, so the display only flashed, and the resolution remained the same. Later, the definition was added. When key_pulse[0] came once, the definition was added once, and then the resolution and row and field information were switched according to the definition status, and finally the problem was solved perfectly :

Finally, there is the display module (Display.v): This module uses the case statement to select the output mode. The difference from the original routine is that the output mode control signal display_cnt is passed in, and the four modes are combined into the case statement.

`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

In the top-level file, this module switches the display_cnt register according to the key status and passes it to the display module.

The following is the experimental result (the monitor initially displayed a resolution of 1024*768, then switched to four playback modes; the second resolution was also displayed, with a resolution of 1280*1024, then switched to four playback modes; finally, the reset button was pressed to reset):

VGA_KEY_trl

Source code:

VGA_KEY_trl.rar (2.12 MB, downloads: 20)

This post is from Domestic Chip Exchange

Latest reply

The last key controls the VGA display and the effect is OK   Details Published on 2022-8-6 22:09
 
 

6555

Posts

0

Resources
2
 

The last key controls the VGA display and the effect is OK

This post is from Domestic Chip Exchange

Comments

I will see if there are more resolution controls and display mode switches later. I mainly want to use it as a signal source and a test signal source in the future. This way, I don’t have to unplug the computer host monitor as an input source for testing.  Details Published on 2022-8-7 16:12
 
 
 

820

Posts

2

Resources
3
 
Jacktang posted on 2022-8-6 22:09 The last key control VGA display effect is OK

I will see if there are more resolution controls and display mode switches later. I mainly want to use it as a signal source and a test signal source in the future. This way, I don’t have to unplug the computer host monitor as an input source for testing.

This post is from Domestic Chip Exchange
 
 
 

Guess Your Favourite
Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list