Introduction
This project is a simple arithmetic game that utilizes almost all the components on the FRDM-KL46Z microcontroller board. The game is very simple and affords players the opportunity to set arithmetic questions for each other, take turns answering them, and get points accordingly. The motivation behind this game was to create a project that maximized the features of the FRDM-KL46Z board (without adding peripherals) to create an engaging but simple math-based game. So the impact of the game would be for elementary school and middle school kids to play a fun game where they can test each other's math skills by playing our game! As far as the board is concerned, the game makes use of two LEDs, two buttons, and a single LCD display. The terminal is used to display the rules of the game to the players as well as the current round and the scores of each player.
When the game starts, the rules are displayed in the terminal and the green LED flashes to indicate it's player 1's turn to start the game. Player 1 uses the buttons (left button - DOT; right button - DASH) to enter the arithmetic question using a morse-code like pattern we created. The question is redisplayed for the second player to view before entering an answer. A message displays in the terminal to indicate it's player two's turn to enter an answer (this is indicated by the flash of the red LED on the board too). Player two enters an answer using the same morse-like code pattern and scores a point if the answer is right. Otherwise, player one scores the point. The terminal displays appropriate messages to guide the players so as to avoid confusion as to what to do at each stage. The LEDs also blink accordingly to indicate when a player can enter an input.
We have learnt a lot by working on this project as most of what we implemented was new so we had to figure stuff out to make things work. For example, we have learned how to setup the LCD display and how to display specific letters on the LCD display which we have not talked about in class. In addition, we learned how to use UART communication and connect to the terminal on MCUXpresso IDE. In short, it has been an amazing journey of learning and discovery. We worked on the project one step at a time by making sure each component or aspect of our project was functional before putting everything together. It was fun watching everything work well and even more exciting seeing everything come together for our game to take form. We learnt how to work with the LCD display, buttons, also how to write modular code and deal with various bugs pertaining to working with these components, etc. Overall, we have accomplished the goal of expanding our knowedge base in terms of embedded systems and used this to create a fun game.
System Overview and diagram
Everything that we want to use is already on the FRDM-KL46Z board, so we don’t require any peripherals. The components of the board that we will be utilizing are the following: the two buttons (SW1 and SW3) which are the sources of input, LCD display, and the two LEDs (Green and Red LED) which are the outputs for our game. Moreover, something in addition we implemented was the connection to our computer/laptop and printing several outputs on the terminal of MCUXpresso using UART communication. So basically these are the components and parts that we used to build our final project. Now we will describe how the system works at a high level and how these components/parts work together.
So basically, our final project is a two-person/two-team arithmetic game. We will run our C code that we implemented on the FRDM-KL46Z board in the debugger mode to be able to view and access the terminal. To create, connect, and view the terminal, we follow the instructions posted under Testing the UART code.pdf on Canvas under Serial Code Example under Final Project module. Once we have the terminal setup, we can run the code in the debugger mode. Then on the terminal, we can see that a welcome message along with the instructions for the game are printed. Also, it prints the round # (round 1) and prints that it is player 1’s turn to enter the arithmetic formula. Then on the board, we have the green LED flash five times to indicate it is player 1’s turn. Player 1 decides the simple arithmetic problem. After deciding this, player 1 inputs this into the board using the two buttons which we setup as sources of input through GPIO (SW1 which corresponds with the dash - right button - and SW3 - left button - which corresponds with the dot). So we will use the PORTC_PORTD interrupts and the corresponding interrupt handler to deal with user input as they are pressing the buttons SW1 and SW3 to enter the inputs. The interrupt handler contains most of the logic we describe below. So player 1 has to enter three inputs one after the other.
To enter each of the 3 inputs, the user has to use the two buttons and follow the encoding we prepared which is displayed under the technical description section. The first input will be operand 1, which is just a non-negative integer which at max can be four digits. As the player is entering their number one digit at a time, we display the digits on the LCD starting from the left-most position and going to the right. Once the player has entered their desired number, they have to enter the pattern for L to lock their input. Then we clear the LCD display and display L at the left-most position on the LCD to indicate the input is locked. The second input is some operation that they would like to perform and the list of valid operations is as follows: S - Subtract, A - Add, E - Exponent, P - Product, r - remainder, d - divide. After the player enters the pattern for the letter, we clear the display, and we display the letter on the LCD at the left-most position. Then they have to enter the pattern for L to lock their input. Then we clear the LCD display and display L at the left-most position on the LCD to indicate the input is locked. The third input is operand 2, which is just another non-negative integer which at max can be four digits. The system works just like it did for operand 1 here.
More specifically, as the user is entering the digits for operand 1 one by one, we will check after 6 button presses and match the entered input against the encoding we created which is very similar to morse code. For example, our encoding is like 1 is dot dash dash dash dash dash and A is dot dash dash dash dash dot. After decoding which number the user inputted, we just display that number on the LCD display. In this format, we display all the digits of operand 1 player 1 enters. Then player 1 should enter the encoding for letter L which represents locking/storing their operand 1 number into a global variable. Then we clear the display. Then as player 1 enters the operation, the encoding for the letter, we will check after 6 button presses and match the entered input against the encoding we created. After decoding which number the user inputted, we display the corresponding letter on the LCD. Then player 1 should enter the encoding for letter L which represents locking/storing their operation letter into a global variable. Then we clear the display. Then for inputting operand 2, player 1 should basically follow what they did for operand 1. After they enter this, then we switch to player 2. This is indicated by flashing the red LED five times.
Using the stored values for operand 1, operation, and operand 2, we display one after the other with small delays in between on the LCD display for player 2 to see what the arithmetic problem is. Also, on the terminal, we can see the statement printed that indicates it is player 2’s turn to enter the answer. Then player 2 will enter their answer to this problem just like player 1 did. We will display this input just like we did with player 1’s operands and we will also store their input as a global variable. Then they have to enter the pattern for L to lock their input. Then we clear the LCD display and display L at the left-most position on the LCD to indicate the input is locked. Once they lock their input, using the global variable we created for player 1’s operands and operation, we will compute the correct answer for the arithmetic problem. Then we compare this correct answer with the stored input of player 2. If these match, player 2 gets a point and we flash the red LED once to indicate player 2 got the point (answered the arithmetic question correctly). Else player 1 gets the point and we flash the green LED once to indicate player 1 got the point as player 2 answered the arithmetic question incorrectly. Also, on the terminal screen, the statements regarding what the correct answer was, who got the points and why, and what the current scores for both the players will be printed. This was basically round 1. Then on the terminal screen, we see that now it is round 2 and it is player 1’s turn to enter the arithmetic formula once again and see the 5 green LED flashes on the board and everything from round 1 repeats.
So our game runs for a total of three rounds (to not get into a situation of ties). We essentially repeat all the processes we described above for rounds 2 and 3, for a total of three rounds. At the end of the three rounds, we blink the red LED the number of times equal to the score of player 2 if player 2’s score is greater. Else, we blink the green LED the number of times equal to the score of player 1 if player 1’s score is greater. Then on the terminal, we can see we print the same things like we did after the end of the previous rounds like what the correct answer was, who got the point for round 3 and why, and the final scores of both the players. Then we print who the winner is and the message that the game is over and they can restart the game if they would like by pressing the restart button on the board or by running the code through the debugger mode like we explained above.
A few more notes about the system description: we will also mention that any time, any player presses both the buttons (SW1 and SW3) at the same time, that will clear the current display of the LCD and clear the current input that we stored for that specific operand or operation or answer that they were entering and they can re enter the desired input. Another thing that we implemented was that if the user enters a pattern that doesn’t match with any of our encodings, then we display Err on the LCD display and the user can re enter their pattern after Err disappears from the LCD display. Finally, if player 1 enters an arithmetic formula that leads to an answer greater than 4 digits or a negative answer, it violates our game rules, so player 2 can enter any number answer they want and they will be given the point for the round.
We only are using C programming to do all of our functionality (if-else statements, while loops, for loops, global variables, functions including helper functions, and other C-constructs that we learned during the semester), and we created .c and .h files as needed. We included utils.h and lcd.h at the top of our code file to be able to use these functions and parts. The concepts from the class that we are using are LED initialization and other functions for LEDs, GPIO, other ports. We are also using the two buttons (SW1 and SW3) and using interrupts to handle the input through these buttons as talked about in our discussion sections. We also use the concept of communication protocols as well where we use UART communication to connect to the terminal on MCUXpresso and print the various things on the terminal to give the players a sense of the information of the current state of the game. In terms of locks and processes and concurrency, we are not really creating a complicated concurrency system with mutual exclusion and a real time system, so we won’t need to use these concepts.
Block Diagram of the System
![Block Diagram of the System](images/IMG_0416.jpg)
Video
Technical description
The FRDM-KL46Z microcontroller was the sole component used for this project. We wanted to maximize all the features of the board as much as we could so we incorporated most of the built-in features of the board. A large portion of the project was figuring out how these features worked and writing code to incorporate them into our game. We used buttons (for inputs), LEDs (for signaling), and most prominently the LCD as the main interface for our game. We decided to make our code as modular as possible so we have several functions that are responsible for the functionalities of the game. Some of the main functions include:
- logicInsideInterrupts()
- PORTC_PORTD_IRQHandler
- main(void)
- calculateAnswer()
- checkAnswer()
- compare()
- letterA(), letterS(), letterP(), letterd(), letterE(), letterr(), letterL()
- A - Addition
- S - Subtraction
- P - Product
- L - Lock
- E - Exponent
- r - Remainder
- d - Division
- displayLetter(), and displayDigit()
This is the main function that implements all the logic inside the interrupt handler. From receiving and processing user input, whether operands (numbers) or letters (operations), this function determines whether the input entered by the user corresponds to a valid pattern or encoding and processes it accordingly. If the user enters a valid encoding of length six, this function ascertains whether it's an operand or an operation and correctly stores the input in an array-type variable corresponding to either the first operand, or the second operand. This is because while we store operations as characters, we store user inputs as arrays of length four (LCD can only display a maximum of four digits at a time). Whenever a user enters a digit for an operand, we store each digit in the corresponding array, unpack the array where necessary, process the unpacked contents, and store the result in the results array before displaying it to the user. So, this function essentially contains the logic to manage all three rounds of the game, receive and process inputs from both players, determine which player's turn it is and generally control the flow of the game. It contains logic that calls functions like clearDisplay() (clears display to receive new user input), displayLetter(), displayDigit(), LEDRed_Toggle() (toggles the red LED), LCD_TimeDelay() (implements delay that readies the LCD to receive new input), and checkAnswer() among other functions that make the game run as expected.
Our game has three modes for the switches (SW1, and SW3): SW1, or SW3 pressed once, or both switches pressed at the same time. This function contains logic that is implemented when depending on which mode is engaged. As far as our code is concerned, this function is the interrupt handler for switches. When either switch is pressed once, this function may write a dash or a dot to the corresponding string where the pattern is later decoded and processed to display an appropriate output. This function calls logicInsideInterrupts() to handle the main logic of the IRQ_Handler. At the end, this function clears the interrupt status flags for the switches by writing 1 to them.
This is the gateway to the entire game as it initializes the LEDs, the LCD, UART, and the switches (SW1, and SW3). It then starts the entire game by indicating it's player 1's turn to entire an arithmetic query. The function calls the neccesary helper functions to manage the entire game logic and ensure everything runs smooothly.
This function contains logic to calculate the answer of the arithmetic input entered by a player. When the second player enters the answer to the question, the checkAnswer() function calls this function to be able to compare the player's answer to the calculated answer returned by this function.
This function contains logic that determines what happens when the second player enters an answer input. It transcribes the answer input of the second player and checks if this answer is equal to the correct answer calculated by the system. It displays appropriate output in the terminal to display which player scores the point, the current round of the game, and also shows the current scores of the players.
This one-argument function contains logic to compare the pattern entered by the user by means of the buttons to the existing patterns we already have. If this pattern matches any of the patterns harcoded in the system, the function returns the result that corresponds to the pattern. For example, if the pattern entered by the user matches the coded pattern for 1 in the system, this function returns 1.
These functions contain logic to turn on the relevant segments to display a given letter. Using the mapping we derived for the LCD display, we created these functions to turn on appropriate segments that will display letters A, S, P, d, E, r, and L which correspond to operations being performed. In this sense, when letterA() is called, the correct segments light up to display the letter "A" on the LCD display.
What do the letters mean?
Depending on the pattern entered by the user, these function are called to display either the letter or the digit corresponding to the pattern entered. So, if the user enters the pattern corresponding to the addition operation, displayLetter() is called to display an "A" (by calling displayA()) which is the letter corresponding to the addition operation. The displayDigit() function is called with the right arguments to display the right operands which consist of digits from 0 to 9.
Mapping Segments of the LCD Display
We wanted to use the LCD display to enable the players to see the operands and operations of their arithmetic input. Initially, we were getting incorrect displays as our code was not displaying what we wanted. In order to resolve this issue, we wrote code to light up each segment of the LCD display and derived a function/mapping of the identifier that turns on each segment. This enabled us to correctly display the digit or letter we wanted.
![Mapped segments of LCD](images/segment1.jpeg)
Digits and letters and their corresponding segments
Initially, we wanted to use the capacitive touch sensor for entering numerical inputs for our game because we believed it was a quick and easy way to enter inputs by just sliding across the sensor. But, due to errors and low dependability, we decided to use a morse-code like pattern to enter inputs for our game. The pattern we came up utilizes the two buttons (left button - dot; right button - dash) on the microcontroller board and is as shown below.
![Digits and letters and their corresponding segments](images/letters.jpeg)
Switch/Button Designations
![Switch/Button Designations](images/labels.jpeg)
Locations of the SW1, and SW3 buttons and their designations are shown above.
Pattern for Entering Inputs
![Table of Morse-code like key for entering inputs](images/segment4.png)
In our code, we represent these letters and digts as strings where dots are represented as "0"s and dashes are represented as "1"s. So, when the user enters a pattern (using the SW1 - dash, and SW3-dot buttons), it is translated as a string such that the input string is compared to hardcoded valid string patterns in the system. A large part of our code is translating user input to values that we can work with so the strcmp() function came in handy and made our code simpler and neat.
Role of UART
UART communication was a crucial part of our project so we had a function to initialize UART to set up all the modules and pins we need to use UART. We needed to be able to print the rules of the game as well as game round and game score information to the players so we created functions that gave us the ability to to print characters and strings to the serial terminal. The init_uart(), uart_putc(), and uart_puts() UART functions from the uart_example.c file provided to us enables us to initialize UART, print characters and print strings respectively to the serial terminal as necessary.
Game Rules
To play the game, you must run our code(final_project_code.c) in debugger mode.
This is a 2 person/team game. Basically, we have player 1 enter an arithmetic formula/problem that needs to be solved by player 2. So for example 153 * 26.
SW3 represents a dot and SW1 represents a dash.
The encodings for each of the numbers and the letters are presented our webpage
The Green LED will flash 5 times to indicate it is player 1’s turn. So player 1 needs to enter three things. They can enter at most a 4 digit number (operand 1), and then enter the letter L to lock their input. Then they can enter any of the valid operations (A, S, d, P, E, r), and then enter the letter L to lock their input. Finally, they can enter their second max 4 digit number (operand 2), and then enter the letter L to lock their input.
Then we switch to player 2’s turn to enter the answer for this problem after flashing the Red LED 5 times.
Now it is player 2’s turn to enter the answer they got trying to solve player 1’s problem. They have to enter the letter L to lock their input.
The score for player 2 will increase if they enter the correct answer, else player 1’s score will increase.
This is the description for round 1. There are a total of 3 rounds which operate identically. The player who has the highest score after round 3 will win.
Some Notes/Rules
If you accidentally enter the wrong number or letter in the wrong location or at the wrong time (so a number in place of letter or vice versa which is not allowed and should not be done), you can press both buttons (SW1 and SW3) to clear the current input (only the current number - operand or answer- you are entering or the operation) and re-enter the desired input, but this can only be done before pressing L (locking) the input you have.
All the numbers entered by the players are limited to max 4 digits and can only be non-negative integers. If you try to enter a number that is greater than 4 digits, then you would just keep replacing the digits from left to right with the new digits you are entering.
Any formula/problem entered by player 1 that results in an answer that is negative or greater than 4 digits will automatically result in a point for player 2 for that round regardless of the answer player 2 enters in.
Players should not interrupt the toggling of LEDs. Wait for the LED to finish blinking before entering any inputs.
Do not press the buttons too quickly because the pattern you might enter might be incorrect if you press the buttons too quickly.
If you enter the wrong pattern, then an Err message will be displayed on the LCD and you can enter your pattern again.
Testing
We worked on major segments of our project separately so we adopted a testing scheme where we tested blocks of code separately before testing the entire code after integrating the various segments. Initially, we tested that our two LEDs to ensure they were working as expected because we wanted to use LED flashes to signify the start of a new game. We initialized our LEDs and tested them using several inputs to make sure we had perfect control over when and how they blinked. We wrote the necessary code for interrupts and delays to make this work seamlessly.
After writing the code for the buttons as inputs, we ensured the correct functionality by testing the buttons out. We actually took turns trying to input specific patterns and see that the pattern was correctly being stored as a string in one of our global variables through the debugger.
The LCD display is a crucial component of our project so we spent a lot of time studying and testing it to ensure we could effectively use it for our game. As mentioned above, we initially had issues working with the LCD display as it was failing most of our test cases. So we decided to execute several tests that enabled us to map out the LCD display to ascertain which characters we can display and what segments of the LCD we need to turn on to display what we want. In other words, the tests we did were just various statements to turn on the segments to see which ones we had to enable and correctly enabled for our letters, displayDigit with specific numbers, etc. to check the correct functionality of the LCD. Moreover, we also tested things like pressing the two buttons at the same time cleared the LCD display, entering the correct pattern resulted in the number/letter being displayed in the correct position on the LCD display, pressing the worng pattern with the buttons printed the word Err on the LCD display, etc.
With respect to displaying characters on the LCD, we had to specify the segments to turn on and the position of the character so we wrote several tests to experiment with shifting positions for characters on consecutive inputs from the user. We decided to execute these tests because we run into an issue where charactes were being displayed in the same position when the user enters consecutive inputs. For instance, a character "3" is quickly replaced by say a "2" when the user enters the pattern for a "2" right after entering a "3". The tests enabled us to experiment with shifting the position of the character to be displayed to the next position on the LCD.
Initially, our code for the game was such that, the arithmetic query entered by the first player is computed and stored before the second player enters his answer. However, we run into several problems where we saw in the debugger mode, the value of the stored answer changes in the system after the second user starts entering his answer. We reckoned some of our global variables or functions were indirectly changing the value but we had trouble tracing the source of the problem. We wrote several tests and made use of print statements to pinpoint the source of the problem but we eventually decided to compute the answer for the problem entered by the first player after the second player enters his answer. In this way, we only had to compare our computed answer with that entered by the second player.
Ad for the values of score variables, round varibales, calculatedAnswer, stored values of operands, operation, and inputted answer, we ensured and tested that these were the right values by playing the game several times and seeing that the right values were being updated and stored in these global variables in the debugger mode.
As far as our final game is concerned, we mainly tested the product (interrupt logic, input and output functionality, etc.) by running our program and interacting with the board to ensure our game worked correctly. We interacted with the board and tried as many edge cases as we could come up with in order to prevent our game from being riddled with bugs and too many rules. Testing the game enabled us to figure out many of the limitations of our game thereby enabling us to draft the rules for the game (see technical description section for the rules).With respect to UART, we made use of the serial termial, so we tested UART functions to print several testing statements to the terminal without interrupting our game. Once that worked, we just replaced those statements with desired statements for the game. So overall, instead of having tradition test cases, we tested our project using manual testing through human interaction with the board (actually by playing the game at every single stage of development and seeing that we got the desired functionality and that there were no bugs present).
Work Distribution
We communicated frequently with each other through text messages about when we wanted to meet, how much work we have done so far, any errors we ran into, etc. Through this constant text messaging and replying to each other within a few hours helped us to really stay in contact with each other for the entire project. Then we constantly met with each other on our own time and during some of the scheduled office hours. So during these time slots, we collaborated on the project & wrote most of the code together. We brainstormed ideas for our implementation and functions, explaining to each other what we needed to do for each of the functions. Then we took turns writing the code. When one person was writing the code, the other person gave them suggestions on what the line of code should be, any errors that might have occurred and how to fix them, etc. Then we switched our roles after writing about half of the code. We played the game together (where one of us acted as player 1 and the other acted as player 2) to test our code with all the edge cases and just general test cases to test our entire functionality. For the video, kp386 was the one who was demonstrating and speaking in the video and ed433 was the one recording the video. Lastly, for the webpage, we split up the sections evenly. kp386 worked on System Overview and Work Distribution. ed433 worked on Introduction, Testing, and Class Concepts Used. Then we both worked on the Technical Description and Additional Resources Used sections and any other sections not mentioned before. This clearly gave the both of us an equal chance of actively participating in the major tasks (coding, taking the video, testing, and creating the webpage, etc.). The tools we used to facilitate collaboration were text messaging for communication, Google Docs to simultaneously write any notes/discussions, emails to share files, etc. For the coding part, we didn’t use any collaboration tool. We worked on one computer, taking turns writing the code, and testing the code with the board on that computer. Finally, everything went smoothly with our work distribution.
Class Concepts Used
Most of our code is written in C, and a large portion of the functionalities we implemented for our game extensively made use of C constructs. if and if-else statements, as well as for-loops came in handy. Strings and int data types as well as data structures like arrays were also used especially for storing user inputs in an easily manipulable form for processing.
With respect to I/O and communication protocols, UART was used to communicate through the serial terminal and a large part of our program depended on getting input from a user, processing it and displaying the right output. Interrupts were used extensively to facilitate the correct flow of the program especially in terms of controlling when the LED flashes, delays between switch/button presses, and displays by the LCD in the right order without disturbing the flow of the game.
Additional Resources
The resources provided by the TAs and those available on Canvas were really instrumental to finishing our project. We reviewed the sample switch_demo.c
code on Canvas to set up the switches/buttons (SW1, and SW3) for our game. The LCD library provided to us is also came in handy as a large part of our game depended on having a
working LCD. While working on the project, we also referred to the FRDM-KL46Z User manual and the reference manual provided on Canvas to be able to
correctly implement timers, and GPIO interrupts and inputs.
- https://canvas.cornell.edu/courses/47697/files/7407304?module_item_id=1846324
FRDM-KL46Z User’s Manual
- https://canvas.cornell.edu/courses/47697/files/7407282?module_item_id=1846323
ARMv6-M Architecture Reference Manual
- https://canvas.cornell.edu/courses/47697/files/7407398?module_item_id=1846298
Switch Demo File
- https://canvas.cornell.edu/courses/47697/pages/lcd-library?module_item_id=1846300
LCD Library
- https://canvas.cornell.edu/courses/47697/pages/serial-code-example?module_item_id=1846297
Serial Code Example - UART