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 hole’s 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 (char’s, int’s, double’s), 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 processor’s 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.