Pull to refresh

Testing a Monitor using DE10-Lite

Reading time5 min
Views2.6K

Introduction

In order not to buy a defective monitor, it is highly recommended to check it before buying. DE10-Lite is great for this because of a compact size and the presence of a VGA connector.

Hardware and software

  • DE10-Lite board (based on Altera's MAX 10 FPGA)

  • Quartus Prime Lite Edition

  • Verilog HDL

Some words about VGA and clock division

VGA (Video Graphics Array) is an analogue interface used to display visual data on computer monitors. Till a few years back, it was the most used display interface. It is now being slowly replaced by HDMI.

To display an image using VGA, we must follow its specification: timings and clock rate. In this paragraph I will consider only the clock rate. Information about the timing is provided in the next block.

VGA needs 25.175 MHz clock rate, but the DE10-Lite can only provide 10 MHz or 50 MHz clock. Therefore, we must somehow obtain 25.175 MHz by dividing or multiplying the existing clocks. In this project, I will divide 50 MHz clock. 

To obtain the required clock, I used Altera Phase-Locked Loop (Altera PLL) from the IP catalogue in Quartus Prime.

Displaying an image on the screen through the VGA protocol

In this project I will use 640x480 at 60 Hz. 
Therefore, we have to follow the following timings:

Code of the VGA controller
module VGA(
input clock25MHz,
output vsync,
output hsync,
output canDisplayImage,
output reg[9:0] x,
output reg[9:0] y
);
reg [9:0] xCounter;
reg [9:0] yCounter;
 always @(posedge clock25MHz)  // horizontal counter
	begin 
		if (xCounter < 799)
			xCounter <= xCounter + 1;
		else
			xCounter <= 0;              
	end  
	
	always @ (posedge clock25MHz)  // vertical counter
		begin 
			if (xCounter == 799)  // only counts up 1 count after horizontal finishes 800 counts
				begin
					if (yCounter < 525)  // vertical counter (including off-screen vertical 45 pixels) total of 525 pixels
						yCounter <= yCounter + 1;
					else
						yCounter <= 0;              
				end  
		end
			
	always @ (posedge clock25MHz)
	begin	
		if(xCounter>=144 && xCounter<784) x=xCounter-144;
		else x = 0;
		if(yCounter>=35 && yCounter < 515) y = yCounter-35;
		else y = 0;
	end

	assign hsync = (xCounter >= 0 && xCounter < 96) ? 1:0;  // hsync high for 96 counts                                                 
	assign vsync = (yCounter >= 0 && yCounter < 2) ? 1:0;   // vsync high for 2 counts
	assign canDisplayImage = ((xCounter > 144) && (xCounter <= 783))
     && ((yCounter > 35) && (yCounter <= 514));

endmodule

Now, when the board can display any image on a screen, it's time to draw something.

Using x and y, we can control each pixel. For example, the part of the code that draws a white 100x100 rectangle:

Drawing a 100x100 square
module tvPattern(
input [9:0] x,
input [9:0] y,
output reg [3:0] red,
output reg [3:0] green,
output reg [3:0] blue);

always @(x||y)
begin
if(x>=0 && x<100 && y>=0 && y<100)) 
	begin
		red <= 4'hF;
		green <= 4'hF;
		blue <= 4'hF;
	end
end



endmodule

Animated test

I decided to add an animated test (e.g. bouncing square).
The variables for squareX and squareY are created. 
Then, in sequential logic block always @(posedge clock25MHz), I am checking for collisions with screen borders and if collision - change the direction of the square.

Moving square
module bouncingSquare(
input clock25MHz,
input [9:0] x,
input [9:0] y,
output [3:0] red,
output [3:0] green,
output [3:0] blue
);
	localparam SQUARE_SIZE = 20;

	// Position of a square
	reg [9:0] squareX =100;
	reg [9:0] squareY = 100;
	
	// Vertical and horizontal speeds
	// 1 - positive direction, 0 - negative direction
	reg vSpeed = 1;
	reg hSpeed = 1;
	
	// Checking for collisions
	wire topCollision = (squareY-SQUARE_SIZE==0);
    wire bottomCollision = (squareY+SQUARE_SIZE==479);
	wire leftCollision = (squareX-SQUARE_SIZE ==0);
	wire rightCollision = (squareX+SQUARE_SIZE == 639);
	

