Our project is creating a miniature golf game, complete with a putter and golf ball to simulate a real putting stroke.
We chose to construct a mini golf game because not only is it interesting, but it has a large balance of computer programming as well as hardware design. Looking from the highest level, the project can be divided in two: the programming code, and the remaining hardware peripherals. The project uses 1 MCU on the STK 500 development board, which is programmed with our entire code. This code draws the entire course, and controls the resulting putt. The direction and speed of the put are controlled by two outside inputs: the putter and the ball. These are all integrated to complete our miniature golf game.
RATIONALE OF PROJECT:
We began selecting this project by identifying the need for a fun and entertaining final project whose construction required a nice balance between computer programming and hardware design skills (our group, 3 members combined, would result in 2 EE majors, 1EE minor, 1CS major, and 1 CS minor). The general idea resulting from this would be a computer game, with additional input and output devices that we used in the game for real-time user interaction.
After tossing standard video game ideas away, which would interface with only a joystick, we remembered how fun it was to play miniature golf as a kid! Actually, one of us played it over Spring Break. Each of us being a fan of the game, making a miniature golf game provided the nice design balance, as well as entertainment.
The project can easily be divided into two parts: the Atmel programming code, and the hardware design. The fundamentals behind the miniature golf game should be obvious, but our game is interesting in that we believed we could use an actual putter and ball to use as the user input for a stroke in the game. Though for sure miniature golf games have been created on electronic hand held games, it is likely that none use a real putter to determine shot velocity.
GENERAL LAYOUT STRUCTURE:
Our project consists of the game code, the putter and golf ball combination, and their interaction. In general, the user boots us the program to see a title screen, hitting any button to proceed to the 1st hole of this 9 hole course, and a scoring grid that keeps track of the total strokes. He uses only the putter to control the game, including shot direction and power. This force is gauged by an accelerometer chip that is attached to the ball. The golfer repeats these steps until the hole, and then eventually the course, is finished.
We followed the RS-170 video standard. This is the standard used in North American and Japanese black and white television. This requires 30 frames per second, and 525 lines per frame. We modified the number of lines per frame to make the calculations faster, and thus easier to finish all the calculations during the short period of time before we had to be ready to send out the next frame. We first chose to only use the odd lines, reducing the number of lines per frame to 262. Then, we only used lines 30-230, and of these, we doubled up the lines so we only had 100 lines (this reduced flicker, and also reduced the number of calculations necessary per frame.
When we want to create a line (to be displayed on the television), we have to start with a horizontal sync pulse of 5 us at 0V. Then, we have to create a “Back Porch” portion of the signal, which is a 5.9us signal at 0.3 V. Then, we have 51.5 us to send out our desired picture. This is done by black being 0.3 V and white being 1.0 V, and the voltages in between 0.3 and 1.0 correspond with varying grays. The position in time of the voltage corresponds to the position of that pixel on the screen. Once we finish sending out the signal for the picture, we have to have a 1.4 us “Front Porch” section of the signal, before starting on the next line.
Once we finish sending out the signals for all the lines in the frame, we have to send a “New Frame” signal. This consists of a series of three 63.55 us vertical sync signals. This directs the TV to return to the top of the screen, and start drawing from there.
USE OF OTHERS’ MATERIAL:
We utilized code from a previous 476 lab, where we had a TV display. We asked permission of our instructor, Professor Bruce Land, before using his code. It is available from http://instruct1.cit.cornell.edu/courses/ee476/ .
The program is run from a while loop in the main function that run per each TV screen, or 63.35 ms. In between these loop iterations, the processor is busy managing the vertical and horizontal synchs, along with outputting the pixel values to the screen. The bulk of the program consists of a succession of stages that run once for each hole, 9 times in total: intermission, aiming, hitting, and running the ball.
The intermission stage, lasting 10 screen cycles, is used between holes to allow time for the transition between them. In the first two cycles, the previous hole is cleared of the screen. In the final two cycles, the next hole is loaded onto the screen, and any variables necessary to start the aiming stage are set. To aid in the drawing and erasing of holes, we have one function for each hole that specifies where the walls and holes are supposed to be located. Based on a parameter given to the function, it can either draw it or erase it from the screen; in this way, we save space by using the same function for two different purposes. To pick which of the nine of these we want to use, we have a switch statement that chooses based on the hole number we are currently on.
The aiming stage, as the name implies, is the stage where we aim the direction of the shot. To represent the putter, we have a line on the screen located at the ball that extends out for a few pixels, so that the user has an idea of where his or her shot will be going. Each cycle, we check if either the clockwise or counterclockwise button is being pressed on the putter. If so, we update the angle of the putter by one degree, erase its previous drawing on the screen, and draw it again at a new angle. We also use the angle to set the dx and dy variables, which represent how much the ball will travel in the x and y directions, based on the current angle. This continues until the done aiming button is pressed; at this point, we set the variables necessary to start the hitting stage, and erase the putter from the screen.
In the hitting stage, we use the accelerometer in the ball to measure how fast the ball should travel. To do this, we have the accelerometer output hooked up to the ADC input. Once the user presses the hit button, we spend the next few seconds measuring the value coming from the ADC every screen cycle; this consists of reading the value in the ADCH register and then setting the ADC to take another measurement. While we are measuring the ADC, we keep track of the largest value we have received so far; the reasoning here is that the greatest acceleration comes when the ball is hit, so this is the value we want to look out for. Once these few seconds are up, we take this largest value and scale it accordingly to a resulting value; this value will represent for how many screen cycles the ensuing hit will travel. Lastly, once the time runs out, we set all the variables necessary so that we can start moving the ball.
The last stage, the running stage, is where most of the code comes into play. First, we erase the previous drawing of the ball (just a dot) on the screen. We then use the in_hole function to check if the ball is in the hole yet; this involves finding the x and y differences between the ball and the hole, and checking if this value means that we are in the hole. If not, we then check if the ball is going to bounce off of any walls and go through any other obstacles. To do this, we have a function for each hole that uses the locations of the holes walls to see if the ball is going to hit them and then change its directions accordingly. For multiple-tiered holes, it also checks if it has entered any of the tubes for these holes, and updates its position and velocity accordingly. After this function, it then uses the current speed and direction of the ball to update its position, and decreases its speed by one. Lastly, it draws the ball onto screen in its new location. Note that if the ball was found to be in the hole, it does not do anything after this was checked; instead it records the number of putts the user took on the hole, and then sets the necessary variables to that the program can enter the next intermission stage, and the user proceeds onto the next hole.
Needless to say, there is code both before and after this loop. Initially, the program must set all of the ports so that it can get the necessary inputs. It had to set the input ports so it can receive the button presses and ADC, and the output ports so it can draw on the TV screen. It also has to set the ADC unit so that it can receive input from the accelerometer at the needed intervals. After that, it is necessary to display the title of the game on the screen. It stays in this state until any of the buttons are pressed, at which points it transitions to the intermission stage before the first hole. Lastly, it must set the timer so that can draw the lines to the screen with the correct necessary timing. After the 9 holes have been done, it then outputs GAME OVER to screen, and idles until it is reset.
Two parts of this code were particularly tricky. One was getting the putter to rotate correctly when it was being aimed. Given the different data types we used (chars, ints, doubles), it took a little while before we had to right casting from one type to another. Until then, the putter would either skip randomly around its rotation or draw lines of differing shapes and sizes that had nothing to do with where the putter was supposed to be aiming.
Harder still was the logic used to check if the ball had run into any walls. Originally, we waited until the ball actually passed through the wall before we changed its direction, using the previous position of the ball to figure which walls it had hit. This worked well into we had blocks in the middle of the course; if the ball approached from the corner, it would get stuck in the block because it would keep changing direction but would be unable to leave the box. To fix this, we changed the logic so that we checked whether or not the next location of the ball, if it continued on its current course, would be in the wall; if so, we changed the direction of the ball before this happened, and this helped to ensure that the ball never got stuck in an obstacle.
All of our code was original except the parts used to draw onto the TV screen. This code was lifted from the code given to us for lab 5. This includes the timer logic necessary to coordinate the synch pulses, along with the code for outputting the screen values to the TV. Additionally, this also includes the bitmaps provided to draw dots, lines, and letters to the TV screen. (The exception is the function used to draw the circular hole on the screen, which we created.)
The hardware consists or the putter, golf ball, and the video signal generation circuit.
On the putter are 4 active low buttons: counter-clockwise direction control, clockwise direction control, set aim, and Go. The direction controls rotate the direction of the putter on the screen, thus affecting shot direction. The set aim button is pressed after the golfer is happy with his putter alignment. Once this is set, it cannot be changed. The last button, Go, is hit to notify the CPU to expect a put within 3 seconds. The golfer then puts the ball according to the alignment marker, and within 3 seconds, the ball on the screen moves with a the direction set, and the speed relative to the force with which it was struck.
A 5-wire bus travels from the button to the STK board, connecting at ports GND, A7, A6, A5 and A4. A basic schematic of the pushbuttons follows below. When a button is pushed, the circuit is completed, and the pull-up port is pulled down to 0V, which is detected by the program.
The brains behind the input power to the program lies in, or rather under the ball. Noting that we needed some way to input a position/velocity into the program, after considering several options, we chose to use an accelerometer. An accelerometer, particularly the ADXL250JQC, outputs a raw voltage, based upon the acceleration it undergoes. By reading this voltage into the ADC converter, we can then simulate the ball being struck at that relative power. This particular chip, from Analog Devices, steps out voltage at 37mV/g, and is rated to +/- 50g. Thus, the max voltage that the chip will output is 1.85V. However, we tested this to be actually about 2.2V. This chip can output two separate axis (x and y), but for our purposes, we only need the x-axis. Without any acceleration, the chip outputs 2V. Thus, 2V, added to 1.85V yields 3.85V. Thus, we set our Aref to 5V to make the 8-bit comparison from port A0, which is the A to D input port.
However, during testing, a medium strike of the golf ball would overdrive the ADC converter. We had to find a way to decrease to overall voltage being sent to the ADC, without sacrificing resolution too much. The simplest way, which is often the best, was the place the ball off-axis. Thus, when the ball is stuck, the acceleration will not be purely in the x-direction, but split into the x and y directions. After testing stroke powers while watching the voltage output on a time-scaled oscilloscope, we found the optimal axis offset to be 45 degrees.
The chip is powered by a voltage between 4 and 6V, so we conveniently were able to use the processors 5V. As noted above, we had to set the stroke direction 45 degrees off-axis. In order to make our game accurate, we had to be able to hit the ball 45 degrees off axis, repeatable. However, the golf ball keeps rolling around. Thus, we devised a way to keep the ball steady: by using a Gatorade bottle cap. This cap was cut through slightly to allow part of the accelerometer IC to poke through. Then, insulation was place inside the cap, and the ball placed on top of that. The whole unit was then taped together tightly to prevent slippage. A white arrow was painted with whit-out over the black tape, to specify the direction the ball should be struck. It is our hope that with this flat cap, and a secured ball, the user will be able to strike the ball accurately every time.
Video Signal Generation
In order to use the TV, we are using a standard NTSC video signal that is not interlaced. This standard is described later in the standards section, and is identical to what we used earlier in the class for the TV oscilloscope lab. The only hardware needed for this was to set the correct voltage levels for output according to synch and voltage output. The following circuit performed this task:
As you can see, the hardware combined is not very complex, but simplicity is often why a circuit works! Besides the putter, ball, and video generation circuit, the only other hardware feature we used was the STK 500 development board, provided for us.
Atmel Mega32 Microcontrolled
STK500 Development Board
Input device (keypad/NES contoller)
Putter (provided by Matt)
For more detail: MiniGolf video game with putter