The list below features the modules that we've created and implemented; expand each section to see its pseudocode.
For full header and code listings, click on the button below to see the full repository on Bitbucket. Our primary files are in the "ProjectHeaders" and "ProjectSource" folders. Game audio files are stored in the "Audio Files" folder.
We developed the C code in MPLAB, and uploaded to the PIC32 using a SNAP programmer.
The state diagram between the two FruitSlice state machines is the same:
GameStartFSM creates the state machine for the start of the game, which includes coin insertion, setting the difficulty, audio feedback, and the start button. Once the user presses the start button, GameStart hands it over to the FruitSlice state machine.
bool InitGameStart(uint8_t Priority)
Initialize the MyPriority variable with the passed in parameter
Put machine into the Initial PseudoState
Initialize port line to receive Start Button State, RA3
Initialize button event checking module
Initialize port line to receive IR Coin State, RB15
Initialize port line to receive potentiometer inputs, RB2
Read the potentiometer value and set game difficulty accordingly
Initialize port line to light LEDs on upper platform, RA0
Initialize port line to light LEDs on lower platform, RA4
Initialize audio output pins
Post Event ES_Init to GameStartFSM
return True if successful
ES_Event_t RunGameStart(ES_Event_t ThisEvent)
If the current state is...
InitPState:
Turn on all of the LEDs
Set the current state to WaitingForCoins
WaitingForCoins:
Play intro audio
If a coin is detected
Increase the coin count by 1
Flash all the LEDs once
Reset the game score and all timers
Start the 20 second no-action timer
Set the current state to WaitingForStartButton
WaitingForStartButton:
If a coin is detected
Increase the coin count by 1
Flash all the LEDs once
Restart the 20 second no-action timer
If the potentiometer has been turned
Set the game difficulty accordingly
Change the LED colors accordingly
Restart the 20 second no-action timer
If the start button is pressed
Stop playing the intro music
Turn off all LEDs
Restart 20 second no action timer
Start 60 second game timer
Play game music
Start motor service and fruit slice state machine
Set current state to WaitingGameOver
If the 20 second no-action timer has timed out
End the game and turn on the red LEDs
Set the current state to GameOver
WaitingGameOver:
If the 60 second game timer has timed out
End the game by stopping the motor service and FruitSlice state machine
Set the current state to GameOver
If the 20 second no-action timer has timed out
End the game by stopping the motor service and FruitSlice state machine
Set the current state to GameOver
GameOver:
Wait 20 seconds before initializing the game again
bool CheckButtonEvents(void)
If the button state has changed
If the button is currently pressed
Post a button down event to the GameStart state machine
If the button is currently unpressed
Post a button up event to the GameStart state machine
bool CheckIRCoin(void)
If the state of the IR coin receiver line has changed
Post a coin detection event to the GameStart state machine
bool CheckPotentiometer(void)
If the value of the potentiometer input (ranging from 0 to 1023) has changed by more than 50
Post a potentiometer change event to the GameStart state machine
float setDifficulty(uint32_t PotValue)
Based on the potentiometer value, set a time interval at which the fruit LEDs will light up
A lower potentiometer corresponds to shorter time intervals
void TurnOnLED(int color, int platform)
If the upper platform is selected
Turn on the corresponding LEDs based on the color input
If the lower platform is selected
Turn on the corresponding LEDs based on the color input
void TurnOffLED(int color, int platform)
If the upper platform is selected
Turn off the corresponding LEDs based on the color input
If the lower platform is selected
Turn off the corresponding LEDs based on the color input
void DifficultyLED(uint32_t potValue)
Turn off all LEDs
Divide the current potentiometer value by 200 to get 6 different 'levels'
If the level is
0:
Turn on all the red LEDs
1:
Turn on all the red and green LEDs
2:
Turn on all the green LEDs
3:
Turn on all the blue and green LEDs
4:
Turn on all the blue LEDs
5:
Turn on all the blue and red LEDs
Once the user clicks the game start button, the GameStart state machine hands it over to the Fruitslice state machines. There are two, one for each fruit, and each one handles the actual gameplay by lighting up the LEDs (indicating that a fruit has arrived) and detecting a user "slice".
The Fruitslice state machines communicate with the motor service to display the user's score as well as the game timer.
bool InitFruitSlice(uint8_t Priority);
Set current state to initial pseudostate
Initialize port line to receive IR state, RB10 for platform A and RB11 for platform B
Initialize random number generator to use later for picking colors
ES_Event_t RunFruitSlice(ES_Event_t ThisEvent);
If the current state is...
Initial pseudostate:
If the button has been pressed,
Turn off the LEDs
Wait 3 seconds
Begin gameplay
LEDs are OFF:
Pick a random color to light up next
Start an LED timer based on the game difficulty
When the LED timer expires, turn ON the LEDs
Update the current state
If the no action 20-second timer expires, end the game
If the 60-second game timer expires, end the game
LEDs are ON:
Start an LED timer based on the game difficulty
If the LED timer expires, turn OFF the LEDs
Otherwise, if a slice was detected
Turn OFF the LEDs and stop the LED timer
Update the current state
If the LEDs were red
Decrease the score to zero and play the bomb audio
Otherwise add one point to the score
If the no action 20-second timer expires, end the game
If the 60-second game timer expires, end the game
Game Over:
Return to initial state
Wait for game start button press
bool CheckIRPlatformA(void);
If the state of the IR receiver line has changed
Post a slice detection event to the FruitSlice state machine
void playSound(int soundName);
For the corresponding sound name
Set the corresponding bit to LOW to start audio playback
void stopSound(int soundName);
For the corresponding sound name
Set the corresponding bit to HIGH to stop audio playback
MotorTest is a service that responds to calls from both the GameStart and Fruitslice state machines. It moves two servo motors- one to display the score, and one to display the passage of time as the user plays the game.
bool InitMotorTest(uint8_t Priority);
Enable all 5 channels of PWM.
Choose _Timer3_, and set the frequency to be 50Hz.
Configure pins RB13 and RB14 as digital.
Map timer channel to RB14.
Map counter channel to RB13.
Assign _Timer3_ to timer channel and counter channel.
Set max PWM value for timer channel.
Set min PWM value for counter channel.
ES_Event_t RunMotorTest(ES_Event_t ThisEvent);
If the event is...
ES_TIMEOUT:
If MOTOR_TIMER times out:
Calculate the PWM value based on current i.
Init MOTOR_TIMER to be one second.
Increment i by 1.
Relate t to i.
Set current PWM value for timer channel.
if t < 1:
Set i to its initial value.
Set max PWM value for timer channel.
Stop MOTOR_TIMER.
If NO_ACTION_20S times out:
Set i to its initial value.
Stop MOTOR_TIMER.
GameStart button pressed:
Init MOTOR_TIMER to be one second.
Set max PWM value for timer channel.
Set min PWM value for counter channel.
Set i to its initial value.
Successfully sliced a fruit:
Score increments by one.
Calculate the PWM value based on current score.
Set current PWM value for counter channel.
Sliced a bomb:
Set min PWM value for counter channel.
Set score to be zero.
float Time_to_PWM_NUM(int time);
Define linear slope k,
k = (MAX_PWM_NUM * TICS_PER_MS - MIN_PWM_NUM * TICS_PER_MS) / 60
Calculate the PWM value based on the input time,
NUM = -k * time + MAX_PWM_NUM * TICS_PER_MS
Return NUM