Introduction

Whack-a-mole is a game in which you use a mallet to hit moles that randomly pop out. We were inspired by this arcade game and used the FRDM-KL46Z board to create our own mini version called “Guac-a-mole.” In our game, an avocado will “pop out,” indicated by the flash of an LED, and the player must press the corresponding button during the flash period to gain a point. We use the on-board LCD screen to display basic instructions and score values and also include a GUI animation to go along with this hardware-based game. Through this project, we extended our GPIO knowledge to peripheral LEDs and switches and also learned how to use the LCD screen and serial communication between the board and laptop.

System Overview and diagram

This project has 3 main components: the FRDM-KL46Z board, peripheral devices, and the python GUI. The board communicates with the LEDs/switches and the python GUI to run a hardware and software component of the game. On the hardware side, the board writes to the digital pin connected to an LED to turn it on and also enables interrupts for that button. If the button is pressed, a signal is sent back as input to the board to keep track of points and change the python GUI accordingly. On the software side, we use the virtual COM port from MCUXpresso’s debugger to send a signal at different stages for the python file to change between game states. The python file reads the signal using the pySerial library and updates the screen accordingly.

Video

Technical description

Hardware

We wired six buttons and six LEDs to a breadboard and connected these components to the FRDM-KL46Z board. The LEDs represent the avocados and the corresponding button acts as input for the player to press.

Software

Main c file

Because we are using the LCD screen in this project, we needed the support files found in the directories in slcd SDK demo. We imported the library files lcd.h and lcd.c from Canvas into our source folder. We also took the functions init_uart() and uart_putc() from uart_example.c on canvas to create the library files uart.h and uart.c. We then included lcd.h and uart.h in our main file final.c.

The function PORTC_PORTD_IRQHandler() handles the button interrupts. After disabling the IRQ we clear all the button GPIO ports. The global “spam” variable is simply used to direct the branching in the IRQ handler. If the spam value is 1 (during spam phase) then the global variable seed is updated and displayed on the LCD. Otherwise, the animation is updated to display the smash screen and the global variable score is updated and displayed on the LCD (during rounds phase). Finally, the interrupt status flag is cleared and all interrupts are re-enabled.

The main function in final.c first initializes the UART, LEDs, and LCD. The UART and LCD are each initialized using the functions init_uart() and init_lcd() from their respective libraries. We initialized the LEDs by implementing the function LED_Initialize() in utilis.c. We extended the utilis.c file from our labs by initializing six GPIO pins from Port E as outputs and the clock for Port E to set up the LEDs. We also enabled the clocks for Ports C and D for the buttons. Two Ports were used for the buttons due to issues with using an overlapping GPIO pin for the onboard LED. After initializations, the uart_putc() function is used to send the ASCII character ‘0’ to the serial port for the python file to read, decode, and display the start screen on the connected laptop.

Next, to ensure the randomness of the LED sequence, we used the rand() and srand() functions from the standard library. The srand() function takes one integer parameter to be used as the seed by the pseudo-random number generator called with the function rand(). For the sequence of LED flashes to differ between games, the seed value also has to be different each time. So, our game begins by prompting the user to spam the left-most button. The LCD_Spam_Now() function prompts the LCD screen to display the words “spam”, “left”, and “now” and the uart_putc() function sends a signal to display the spam animation with instructions on the laptop screen. The BUTTON_Seed() function initializes and enables button interrupts on the leftmost button for 5 seconds to allow for user input to set the seed value. While the player clicks the button, the LCD screen displays the number of times the button has been clicked to represent the input seed value for the game.

Once five seconds are over we send another ASCII character to update the animation to the avocado screen. Then we create the processes LED_Start and LCD_Start using the code from Lab3. These processes will run concurrently such that LED_Start() flashes all the LEDs three times while LCD_Start() displays the words “tres”, “dos”, “uno”, and “go”. We begin these processes with a call to process_start. When both processes are complete, we enable all interrupts in preparation for the buttons at the next stage.

Once the countdown to the game completes, we enter the rounds phase of the game. Our game consists of ten rounds, so we used a for-loop to call the function round() ten times. The function round() begins by sending an ASCII character to the serial port to return to the avocado animation in case the player scored a point in the previous round and the smash animation was played. Then, LED_PIN is randomly assigned using rand(), and the BUTTON_PIN is simply set by subtracting 14 from LED_PIN, unless LED_PIN 19 is selected in which case the BUTTON_PIN is set to 13 (due to the inability to use PTD5). We initialize the single button by setting BUTTON_PIN as an input GPIO port, and setting up interrupts on the falling edge. This is to ensure that the player does not gain points by pressing any button. Next, the LED corresponding to the LED_PIN turns on, and button interrupts are enabled. After approximately one second, the interrupts are disabled, all the buttons are cleared, and the LED is switched off. This is so that the player can only gain a point during the duration when the LED is on. This phase is repeated until ten rounds are complete, then the LED_Result() function is called to light the different color LEDs based on score and uart_putc() is used to send an ASCII character to the serial port to change the display depending on the number of points scored.

LCD and UART configuration

Since LCD screen and UART communication were used, board files needed to be modified and added to the LCD screen project in order to correctly configure the pins, ports, and clocks necessary. Note that UART was added to the LCD project due to initial testing showing that UART communication works on a wide range of project configurations, while the LCD screen required many specific configurations. We stepped through the UART demo provided on Canvas in debug mode to determine which files and functions were necessary to configure UART. These functions or files were then added to the LCD project files in the board folder if they were not previously present in the LCD project.

GUI

A GUI was implemented using Pygame. To create the main.py, pygame and sys were imported and 4 classes called Spam, Avocado, Smash, and GameState were created for the different animations and a state to determine state and display the animations. In addition to creating these 4 classes, a section defining fonts, text, and other constants was created. Next, serial port reading was initialized and an infinite while loop was created to read the serial port using the inWaiting() function, such that the port was only ready when a signal was sent. The received ASCII character was decoded, and the game state was changed depending on the ASCII character. Once the game state was changed, the corresponding animation or image was displayed in the Pygame window.

Testing

Since we developed a game for our final project, we did not have traditional test cases but instead visually analyzed the game by running the debugger to step through and check certain sections if we had a hard fault error. Additionally, we used incremental testing to test individual functionalities of our game. For example, we tested each individual demo code and slowly added more functionality and combined it with our game. To test our full game, we ran our code in debug to use the virtual COM port for serial communication. We then played our game with various seed values (minimum 0 and maximum 20). Additionally, we tested our game for when the correct button is pressed on time, correct button pressed late, wrong button is pressed, and no button is pressed and made sure that the score was updated accordingly. We also tested the edge case for when a button was held down for a longer period of time. We made sure that the LEDs continued to flash as the LED output should be independent from button input. When we ran into a bug, we used the debugger to step through each line of code to identify the line that caused the bug. We also used print statements to debug our GUI in python.

Additional Resources Used

Work Distribution

We worked together to research, code, and test our final project. Coding was completed individually and together; each person would work on certain sections of the code, and then we would work together to debug to bounce ideas off of each other.