As my first microcontroller project I decided to make a hand-held clone of the classic game Pong. Normally a graphical display would be required for this kind of project but I decided to take advantage of the custom character capabilities of the readily available 1602a LCD. The core of the project is the updated Basic Stamp from Coridium which based on the LPC11u37 ARM microcontroller, software for the stamp is written using an updated version of basic which is very similar to the first language I had learned, Qbasic.
The wiring diagram below shows how I connected the Basic Stamp to the LCD and three inputs needed to play the game. The pushbutton with the pullup resistor R1 is used to make selections and progress through the small menu system, R2 and R3 are the potentiometers that return a 0 to 3.3v signal used to control the paddles and change selections in the menu. The 10 digital outputs are used to interface with the LCD using it’s 8-bit parallel mode, R4 is a small trimpot used to set the contrast of the display. The contrast makes a big impact on how clearly the ball’s motion is visible but only needs to be set once. Finally I used a USB battery as the power source for the project, this takes care of charging the battery and providing a stable 5v supply for the microcontroller.
With the project wired on a breadboard I could start laying out my program, the flowchart below shows how the final version of the software turned out. After being powered on there are a few tasks that only need to be done once, like sending commands to the LCD to get it started and initializing all the global variables used in the game. After the PONG splash screen is shown the user can press the button and is presented with a choice between One and Two Player Modes, to get this input I look at the current value of the analog input for the right hand potentiometer. If the analog input is greater than half of 65472, the maximum input value, then One Player Mode will be selected when the button is pressed, if the analog input is lower than half the maximum value then Two Player Mode is selected.
For a single player the game now begins but for two players there is an additional step, picking the maximum score for the round. I wanted a range from 10 to 50 using increments of 5 and came up with the following line.
maxScore = ((((65472 - AD(1)) * 9) / 65480) * 5) + 10
"65472 – AD(1)" was used to reverse the direction of the input to make clockwise motion of the potentiometer increase the score, the result will still be a number from 0 to 65472. Multiplying the 0 to 65472 value by 9 and then taking the quotient of that value and 65480 (slightly larger than the maximum) results in integers from 0 to 8. Multiplying that integer value by 5 and adding it to 10 turns the 0 to 65472 input from the potentiometer into a 10 to 50 output with evenly spaced increments of 5.
Once the game has started in either One or Two Player Mode the logic is very similar. On every cycle of the game loop the position of the paddle(s) is updated by checking the value of the analog inputs and scaling them to a value from 0 to 16 (the usable height of my display) using the same method used to pick the max score. Next the ball’s position is updated by adding COS(BallDirection) * BallSpeed to the current X coordinate and SIN(BallDirection) * BallSpeed to the current Y coordinate, by storing the ball’s coordinates, direction and speed as floating point numbers I was able to get much smoother movement of the ball than if they were all integers. Instead of cogging from one pixel to the next the ball moves smoothly and it’s position is rounded to an integer only when being displayed on screen.
After being sent to the display the game checks if the ball should bounce off of any of the walls or paddles and adjusts the direction accordingly. In One Player Mode bounces off the far wall increase the players score, if the player misses the ball with their paddle they loose a ball, in Two Player Mode getting the ball past the other player’s paddle increases the score up to the chosen maximum. If the ball is to be served on the next loop because a player missed a bounce then then a variable called gameState is set to indicate that a serve is needed and towards which player the serve will go. If the gameState variable is left untouched then the game continues as normal by updating the paddle position, ball position and then drawing it on the LCD. If the button is pushed or one of the ending conditions is met the game loop ends and the player(s) are returned to the splash screen by the main loop of the program.
The majority of the program is handled using simple math, loops and if statements but interfacing with the LCD was a big challenge for me. The LCD takes information as 8bits on 8 DB pins as well as a Register Selection pin which determines of the 8bits of data is a command or data to be written to the LCD’s memory, finally there is an enable pin which is toggled to indicate to the LCD that the current input values are valid. In order to quickly set the values of the 8 DB pins I had to learn about Bit Masking, this is where you perform a logical AND function on your data byte and a mask. For example, if I want to know the state of the x bit in this byte 0000x000 I can perform a logical AND between it and 0000100. Because all of the undesired bits in the bitmask 00001000 are zero they will always result in a zero, the value of x will however be in the result because x AND 1 = x. By using a different bit mask to check each bit of the byte I want to send I could determine which DB pins should be on and which should be off.
Where B is the byte of data I want to send then the value of each DB pin could be determined in the following way. When turning an output pin on or off in Arm Basic applying any non-zero value will turn the pin on so it doesn’t matter that the pin I use for DB7 will be told “OUT(DB 7 pin) = 10000000,” the pin will always be on when that bit is on.
DB7 = B AND %10000000
DB6 = B AND %01000000
DB5 = B AND 100000
DB4 = B AND 010000
DB3 = B AND 001000
DB2 = B AND 000100
DB1 = B AND 000010
DB0 = B AND 000001
The other big hurtle was to take the xy data of the ball and paddle positions and translate them into something that was compatible with how the LCD works. Being an alphanumeric display it is only made to show characters from it’s ROM and 8 custom characters that can be programmed by the user. These 8 custom characters are 5 pixels wide and 8 pixels tall, by arranging them in a 4 wide by 2 tall grid it is possible to create a 20x16 pixel graphical display with room for score keeping (and debugging) on either side.
Each row of a character has an address (the CGRAM address) and shows the state of the 5 lowest bits of the byte stored at that address. So my 20x16 area is split up into 64 addresses with each address containing 5 pixels each.
By mapping out the locations of the addresses I was able to make a pair of functions that would return the CGRAM address and the value of the byte that should be stored in that address for any given x and y pixel position, I could then write this byte to the appropriate CGRAM address and display the playing field.
For a more detailed explanation of the functions I used you can check out a video I did on the subject linked here: Bit Mapping on the 1602a.
Finally after about a month of development and testing I was ready to box up my portable Pong project in a suitably retro looking enclosure and call it complete.