Since I hardly ever post anything to this website I've decided to submit a few projects in a slightly different format. At the moment every time I have a day free to do some electronics the thought of fully documenting the project and posting it on here puts me off!
Instead, from now on, I'm going to do a series of one day builds where I have a singe day (or less) to make a specific project. I'll use this website like a lab book, documenting my progress as I go along. The notes will be rough but there should be enough detail to follow what I've done. I hope someone finds them useful/interesting.
10:45 Research VGA interface and timing specifications
There are 5 main signals for VGA:
H sync and V sync tell the monitor where the horizontal and vertical lines begin and end.
See diagram below for details:
11:30 Work out timings for my monitor
The timings of VGA signals depends on the desired resolution and refresh rate.
My monitor has a native resolution of 1680 x 1050 resolution and supports a 60Hz refresh rate. VGA timings can be a bit awkward to calculate so it is often easier just to look them up online.
I used this site which has a large selection of common resolutions.
The horizontal width in pixels = "Front porch" width + "Sync pulse" width + "Back porch" width + monitor width. For my chosen resolution is 104 + 184 + 288 + 1680 = 2256.
The vertical width in pixels is = "Front porch" width + "Sync pulse" width + "Back porch" width + monitor width. For my chosen resolution is 1 + 3 + 33 + 1050 = 1087.
The h_sync line should be normally high, and then pulsed low for 184 pixels to trigger the h_sync.
The v_sync line should be normally low, and then pulsed high for 3 pixels to trigger the v_sync.
To get a refresh rate of 60Hz I need a pixel clock of 147.14MHz. This is calculated from:
h_width * v_width * refresh rate = 2256 * 1087 * 60 = 147136320Hz
12:00 Start writing the VHDL
I'm going to use my Altera DE1 development board as it has a VGA connector already on the board.
The DE1 has a 4 bit DAC for each colour channel. So there are only 16*16*16 possible colours. This should be plenty for simple games like Pong.
First I wrote a simple component in VHDL to count through each of the pixels in a line and then each of the lines in a frame. I also added some logic to generate the v sync and h sync pulses.
The DE1 development board has a 50MHz oscillator. To get the required pixel clock frequency of 147.14MHz I used a Phased Locked Loop (PLL). The FPGA on the DE1 has 4 PLLs built in.
The closest the PLL could get to 147.14MHz with a 50MHz reference was 147.22MHz which should be close enough. The refresh rate will just be slightly faster than 60Hz.
12:30 First test
I set the FPGA to output a red box covering the whole visible area of the screen.
The vertical alignment on the screen was perfect, but the horizontal was shifted to the right by about ~100 pixels making the right part of the image off the screen and the left of the screen black.
I did a few tests drawing small boxes on the screen at the limits of visibility. As initially thought the vertical alignment was fine, but the horizontal was shifted to the right.
12:45 Realised that I had mixed up the polarity of both the h_sync and v_sync pulses. Not sure why the vertical still seemed to work though. Fixed this and it worked!
Next I tested with a 10 pixel line going around the edge of the screen. This would show up any alignment issues.
Seems to display perfectly with none of the image overlapping the edge of the screen.
13:15 Back after lunch. Time to make Pong!
I uploaded the initial VGA driver code to github (GIST actually because it's only a small project) It can be found here.
I rearranged the VHDL VGA component to allow other modules to write to the screen. Otherwise everything would have to be done within the VGA driver component.
I decided to leave the 10 pixel boarder surrounding the screen as it makes it clear where the edge of the play area is.
I quickly made a process to draw the paddles to the screen. The DE1 only has 3 buttons, I would need 4 to allow 2 players to move the paddle up and down. Instead I used the switches. If the switch is in the up position it moves the paddle upwards and vice versa.
14:37 Things are moving on the screen!
Paddles are now moving based on the position of the switches. The paddles are also restricted from moving past the top and bottom of the screen.
I got the ball bouncing off the walls correctly. If it hits the top or bottom the vertical direction is reversed.
Currently if it hits the left or right the horizontal direction is reversed. This will be changed to reset the ball and increment the score. I've set the velocity as an integer in VHDL. This means the speed could be increased gradually as the game progressed (not yet implemented).
15:30 It works
Added the some very shoddy collision detection. Works by detecting when the FPGA is outputting both red and blue. Since only the paddles are red and only the ball is blue if both red and blue are outputted then a collision has occurred. When a paddle collision is detected the horizontal direction of the ball is reversed.
15:57 Playing pong against myself
Collision detection works surprisingly well. Will probably leave it like this for the moment. Still need to add scoring and implement a reset when someone looses. Pushed code changes to GitHub.
16:30 Finalising the code
Set the ball to reset when it hits the left or right side.
Added scoring from 0 to 9 for each player, after which the game stops.
Added 2 seven segment displays to show the score for each player.
Cleaned up code and added a few more comments.
I had a quick look at adding a PS/2 keyboard to the FPGA. It looked pretty easy, but then I realised that my keyboard is USB only. I pushed latest code to GitHub. All done for the day. I'll probably use the VGA driver in some future projects, maybe VGA Tetris...