always @(posedge clock25MHz)
begin
	if(topCollision) vSpeed<=1;
	if(bottomCollision) vSpeed<=0;
	if(leftCollision) hSpeed<=1;
	if(rightCollision) hSpeed<=0;
	counter <= counter + 1;
	squareX <= squareX + (hSpeed==1? 1 : -1);
	squareY <= squareY + (vSpeed==1? 1 : -1);
end

// Drawing a square
wire square = (squareX-SQUARE_SIZE<=x && squareX+SQUARE_SIZE>=x) && (squareY-SQUARE_SIZE<=y && squareY+SQUARE_SIZE>=y);

assign red = (square) ? 4'hF: 0;
assign green = (square) ? 4'hF: 0;
assign blue = (square) ? 4'hF: 0;
endmodule

The code works fine, but the square moves too fast. Thus, we must slow it down somehow. To do this, I created another reg and implemented in every clock's rising edge. Then, when the value of this reg becomes equal to some constant (SPEED_CONSTANT), I am changing the values of the squareX and squareY.

Slow moving square
module bouncingSquare(
input clock25MHz,
input [9:0] x,
input [9:0] y,
output [3:0] red,
output [3:0] green,
output [3:0] blue
);
	localparam SPEED_CONSTANT = 100000;
	localparam SQUARE_SIZE = 20;

	// Position of a square
	reg [9:0] squareX =100;
	reg [9:0] squareY = 100;
	
	// Counter for slowing down the animation
	reg [30:0] counter;
	
	// Vertical and horizontal speeds
	// 1 - positive direction, 0 - negative direction
	reg vSpeed = 1;
	reg hSpeed = 1;
	
	// Checking for collisions
	wire topCollision = (squareY-SQUARE_SIZE==0);
   wire bottomCollision = (squareY+SQUARE_SIZE==479);
	wire leftCollision = (squareX-SQUARE_SIZE ==0);
	wire rightCollision = (squareX+SQUARE_SIZE == 639);
	

always @(posedge clock25MHz)
begin
	if(topCollision) vSpeed<=1;
	if(bottomCollision) vSpeed<=0;
	if(leftCollision) hSpeed<=1;
	if(rightCollision) hSpeed<=0;
	counter <= counter + 1;
	if (counter == SPEED_CONSTANT)
	begin
		squareX <= squareX + (hSpeed==1? 1 : -1);
		squareY <= squareY + (vSpeed==1? 1 : -1);
	end
	if(counter>SPEED_CONSTANT) counter<=0;
end

// Drawing a square
wire square = (squareX-SQUARE_SIZE<=x && squareX+SQUARE_SIZE>=x) && (squareY-SQUARE_SIZE<=y && squareY+SQUARE_SIZE>=y);

assign red = (square) ? 4'hF: 0;
assign green = (square) ? 4'hF: 0;
assign blue = (square) ? 4'hF: 0;
endmodule

The video can be found here: link

Switching between different test modes

I used both buttons and switches to display different tests. 

Everything worked correctly with switches, however there was a problem with buttons - the tests either changed randomly either didn't change at all. This problem is called Button Bouncing.
Button Bounce happens when you push a mechanical button. When a button is being pushed, it tends to literally bounce upon the metal contact, which connects the circuit. 

This effect is shown in the following picture:

To overcome this problem, I used the debouncing technique. You can read more information about it here.

Conclusion

I have described the key stages of creating a monitor tester using an FPGA board and Verilog HDL. Areas of the project's application: business (for example, in a stand for checking monitors in electronics store) and personal use.

This work has been implemented in the scope of «Computer Architecture» course in the Innopolis University

Some examples

Images

References

  1. Project on the GitHub

  2. Button debouncing

  3. VGA explained

  4. Video example


Tags:
Hubs:
+3
Comments1

Articles