CSCI 381: Embedded Systems Design

LED Kart 648

by: Adam Chevallier, April Fleck, Jerad DeVries

Our project was based on an existing game called "The Great Race" which can be seen here. The object of this arcade-style game is to drive your kart through a maze of obstacles without crashing. To begin, you first choose a track to play using the Right button - we provide 4 different track options. Press the Left button to load the track and start. When the game starts, your LED kart is represented by the red LED at the bottom of the screen, and the track (in green) will scroll towards your kart. Navigate around the obstacles (and don't hit the wall!) by using the Right and Left buttons. It sounds easy, but the game speeds up as you play... survive long enough and you win!

We hope to provide enough detail on this site so you can build the game yourself. Our source code, and other downloadable files, are available at the bottom of the page. Click on any of the thumbnail images for a larger view. Thanks for your interest.

Hardware

Our version of the game utilizes some different components than the original, but the fundamental parts are the same. The brain of LED Kart 648 is an 18-pin microprocessor chip called the PIC 16F648A microcontroller. For the display screen, we use an 8x8 matrix of bi-color LEDs, part LC2088AURYG1C, which is approximately 2.3" square and has both green and red lights in common cathode orientation. We power the game from a standard (US) wall outlet using an adaptor and power jack. To get the desired 5V current to the PIC, we use voltage regulator MC7805BDTRKG. Other components include a CBT3251 decoder, two CBT3257 multiplexors, four FDC6305 MOSFET transistor pairs (8 transistors total), two tactile switches (buttons), and resistors.

In order to light up an individual LED on the matrix, the LED must be passed a logical 1 to signify its column being lit, and a 0 ground for its row in the grid. For our design, we opted to use the two 3257 multiplexors to ensure that each LED is powered individually - that is, each LED is passed the maximum amount of power from the PIC (25 mAh). The original design was not set up this way, and as a result, when many LEDs in a single row were lit at once, they lost intensity because all LEDs in the row would be sharing the power. We built a prototype from the original board design before finalizing our own, and in the photograph below you can see the power drain effect in the bottom two rows of the the displayed '1' where many LEDs are lit - they are dimmer than the rest. Also of importance is the RA3 pin on PORTA of the PIC, which determines whether an LED will be displayed in green or in red. This is why RA3 is connected to both 3257 multiplexors. Passing a 1 to RA3 switches the color to green by activating a set of four pins in each 3257 corresponding to the green LEDs of the matrix only, and passing a 0 to RA3 switches the color to red by activating those pins corresponding to only the red LEDs.

To draw a game track, bytes of data representing each line of the track are passed successively from the PIC to the 3257 multiplexors (one bit per pin), and then on to the LED matrix through whichever pins are activated by the RA3. The 1s in each byte represent LEDs we want on, and 0s are those we want off. The other requirement for an LED to be lit is that it be grounded. We signify which row of the LED matrix is grounded (only one row at a time may be grounded) by passing a 3-bit value from the PIC to the 3251 decoder, designating the row which receives the 0. All other rows will receive a 1, and are therefore not grounded. This setup works out nicely since we can represent exactly 8 values with 3 bits, and we have exactly 8 rows in our matrix. The values are passed from the decoder to the transistor pairs, which are connected to 8 pins of the LED matrix. In our design, these transistor pairs are important, because they sink the extra current passed when multiple LEDs are lit in one row. The decoder alone could not handle the sum of their power, so the transistors are necessary to ground our LEDs row by row.

So, with bytes of data passed to the columns of the matrix, and the rows grounded one at a time, all 64 LEDs can be lit up in sequence to create the patterns which make up our tracks. We decided on this final hardware configuration and went to work on a PCB layout incorporating our changes, using Eagle Layout Editor software. It was a challenge learning to use the software and playing with the layout to make all the connections in as little a footprint as possible, but in the end we had a nice compact design which can be seen below. The board is designed to fit into Hammond 1553 series plastic enclosure.

Schematic*PCB LayoutPrototype Assembled PCB

*Note: there should be pulling down 10K resistors at the gates of all our MOSFET transistors, but they were left off the schematic.

Software

The code for this project is written in assembly, using the instruction set for the PIC 16F627A/628A/648A microcontrollers.

The program was an improvement on the original code, which can be found at the link on the top of this page. The program starts by initializing the speed of the car and by setting the course to 1. Then the program goes into the select_track routine. This routine allows the user to select a course. The right button is used to change the course, while the left button is used to select the course. Whenever the left button is pressed, bit 7 in PORTA is cleared. Bit 6 in PORTA is cleared when the right button is pressed. Whenever a button is pressed it has to be debounced. This means that we must wait some time before testing it again. Without debouncing, the program would get a lot of signals whenever a button is pressed. We must allow the signal to stabilize before checking it again.

When the left button is pressed from inside the select_track routine the program goes into the main loop (label begin in the code). The main loop consists of 2 procedure calls. First it calls display, then it calls fill_vram. The display routine grabs our screen data, which is stored in the variables vram_1 to vram_8, and displays it on the screen. The stop_repeat bit is used to determine when to check the buttons and update the car position. The car collision is tested by anding the car data with vram_8 and testing if the result is zero (vram_8 is the data for the line of lights right above the car). If there is a collision, then we display a red flash on the entire screen and then display what level the user got to in the course. We then wait for a button to be pressed. When a button is pressed, we go back to the setup procedure and start all over. If there was no collision, then we decrement repeat_frame and loop back to the top of the procedure, returning when repeat_frame becomes zero. Next the fill_vram procedure is called from our main loop. This procedure grabs the next line of track data and shifts the current lines of track data down one line to scroll the track. It then returns back to the main loop and the program proceeds.

Here is a step-by-step flowchart showing the program logic. You can print the 3 pages and arrange them into one large chart for ease of viewing.

Flowchart p.1 Flowchart p.2 Flowchart p.3

A Few Words on Development

As all three of our group members were new to this type of system design, and also had no experience with assembly programming, it became clear that by the time we had finished with the hardware, we would not have time to design the software from scratch. We decided it would be better to work off of Brad's original code and find ways to improve on and add to its functionality in order to have a working game by semester's end.

First, we thought of creating multiple 'courses' of varying difficulty (different from the 'levels' which denote the game scroll speed). We designed four different courses of increasing difficulty: easy, medium, hard, and insane. This would be the final major implementation to be worked in, as it was the most difficult. It required us to add several procedures into the setup routine of the program. We had to present a display, allow the player to scroll through each of the four course options (indicated by a 1, 2, 3, and 4), and then select the course they wished to play. A stumbling block here was that we were limited to only two buttons. We settled on using the right button to scroll through the course numbers and the left button to select and start the game. This meant we had to add code so that our level selection would wrap-around back to '1' after moving past '4.'

Second, we thought it would be a good idea to create a red flash when the player collided with an obstacle - just having all the LEDs in the matrix turn red for a moment - a nice dramatic signal to the player that they had failed. This was quite simple, and only required one additional procedure and the modification of a routine call.

The third addition to the code was not originally planned, but was implemented when Ryan (a classmate testing the game) discovered that if a player quickly moved left or right off the screen at the beginning of the course, they could essentially cheat and would beat the game just by waiting for all the levels to complete. This was due to the manner in which the car position was recorded, and in which collision was detected. The car is stored in a byte, with one bit activated to represent the car's position on the bottom line of the matrix, and collision is detected by logical and-ing the car and the bottom row of the track. Rolling the car past the screen outside of the track boundaries sets the car byte to zero so collision is never detected. We fixed this bug by checking the carry bit after each car movement, and calling the collision routine whenever the car byte is zero and the carry bit is set, as this indicates that the car has left the track.

As we continued developing the program we made several other changes. For instance, we decided that the level number the player is on when they crash should be displayed in red, to distinguish it from the course selection menu (whose numbers display in green). We also modified minor details in Brad's program to meet our preferences, such as the button response delay, and the base speed of the game.

Finished Product

The project was an interactive learning experience from start to finish - from choosing components, to understanding how the hardware works, to learning the assembly language used with the PIC - and its culmination was very satisfying. Thanks to Sergei for teaching us so much in this class and helping us troubleshoot our problems and refine our code, and thanks to Brad for the game idea.

Next you can watch a game demo of tracks 1 and 4 being played. I turned the light off to eliminate the annoying screen flicker of the led matrix due to the recording (this flickering does not occur in real game-play, only on the video).

Front of Game Back of Game

Track Options:

Here are the different tracks you can try to navigate in our game... it's easy to create your own!

1 - EASY2 - MEDIUM3 - HARD4 - INSANE
easy track medium track hard track insane track

Downloads


Last modified:Sat, Jul 19, 2014.

02961