Difference between revisions of "F20: Corona Run"
Proj user4 (talk | contribs) (→Project Title) |
Proj user4 (talk | contribs) (→Appendix) |
||
(256 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | + | [[File:Corona run 1.png|thumb|600px|caption|right|Corona Run Logo]]<BR/> | |
− | + | == Corona Run == | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | </ | ||
− | == | + | == '''Abstract''' == |
− | == | + | <font size=4>'''''CORONA Run'''''</font> is a game designed for every one (<font size=2>'''Not PG-13 Rated'''</font>) to have a good time during this pandemic. Inspired by the current real-life pandemic situation and the game "Minion Rush", the main objective of this game is to avoid ''CORONA'' virus. As the game progresses, the player will encounter not only the virus, but also power-ups that provide the player with temporary immunity to the virus. The score of the player will depend on how many viruses the player can avoid. To achieve so, we had to design driver for 64*64 Led matrix and interface it with SJTWO board. Sound effects were given using audio Fx board. The game utilizes accelerometer sensor as the controller to make the game more compact and simple to play. The only buttons that the player uses will be the START and PLAY AGAIN button. |
− | |||
− | == Objectives & Introduction == | + | == '''Objectives & Introduction''' == |
− | + | The main objective of this project is to use SJ2 board as the center of our project. To create a properly working game, the game must be able to: | |
+ | * Detect collision with CORONA virus to reduce life | ||
+ | * Provide the player with power-ups when the player touches it | ||
+ | * Keep track of the score | ||
+ | |||
+ | There are some requirements that must be used when creating the game, such as: | ||
+ | * Utilizing FreeRTOS to run the game on SJ2 Board | ||
+ | * Utilizing accelerometer to control the game | ||
+ | |||
+ | '''How to play?''' | ||
+ | |||
+ | Player will start by being in one of the 3 tracks and can move when needed.Player position is controlled by on-board accelerometer so the gamer can move the player as per their wish. Corona viruses will approach continuously towards the player and the position of virus is randomized. Each time the player avoids a virus, 1 point is added to the score. To add life, we have sanitizers and they are less frequent than the virus on screen. In order to gain immunity the player can gain sanitizer and this will also result in 3 points being added to score. The maximum life is three and after that if the player hits the virus the game ends. The game starts with a very basic speed and with time the speed of viruses increases periodically which adds difficulty to the game. We also have a easter egg feature in which player has to get at least 10 points without hitting virus and sanitizer and once they succeed, a mask will spawn. If the player grabs the mask, 30 points are added to the score. Player needs to gain 999 score to win this game. | ||
=== Team Members & Responsibilities === | === Team Members & Responsibilities === | ||
− | * | + | <gallery widths=240px heights=240px perrow=4 mode="packed-hover"> |
− | ** | + | Image:prof_pic_cmpe244_f20.jpg|Suryanto Phienanda<br>''[https://www.linkedin.com/feed/ LinkedIn][https://gitlab.com/sphienanda GitLab][[File:cartman.gif|50px]]'' |
− | * | + | Image:ellis.jpg|Ellis Makwana<br>''[https://www.linkedin.com/in/ellis-makwana-1b9026192/ LinkedIn]'' |
− | ** | + | Image:anee.jpg|Anee Dudhia<br>''[http://www.linkedin.com/in/anee-dudhia-15693b1a0/ LinkedIn]'' |
− | * | + | Image:will.jpg|William Asper<br>''[https://www.linkedin.com/in/asperw LinkedIn][[File:catjam.gif|50px]]'' |
− | ** | + | </gallery> |
− | * | + | |
− | ** | + | {| class="wikitable" |
+ | |- | ||
+ | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> '''Name''' </font></span> | ||
+ | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> '''Roles / Responsibilities'''</font></span> | ||
+ | |- | ||
+ | ! scope="row"| Suryanto Phienanda | ||
+ | | | ||
+ | *'''Team Lead''' | ||
+ | *Designer of Main Menu Screen | ||
+ | *Game Logic Developer | ||
+ | *LED Matrix Driver | ||
+ | *Code Debugger | ||
+ | |- | ||
+ | ! scope="row"| Ellis Makwana | ||
+ | | | ||
+ | *Game Logic Developer | ||
+ | *Designer of Game Characters | ||
+ | *Git Repo Manager | ||
+ | *Code Debugger | ||
+ | |- | ||
+ | ! scope="row"| Anee Dudhia | ||
+ | | | ||
+ | *Designer of Countdown Animations | ||
+ | *Game Logic Developer | ||
+ | *Designer of Game Over Screen | ||
+ | *Wiki Manager | ||
+ | |- | ||
+ | ! scope="row"| William Asper | ||
+ | | | ||
+ | *Game Logic Developer | ||
+ | *Code Debugger | ||
+ | *Audio FX Configuration to SJ2 | ||
+ | *Accelerometer Configuration to SJ2 | ||
+ | |} | ||
− | == Schedule == | + | == '''Schedule''' == |
− | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
− | ! scope="col"| Week | + | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Week </font></span> |
− | ! scope="col"| Date | + | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Date </font></span> |
− | ! scope="col"| Task | + | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Task </font></span> |
− | ! scope="col"| Actual | + | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Actual </font></span> |
|- | |- | ||
! scope="row"| 1 | ! scope="row"| 1 | ||
− | | 10/8 | + | | 09/16 |
− | | | + | | |
− | | | + | * Form Project Group |
+ | * Setup first meeting on 09/18 | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 2 | ||
+ | | 09/18 | ||
+ | | | ||
+ | * Brainstorming project ideas | ||
+ | * List project parts/equipment | ||
+ | * Deciding on LED Matrix resolution | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 3 | ||
+ | | 09/26 | ||
+ | | Project Proposal Submission | ||
+ | | <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 4 | ||
+ | | 10/13 | ||
+ | | | ||
+ | * Group assignments | ||
+ | * Project approved by Prof. Preetpal Kang | ||
+ | * Review previous CMPE244 projects | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 5 | ||
+ | | 10/19 | ||
+ | | | ||
+ | * Order LED Matrix | ||
+ | * Creating the game cover page | ||
+ | * Wiki Schedule | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 6 | ||
+ | | 10/23 | ||
+ | | | ||
+ | * Review datasheet for components | ||
+ | * Finalizing gameplay, challenges, score system | ||
+ | * Setup Git Repo | ||
+ | * Discussing each member possible role | ||
+ | * Setup weekly schedule | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 7 | ||
+ | | 10/25 | ||
+ | | | ||
+ | * Assigning each member role | ||
+ | * Design FSM for the game | ||
+ | * LED Matrix arrived | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 8 | ||
+ | | 10/30 | ||
+ | | | ||
+ | * Write LED Matrix Driver for Sj2 Board and Show Some Animations on LED Matrix | ||
+ | * Display Some Number or Letter on LED Matrix | ||
+ | * Display Main Menu on LED Matrix | ||
+ | * Make "CORONA RUN" Moving on LED Matrix | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 9 | ||
+ | | 11/1 - 11/7 | ||
+ | | | ||
+ | * Show Three Lanes for The Game | ||
+ | * Display Player Animation | ||
+ | * Display Corona Virus Animation | ||
+ | * Display Hand Sanitizer Animation | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 10 | ||
+ | | 11/8 - 11/14 | ||
+ | | | ||
+ | * Working on collision detection | ||
+ | * Showing Left / Right on Telemetry Controlled by Accelerometer | ||
+ | * Working on difficulty of the game | ||
+ | * Working on randomization for enemy and power ups | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 11 | ||
+ | | 11/15 - 11/28 | ||
+ | | | ||
+ | * Move Player to Left / Right with Accelerometer | ||
+ | * Display Score on LED Matrix (Increase by 1 for Virus and by 3 for Sanitizer) | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 12 | ||
+ | | 11/29 - 12/5 | ||
+ | | | ||
+ | * Start looking for bugs and debugging process | ||
+ | * If none found, add easter eggs to the game | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 13 | ||
+ | | 12/6 - 12/12 | ||
+ | | | ||
+ | * Cleaning code and uploading to Git repo | ||
+ | * Create notes on problems encountered and what could be added to the game | ||
+ | * Make a Case for LED Matrix | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
+ | |- | ||
+ | ! scope="row"| 14 | ||
+ | | 12/16 | ||
+ | | | ||
+ | * Documenting on Wiki | ||
+ | * Demo in CMPE244 | ||
+ | | | ||
+ | * <font color = "green">Complete | ||
+ | * <font color = "green">Complete | ||
|} | |} | ||
== Parts List & Cost == | == Parts List & Cost == | ||
− | + | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Parts </font></span> | ||
+ | ! scope="col" style="background:#588ecc;"|<span style="color:#FFFFFF"> <font size=3> Costs </font></span> | ||
+ | |- | ||
+ | ! scope="row"| SJ2 Board | ||
+ | | $ 50.00 | ||
+ | |- | ||
+ | ! scope="row"| [https://www.amazon.com/MakerSpot-Breadboard-Voltage-Solderless-Friendly/dp/B01IUYLVFK/ref=sr_1_5?crid=31T60F4PYZDVU&dchild=1&keywords=power+supply+module+5v&qid=1608152249&sprefix=power+supply+module+5v%2Caps%2C307&sr=8-5/ 5 V Power Supply Module] | ||
+ | | $ 6.99 | ||
+ | |- | ||
+ | ! scope="row"| [https://www.adafruit.com/product/2220 Audio FX Sound Board] | ||
+ | | $ 24.95 | ||
+ | |- | ||
+ | ! scope="row"| [https://www.bestbuy.com/site/jbl-go-2-portable-bluetooth-speaker-black/6239934.p?skuId=6239934&ref=212&loc=1&ref=212&loc=BM01&gclid=CjwKCAiA_eb-BRB2EiwAGBnXXhNbOfk_cH2HPk7DUMvU-MUUZRjXBWg-4lLi_VfRXsvFA6tIUBBoGRoC2e4QAvD_BwE&gclsrc=aw.ds JBL Go Speaker] | ||
+ | | $ 29.99 | ||
+ | |- | ||
+ | ! scope="row"| [https://www.adafruit.com/product/3649 LED Matrix] | ||
+ | | $ 71.40 | ||
+ | |- | ||
+ | ! scope="row"| [https://www.adafruit.com/product/825 Jumper wires1] [https://www.adafruit.com/product/1950 Jumper wires2] | ||
+ | | $ 11.24 | ||
+ | |- | ||
+ | ! scope="row"| Total | ||
+ | | $ 194.57 | ||
+ | |} | ||
+ | |||
+ | P.S: Some prices included shipping | ||
== Design & Implementation == | == Design & Implementation == | ||
− | |||
=== Hardware Design === | === Hardware Design === | ||
− | + | The following is block diagram which shows the connection of the board to LED matrix and Audio FX. | |
+ | |||
+ | [[File:FIGURE-BLOCK DIAGRAM.GIF|800px| thumb|centre|Block diagram]] | ||
+ | <br> | ||
+ | The main controller of this project is the SJ2 board. Every hardware communicates through SJ2 board.<br> | ||
+ | To begin with the project, the SJ2 board must first be connected properly to the LED matrix. To make wiring easier, the team tried to mimic the pinouts of the LED matrix. | ||
+ | The pins are configured down and are really useful in order to send signals from accelerometer to the LED Matrix. Since the accelerometer is embedded on the SJ2 board, then there are no connections neccessary. <br> | ||
+ | |||
+ | The idea behind this design is to basically control the player using accelerometer, which sends a signal that is received by SJ2 board. Based on the signal, the player will move to the left/right. This is controlled by SJ2 board and the SJ2 board tasks after receiving the signal is to draw character at the new location and clear the character at the old location.<br> | ||
+ | Besides that, the SJ2 board task is to generate random location for the virus/power-ups to spawn. In addition, the SJ2 are also responsible for detecting collision, adding / subtracting health and keeping track of the score.<br><br> | ||
+ | |||
+ | The state machine of the game can be found below:<br> | ||
+ | [[File:CoronaRunStateMachine.png]] | ||
+ | |||
+ | '''Adafruit Audio FX Sound Board''' | ||
+ | |||
+ | |||
+ | The Sound Board has a lot of amazing features If you want to control the Sound Board over UART Serial, you can do so by using the TX, RX and UG pins. | ||
+ | |||
+ | [[File:MP3-aDAFRUIT.GIF|400px| thumb|right|File:Audio_FX-aDAFRUIt]] | ||
+ | |||
+ | Specifications – | ||
+ | No Arduino or other microcontroller required .Standalone. | ||
+ | |||
+ | Operated on 3 to 5.5VDC battery. | ||
+ | |||
+ | Small - only 1.9" x 0.85" | ||
+ | |||
+ | Built in storage, don’t even need an SD card | ||
+ | |||
+ | Built in Mass Storage USB | ||
+ | |||
+ | Compressed or Uncompressed audio ,Go with compressed Ogg Vorbis files for longer audio files, or uncompressed WAV files | ||
+ | |||
+ | High Quality Sound - You want 44.1KHz 16 bit . The decoding hardware can handle any bit/sample rate and mono or stereo | ||
+ | |||
+ | |||
+ | '''LED MATRIX''' | ||
+ | |||
+ | [[File:LED MATRIX.GIF|500px| thumb|right|File:LED MATRIX.GIF]] | ||
+ | Brightness: 2800cd/square meter | ||
+ | |||
+ | Size: 160x160mm | ||
+ | |||
+ | Pitch: 2.5M | ||
+ | |||
+ | 5 Addressable Pins | ||
+ | |||
+ | Compatible with M3 mounting screws | ||
+ | |||
+ | Scan: 1/32 | ||
+ | |||
+ | Refresh Frequency: >=400HZ | ||
+ | |||
+ | Waterproof Class: IP43 | ||
+ | |||
+ | Weight: 204.8g (only matrix panel). 255g (w/ components) | ||
+ | |||
+ | Best for indoor use | ||
=== Hardware Interface === | === Hardware Interface === | ||
− | + | ||
+ | '''Our Approach to LED Matrix:''' | ||
+ | LED display Matrix is divided into two sections | ||
+ | |||
+ | Section 1 - First 32 rows (Row 0 to row 31) | ||
+ | Section 2 - Remaining 32 rows (Row 32 to row 63) | ||
+ | |||
+ | There are 6 data lines, 3 for each section are available to drive the RGB matrix. | ||
+ | |||
+ | Section 1: R1, G1, B1 | ||
+ | |||
+ | Section 2. R2, G2 , B2 | ||
+ | |||
+ | Other pins: | ||
+ | |||
+ | OE - Output Enable | ||
+ | |||
+ | Lat - Latch | ||
+ | |||
+ | Clk - clock signal | ||
+ | |||
+ | Vcc - High Voltage (5V) | ||
+ | |||
+ | Gnd - Ground (0V) | ||
+ | |||
+ | [[File:FIG-Intro to LED Matrix.GIF|575px| thumb|right|Introduction to LED ]] | ||
+ | |||
+ | |||
+ | The RGB LED Display Matrix gets the input from Master Module which is our SJTwo board. It is driven by the GPIO Pins of the Master Module. However, these GPIO pins are not connected directly to the display matrix since 64x64 RGB LED Matrix drives on 5V input whereas the GPIO pins from SJTwo board output 3.3V. So, two level shifter ICs are used as an intermediate connection to convert 3.3V GPIO output from SJTwo board to 5V before giving it as an input to the RGB Led Matrix. Each LED can be independently addressed and controlled. This 64x64 LED matrix has 5 address lines viz. A, B, C, D, E. With 5 address lines, we get 2^5 = 32 unique addresses. But, the matrix has 64 rows. The scan rate of the LED Matrix is 1/32, i.e., 2/64. This indicates that by making each address line high, two rows will be driven at the same time. As shown R1,G1,B1 handles the section 1 of the LED Matrix and R2,G2,B2 handles the section 2 of the matrix. | ||
+ | |||
+ | {| class="wikitable" style="text-align: center; width: 400px; height: 200px;" | ||
+ | |+ LED MATRIX | ||
+ | |- | ||
+ | ! Sr. No. | ||
+ | ! SJTwo board Pin | ||
+ | ! LED MATRIX | ||
+ | ! Function | ||
+ | |- | ||
+ | ! 1 | ||
+ | | P1_31 || R1 || Upper half (Section 1) | ||
+ | |- | ||
+ | ! 2 | ||
+ | | P1_30 || G1 || Upper half (Section 1) | ||
+ | |- | ||
+ | ! 3 | ||
+ | | P1_23 || B1 || Upper half (Section 1) | ||
+ | |- | ||
+ | ! 4 | ||
+ | | P1_28 || R2|| Lower half (Section 2) | ||
+ | |- | ||
+ | ! 5 | ||
+ | | P1_29 || G2 || Lower half (Section 2) | ||
+ | |- | ||
+ | ! 6 | ||
+ | | P2_0 || B2 || Lower half (Section 2) | ||
+ | |- | ||
+ | ! 7 | ||
+ | | P2_2 || A || Address Line | ||
+ | |- | ||
+ | ! 8 | ||
+ | | P2_4 || B || Address Line | ||
+ | |- | ||
+ | ! 9 | ||
+ | | P2_5 || C || Address Line | ||
+ | |- | ||
+ | ! 10 | ||
+ | | P2_6 || D || Address Line | ||
+ | |- | ||
+ | ! 11 | ||
+ | | P2_0 || E || Address Line | ||
+ | |- | ||
+ | ! 12 | ||
+ | | P2_7 || Clk || For upper half and lower half | ||
+ | |- | ||
+ | ! 13 | ||
+ | | P2_8 ||Latch|| For upper half and lower half | ||
+ | |- | ||
+ | ! 14 | ||
+ | | P2_9 || OE|| For upper half and lower half | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | |||
+ | |||
+ | The row contains 5:32 decoder as shown in the figure .Here the rows are selected in parallel so if row 1 starts functioning then simultaneously 33 will start .There are five address which are marked as A,B,C,D,E ,only one input is active low at time .The pseudo code is presented below to give broad description how the row can be operated on LED matrix . | ||
+ | |||
+ | |||
+ | The column data is stored in a 64-bit serial-in, a parallel-out shift register. The shift register is designed to work with LEDs and implements a constant-current system that ensures the LED brightness remains uniform.R1,G1,B1 is for section 1 32x64 and R2,G2,B2 is for section 2 .There is single clock interfaced between them. Once the clocking and shifting the register is completed . We need to latch this data to the register. The register data is sent out to all the row lines and that Row line which is pulled low by the decoder will receive this data and corresponding pixels are turned on. The pseudo code is presented below to give broad description how the column can be operated on LED matrix . | ||
+ | |||
+ | |||
+ | <table> | ||
+ | <tr> | ||
+ | <td> | ||
+ | [[File:Approach to Row and Col.GIF|575x375px| thumb| left |Understanding ROW AND COL]] | ||
+ | </td> | ||
+ | <td> | ||
+ | |||
+ | <td> | ||
+ | [[File:Row and Col.GIF|700x600px| thumb| left |Understanding ROW AND COL]] | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | |||
+ | '''Working of Accelerometer''' | ||
+ | |||
+ | The accelerometer makes use of transient detection channel of motion detection. Using this, we can set thresholds for the sensor to generate an interrupt.This is preferable to continually reading sensor values and trying to mimic the same thing in software on the SJ2. This saves CPU cycles for more intensive tasks.The interrupt pins unfortunately are not broken out on our board, so we must continually read the interrupt register.To configure the registers needed for the transient detection, we need to add some code to the acceleration.c file. The registers we need to set are below: | ||
+ | |||
+ | TRANSIENT_CFG = 0x12 | This register tells the board to latch the interrupt to the src register and enables this for the X axis. | ||
+ | |||
+ | TRANSIENT_THS = 0x08 | This value in the ths register sets the transient threshold to a half g. This is the parameter you would adjust for sensitivity. | ||
+ | |||
+ | TRANSIENT_TC = 50ms = 0x05 | writing this value to the tc register will debounce the signal by 50ms. Basically the board will not generate another interrupt within | ||
+ | 50ms. This parameter should also be adjusted to remove the stopping reaction force from the input. This value I believe should be half the "sampling rate" of reading | ||
+ | the interrupt src register. | ||
+ | |||
+ | TRANSIENT_CR4 = (1 << 5) | This value is the last register that needs to be set is to enable the transient detection feature. After this, the sensor can be brought up | ||
+ | into active mode | ||
+ | |||
+ | |||
+ | |||
+ | To read the interrupt there is a function that performs a single read of the src register to see if there is an interrupt present. Reading this register clears the interrupt and the sensor continues. The src register is returned to the task that read it. The register contains the interrupt flag at bit 6 and if that bit is set, we look at bit 0 to determine the direction of the half g force that caused the interrupt. Depending on the value, a boolean is sent to a queue from the task. | ||
+ | |||
+ | |||
+ | The consumer of the queue reads this boolean and makes a decision based on it. In our case, moving the player. | ||
+ | |||
+ | '''Our Approach to Sound Effects:''' | ||
+ | Below is pin connection table of Audio FX and SJTWO board. | ||
+ | |||
+ | '''PINS USED''' | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! SJ2 Pins | ||
+ | ! Audio FX Pins | ||
+ | ! Description | ||
+ | |- | ||
+ | ! P0.8 | ||
+ | | RST | ||
+ | | RST pin is used to reset the audio FX board. This is connected to P0.8 that is set as GPIO to send signal high / low to RST | ||
+ | |- | ||
+ | ! P4.28 | ||
+ | | RX | ||
+ | | RX pin functions as the receiver for the UART communication. Since the audio FX board only receives signals from the SJ2 board, therefore, only RX pin needs to be connected to TX pin of the SJ2 board | ||
+ | |- | ||
+ | ! GND | ||
+ | | GND | ||
+ | | GND pin of the audio FX board is grounded on the SJ2 board | ||
+ | |- | ||
+ | ! GND | ||
+ | | UG | ||
+ | | UG pin determines whether the audio FX board is operating under GPIO mode or UART mode. Since this pin is connected to GND, the audio FX board will operate in UART mode | ||
+ | |- | ||
+ | ! 3.3 V | ||
+ | | Vin | ||
+ | | Vin pin is used to receive power to power up the audio FX board from the outside source. The range of the input power allowed is 3 - 5.5 V | ||
+ | |- | ||
+ | |} | ||
+ | The sound fx board by Adafruit makes it easy to add sound effects to your project. This board is able to play up to 16MB .ogg and .wav files that are added easily by dropping them to the onboard mass storage. The device supports two modes of playback, selectable with the UG pin.Normally nothing is connected to this pin on startup, and it initializes the device accept input on the 10 gpio trigger pins. When the pin is grounded, a file will be played depending on the file name conventions. | ||
+ | The file names start with a "T" for trigger, plus the two digit pin number. For more advanced triggering there are options that can be added to this. | ||
+ | |||
+ | Appending HOLDL will continuously loop the track while the input is grounded. | ||
+ | Appending LATCH will continuously loop the track until the input on that pin occurs again. | ||
+ | Appending NEXT plus a one digit number (0-9) will play the tracks in order. | ||
+ | Appending RAND plus a one digit number (0-9) will play the tracks randomly. | ||
+ | |||
+ | The other way to trigger is through the uart interface. To access the uart interface, the UG pin needs to be grouned on startup. | ||
+ | Through the uart interface at 9600bps, the tracks can be played as needed by using the string: | ||
+ | "P<capitalized file up to 8 chars right padded with spaces plus the extension in caps>" | ||
=== Software Design === | === Software Design === | ||
− | + | '''All about RGB LED matrix:''' | |
+ | |||
+ | RGB led matrix is made up of standard tri-color led chips- red, green and blue. For our game we are using 64*64 matrix and there is no proper documentation for this one hence we have tried to provide as much as we could in this section. | ||
+ | |||
+ | 64*64 stands for matrix having 64 rows and 64 columns, however rows are further divided in 2 sections.For upper 32 rows, each pixel has R1, G1 and B1 but for the lower half we need to pass values to R2, G2 and B2 in order to light it up. | ||
+ | |||
+ | To light up a particular led we need to keep a few things in mind. Each led is driven by 1 bit of shift register and there are several shift registers which are daisy chained to each other. This allows us to drive all leds of a row one by one by clocking in R1, G1, B1 or R2, G2, B2 in accordance to the position of led. To understand this, we first need to understand how a shift register works and this will help us figure out the pins we need to set low or high and at what instance (remember this is something that the matrix consists of and not on the surface for us to control except for understanding latch and clock pins). | ||
+ | |||
+ | Shift registers are the modules that allow us to control a large number of input or output by using only a few pins of the microcontroller. It basically can hold the register of bits in memory and the bits are shifted one at a time and hence the name shift register.The advantage is that we can connect as many shift registers as we need the output lines/leds to be and 1 shift register will use the same amount of pins as 50 shift registers would. Daisy chaining will involve shifting the data from one shift register to the next one. | ||
+ | |||
+ | Say, you have 8 leds at the output of the shift register and you intend to set and reset as such that only first and fourth led lights up and all the others are 0. You can do so by using latch, data and clock pins. So you will start by clocking the 0 first through shift register and it gets saved at eighth led. By clocking, I mean we need to set and reset the clock to let the hardware know that a new data is going to come out as output. And so every time we are sending a bit, we need to provide the clock signal. Next u need to push a 1(for 1st led to light up) and this will shift the 0 at 8th position to 7th led and hence the 7th led is 0 and the 8th is set or 1 . So this repeats and the bits are shifted each time you output from the shift register. Once all the bits are shifted ;i.e you have all 8 bits; we will enable the latch and this will send all the bits to output at once. So, we move data in serially and once all the data is gathered we latch it out all together; as in parallely moving the data out. The led number 1 and 4 wont turn on before we latch the bits out. | ||
+ | |||
+ | Now relating this to our 64*64 matrix, we have 64 columns for a single row and they contain shift registers. For a row, we need to drive these leds one by one by clocking R1, G1, B1 or R2, G2 and B2 based on if the row is >32 or row <33.After all the 64 columns are clocked in by color values, we will enable the latch and hence this will turn that particular row’s leds on or off based on what we passed as color bits. | ||
+ | |||
+ | Coming to rows back, the RGB matrix has 5:32 decoder and hence it takes 5 bits as input as in A, B, C, D and E and the resulting address will select a row. This will select 1 row from each section, that means when we select 1, we also select 33, similarly 2 and 34 are selected at the same time and so on. | ||
+ | |||
+ | '''LED Matrix driver:''' | ||
+ | |||
+ | As per the above discussion, we now have a clear idea of what pins need to be reset and set in order to get the matrix working. Here are the steps to be followed to get led driver : | ||
+ | |||
+ | 1.Configure all the pins as gpio. | ||
+ | |||
+ | 2.Create a matrix buffer to store color values | ||
+ | |||
+ | 3.Set the output enable bit | ||
+ | |||
+ | 4.Check if R1, G1, B1 or R2, G2, B2 are set of any particular element of the buffer. | ||
+ | |||
+ | 5.Set or reset the color bits as per the above step. | ||
+ | |||
+ | 6.Once a column is done, as per explained above we will do the clocking. | ||
+ | |||
+ | 7.After checking all the columns of a row or completion of a row, we will disable OE and enable latch(as per explanation) | ||
+ | |||
+ | 8.Lastly we will set A,B,C,D,E to set the row. | ||
+ | |||
+ | <pre> | ||
+ | for (uint8_t row = 0; row < 32; row++) { | ||
+ | lat_clear(); | ||
+ | oe_set(); | ||
+ | for (uint8_t col = 0; col < 64; col++) { | ||
+ | if (matrixBuff[row][col] & 0x1) { | ||
+ | LPC_GPIO1->SET = R1; | ||
+ | } else { | ||
+ | LPC_GPIO1->CLR = R1; | ||
+ | } | ||
+ | if (matrixBuff[row][col] & 0x2) { | ||
+ | LPC_GPIO1->SET = G1; | ||
+ | } else { | ||
+ | LPC_GPIO1->CLR = G1; | ||
+ | } | ||
+ | if (matrixBuff[row][col] & 0x4) { | ||
+ | LPC_GPIO1->SET = B1; | ||
+ | } else { | ||
+ | LPC_GPIO1->CLR = B1; | ||
+ | } | ||
+ | if (matrixBuff2[row][col] & 0x8) { | ||
+ | LPC_GPIO1->SET = R2; | ||
+ | } else { | ||
+ | LPC_GPIO1->CLR = R2; | ||
+ | } | ||
+ | if (matrixBuff2[row][col] & 0x10) { | ||
+ | LPC_GPIO1->SET = G2; | ||
+ | } else { | ||
+ | LPC_GPIO1->CLR = G2; | ||
+ | } | ||
+ | if (matrixBuff2[row][col] & 0x20) { | ||
+ | LPC_GPIO2->SET = B2; | ||
+ | } else { | ||
+ | LPC_GPIO2->CLR = B2; | ||
+ | } | ||
+ | |||
+ | clk_set(); | ||
+ | clocking(); | ||
+ | } | ||
+ | oe_clear(); | ||
+ | lat_set(); | ||
+ | |||
+ | LPC_GPIO2->CLR = A | B | C | D | E; | ||
+ | |||
+ | if (row & 0x1) { | ||
+ | LPC_GPIO2->SET = A; | ||
+ | } | ||
+ | if (row & 0x2) { | ||
+ | LPC_GPIO2->SET = B; | ||
+ | } | ||
+ | if (row & 0x4) { | ||
+ | LPC_GPIO2->SET = C; | ||
+ | } | ||
+ | if (row & 0x8) { | ||
+ | LPC_GPIO2->SET = D; | ||
+ | } | ||
+ | if (row & 0x10) { | ||
+ | LPC_GPIO2->SET = E; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | <br> | ||
+ | In the driver code, we utilized 2 separate matrix buffers. This was done to make the moving animation on the main menu screen. By having two separate matrix buffers, we were able to only make the top half of the screen moving without interfering the lower half of the matrix. | ||
=== Implementation === | === Implementation === | ||
− | This section | + | |
+ | '''Game development stages:''' | ||
+ | |||
+ | '''A.Initial development:''' | ||
+ | |||
+ | The initial section contains information of main screen where the title of project and basic animation and feature of rotating run is added . The idea was to divide the 64x64 led matrix into two sections . As seen the upper section is divided into 32x64 and lower half section is divided into 32x64 . In terms of programming two buffer are taken: | ||
+ | This buffer is to deal with upper half of section : | ||
+ | matrixBuff[row][col] | ||
+ | The second buffer to deal with lower half of the section : | ||
+ | matrixBuff2[row][col]. | ||
+ | The first half section as seen contains CORONA RUN in display of led matrix whereas the other half display animation like human standing wearing mask ,corona virus and Must wear a mask . | ||
+ | |||
+ | void coronaMainMenuScreen(void) { | ||
+ | for (int row = 0; row < 14; row++) | ||
+ | for (int col = 0; col < 64; col++) | ||
+ | matrixBuff[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col]; | ||
+ | |||
+ | for (int row = 0; row < 14; row++) { | ||
+ | for (int col = 0; col < 64; col++) { | ||
+ | if ((col - 1) < 0) { | ||
+ | matrixBuff[row][63] = matrixBuff2[row][col]; | ||
+ | } else { | ||
+ | matrixBuff[row][col - 1] = matrixBuff2[row][col]; | ||
+ | } | ||
+ | } | ||
+ | delay__ms(5); | ||
+ | } | ||
+ | } | ||
+ | if (load_matrixbuff_done) { | ||
+ | for (int row = 15; row < 32; row++) | ||
+ | for (int col = 0; col < 64; col++) { | ||
+ | if ((col - 1) < 0) { | ||
+ | matrixBuff[row][63] = matrixBuff[row][col]; | ||
+ | } else { | ||
+ | matrixBuff[row][col - 1] = matrixBuff[row][col]; | ||
+ | } | ||
+ | } | ||
+ | delay__ms(2); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | void runMainMenuScreen(void) { | ||
+ | if (!load_matrixbuff_done) { | ||
+ | for (int row = 15; row < 32; row++) | ||
+ | for (int col = 0; col < 64; col++) | ||
+ | matrixBuff[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col]; | ||
+ | } | ||
+ | |||
+ | |||
+ | void animationMainMenuScreen(void) { | ||
+ | for (int row = 0; row < 32; row++) | ||
+ | for (int col = 0; col < 64; col++) | ||
+ | matrixBuff2[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col]; | ||
+ | } | ||
+ | |||
+ | |||
+ | '''B.Intermediate development:''' | ||
+ | |||
+ | This subsection will describe the game characters and basic movement. To get a character on screen we have implemented a function that can draw a pixel or basically turn just one led on as per the row and column value. | ||
+ | <pre> | ||
+ | |||
+ | |||
+ | void draw_pixel(uint8_t row, uint8_t col, eight_color_t color) { | ||
+ | if (col > 31) { | ||
+ | matrixBuff2[col - 32][row] = (matrixBuff2[col - 32][row] & 0X07) | (color << 3); | ||
+ | |||
+ | } else { | ||
+ | matrixBuff[col][row] = (matrixBuff[col][row] & 0X38) | color; | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | Now to draw a character or pattern on the matrix, we call this draw_pixel() function multiple times such that it turns the leds on in a pattern by passing row, column and color values as we need. Our team went through a tough time to decide how the game characters will look like. Although not big of an issue, we had to make sure that the gamer didn't have to struggle to see or to understand the orientation of the game.We drew the main player when its alive, when he is dead (when it loses all the three lives and gets hit by the virus), the virus, a sanitizer to boost life up, a mask for easter egg and the game track and score system display; all by using draw_pixel function. Below are some images and gifs to show how they looked the very first time they were displayed. | ||
+ | |||
+ | |||
+ | <table> | ||
+ | <tr> | ||
+ | <td> | ||
+ | [[File:20201202 161234 1.gif |275x275px| thumb| center |main screen]] | ||
+ | </td> | ||
+ | <td> | ||
+ | [[File:Countdown.gif|400x300px| thumb| center |countdown]] | ||
+ | <td> | ||
+ | [[File:Basic mov gif.gif |400x300px| thumb| center |Initial Basic movement]] | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
+ | |||
+ | '''C. Advance development :''' | ||
+ | |||
+ | In this stage, we combine every animation into the track. The character animation will always be visible, but the sanitizer animation will be rare since it provides health and extra score. The virus will always appear after it disappears. To make the game harder, we made the sanitizer to have only 5% chance of spawn. The mask, on the other hand, is an easter egg that can only be triggered when a certain requirement is met. In our game, the requirement to trigger the easter egg is to not grab the sanitizer, which will cause a mask to appear when score is 10.<br> | ||
+ | After making everything appear on the matrix, then we proceed on making sure that the sanitizer and the virus will not appear in the same lane. This was pretty much achieved by checking if the starting spawn points of the two items are not equal.<br> | ||
+ | After this step, we developed the collision detection in order to see whether the player would die when the virus touches the character. At this stage, the health was only set to 1 because we wanted to see the effect as soon as possible. During collision detection, we were facing technical issues in detecting the collision.<br> | ||
+ | After we figured out the collision detection, we started working on the health system. We decided to save 6 pixels on the top of the matrix to display health and score. The team decided to have 3 health for the player and each virus hit will cost the player one health.<br> | ||
+ | To work on the difficulty of the game, we designed the speed to decrease (faster) after every virus has re-spawned by 5. However, if the easter egg is grabbed, then the speed of the game will be increased by 50 and the speed decreasing rate will be changed to 1. This will make the game easier and allow the player to obtain higher score. <br> | ||
+ | Then the team decided to work on the scoring system, which is maxed at 999. If the player reaches 999, then it will trigger the next state of the game, which is the game over state to let the player know that it has reached the maximum score. <br> | ||
+ | |||
+ | <table> | ||
+ | <tr> | ||
+ | <td> | ||
+ | [[File:Corona_run_Easter_egg.gif |275x275px| thumb| center| Mask Easter Egg]] | ||
+ | </td><td> | ||
+ | [[File:Corona_Run_Virus_collision.gif |275x275px| thumb| center| Virus Collision]] | ||
+ | </td><td> | ||
+ | [[File:Corona_run_Sanitizer_colision.gif |275x275px| thumb| center| Sanitizer Power Up Collision]] | ||
+ | </td> | ||
+ | </tr> | ||
+ | </table> | ||
== Testing & Technical Challenges == | == Testing & Technical Challenges == | ||
− | |||
− | |||
− | |||
− | === < | + | === <font size=4> '''TECHNICAL CHALLENGES''' </font> === |
− | + | '''1. Wires Issues'''<br> | |
+ | When the team first received the LED matrix, the team was struggling to find out the problem. However, after some time, the team realized that ''OE'' wire was not working properly, which resulted in the team getting new jumper wires to solve this problem. Every time the LED matrix starts to glitch out, make sure that the wire is functioning properly.<br> | ||
+ | '''2. Audio FX Issues'''<br> | ||
+ | When working with audio FX board, the team encountered a problem where the audio would not play even after the reset pin has been asserted to enable UART mode. To solve this, the team only unplugged and plugged the usb cable to the SJ2 board, then it worked.<br> | ||
+ | '''3. Collision Detection Issues'''<br> | ||
+ | During writing collision detection code, the team encountered an issue where the collision would have been detected every time. This problem occurred because in the draw_pixel(), the row and col were switched. | ||
+ | |||
+ | === <font size=4> '''BUGS''' </font> === | ||
+ | '''1. Collision Detection Bug'''<br> | ||
+ | During development, the team encountered a bug where a collision would be detected even though the virus was still in the lower half of the matrix, | ||
+ | not touching the player.<br> | ||
+ | Resolution: This bug was resolved by doing collision detection only after the virus gets into the top half of the matrix.<br> | ||
+ | '''2. Accelerometer Bug'''<br> | ||
+ | During development, the team encountered a bug where the player movement using accelerometer would not be in one of the three positions that | ||
+ | the team had coded.<br> | ||
+ | Resolution: This bug was caused by the variable type of player_position. Initially it was uint8_t, therefore, if the position is at 0, the next position | ||
+ | will not be -1, but it will be some positive values because its uint8_t. The team fixed this issue by setting the variable type to int.<br> | ||
+ | '''3. Mask Power Up Bug'''<br> | ||
+ | There was a bug regarding an easter egg power-up (mask) where the mask would appear out of bounds. This was cause because the team tried to | ||
+ | spawn the mask power up at a location that is different from virus or sanitizer.<br> | ||
+ | To fix this bug, the team decided that the easter egg must be triggered by a certain condition in order to spawn mask replacing sanitizer | ||
+ | at a specified score.<br> | ||
+ | '''4. Collision Bug'''<br> | ||
+ | There was a bug regarding collision with items/virus where the pixels of the character would go dark for a really short period of time after | ||
+ | touching virus/items. This bug was noticable when the speed of the game was still slow. However, as the speed goes faster, this bug could not be | ||
+ | found.<br> | ||
== Conclusion == | == Conclusion == | ||
− | + | ||
+ | Overall, this project is a success because we successfully implemented the idea of "Minion Rush" game onto the LED matrix with an easter egg. The primary problem that the team encountered was mostly on collision detection where the game would detect a collision even though there was no collision occurred. <br> | ||
+ | From this project, the team learned so much stuff and gained so much knowledge. Not only do the members know how to write the driver for LED matrix, but also everyone on the team learned how graphics work. From this project as well, the team learned how to implement collision detection in developing a game. In addition, the team also learned how to use sensors to control the movement in a game, which gives more options for the projects in the future.<br> | ||
+ | <br> | ||
+ | There are some rooms for improvements that can be implemented in the future for this game, such as:<br> | ||
+ | '''1. Spawn more enemies at max of 2 lanes'''<br> | ||
+ | The game can be further improved by spawning more enemies at different lanes with the maximum of 2 enemies. This will make the game even | ||
+ | more difficult and challenging to play, but almost impossible to win<br> | ||
+ | '''2. Asynchronous spawn'''<br> | ||
+ | The game will be even more challenging if the virus and the sanitizer do not spawn at the same time. This was under consideration during | ||
+ | development, but it was never implemented because the game itself was already impossible to beat with synchronous spawn time.<br> | ||
+ | '''3. Make Bigger/Longer Tracks'''<br> | ||
+ | The game will be easier if the track is longer. However, to do this, two LED matrixs will be needed. During development, the team only had | ||
+ | 1 LED Matrix, therefore, the track was smaller. In the future release, the game could be bigger by making the track longer.<br> | ||
+ | '''4. Use Joystick instead of accelerometer'''<br> | ||
+ | The game is harder to control with accelerometer. In the future release, the player could be given an option to use accelerometer or button | ||
+ | to move the player.<br> | ||
=== Project Video === | === Project Video === | ||
− | + | [https://youtu.be/36A5fxBiR34 Cute Corona Run] | |
=== Project Source Code === | === Project Source Code === | ||
− | + | [https://gitlab.com/william.asper/corona_run/-/tree/Corona_Run_Project Cute Source Code] | |
== References == | == References == | ||
=== Acknowledgement === | === Acknowledgement === | ||
− | + | ||
+ | We would like to first thank Professor Preetpal Kang for providing us with the knowledge and skills that were used to develop this game. Without Prof. Kang, the team would have encountered so many issues and would not be able to expert freeRTOS. Secondly, we would like to thank Adafruit for providing us with 64x64 LED Matrix and the Audio FX board. We would also like to thank the creator of SJ2 board that we use for this game as the main controller. Lastly, we would like to thank Audacity that helped us in converting sound effects of the game from mp3 to Ogg. Thank you! | ||
=== References Used === | === References Used === | ||
− | + | [http://books.socialledge.com/books/embedded-drivers-real-time-operating-systems/page/sj2-board SJ2 Board]<br> | |
− | + | [https://www.nxp.com/docs/en/user-guide/UM10562.pdf LPC408x/407x User Manual]<br> | |
− | + | [https://cdn.sparkfun.com/datasheets/Sensors/Accelerometers/MMA8452Q-rev8.1.pdf MMA8452Q datasheet (Sparkfun)]<br> | |
− | + | [https://learn.adafruit.com/adafruit-audio-fx-sound-board/pinouts Adafruit Sound FX Sound Board pinouts]<br> | |
+ | [https://learn.adafruit.com/microcontroller-compatible-audio-file-conversion Converting sound files with Audacity]<br> | ||
+ | [https://www.mediawiki.org/wiki/Help:Images Wiki formatting] |
Latest revision as of 07:57, 18 December 2020
Contents
Corona Run
Abstract
CORONA Run is a game designed for every one (Not PG-13 Rated) to have a good time during this pandemic. Inspired by the current real-life pandemic situation and the game "Minion Rush", the main objective of this game is to avoid CORONA virus. As the game progresses, the player will encounter not only the virus, but also power-ups that provide the player with temporary immunity to the virus. The score of the player will depend on how many viruses the player can avoid. To achieve so, we had to design driver for 64*64 Led matrix and interface it with SJTWO board. Sound effects were given using audio Fx board. The game utilizes accelerometer sensor as the controller to make the game more compact and simple to play. The only buttons that the player uses will be the START and PLAY AGAIN button.
Objectives & Introduction
The main objective of this project is to use SJ2 board as the center of our project. To create a properly working game, the game must be able to:
- Detect collision with CORONA virus to reduce life
- Provide the player with power-ups when the player touches it
- Keep track of the score
There are some requirements that must be used when creating the game, such as:
- Utilizing FreeRTOS to run the game on SJ2 Board
- Utilizing accelerometer to control the game
How to play?
Player will start by being in one of the 3 tracks and can move when needed.Player position is controlled by on-board accelerometer so the gamer can move the player as per their wish. Corona viruses will approach continuously towards the player and the position of virus is randomized. Each time the player avoids a virus, 1 point is added to the score. To add life, we have sanitizers and they are less frequent than the virus on screen. In order to gain immunity the player can gain sanitizer and this will also result in 3 points being added to score. The maximum life is three and after that if the player hits the virus the game ends. The game starts with a very basic speed and with time the speed of viruses increases periodically which adds difficulty to the game. We also have a easter egg feature in which player has to get at least 10 points without hitting virus and sanitizer and once they succeed, a mask will spawn. If the player grabs the mask, 30 points are added to the score. Player needs to gain 999 score to win this game.
Team Members & Responsibilities
Name | Roles / Responsibilities |
---|---|
Suryanto Phienanda |
|
Ellis Makwana |
|
Anee Dudhia |
|
William Asper |
|
Schedule
Week | Date | Task | Actual |
---|---|---|---|
1 | 09/16 |
|
|
2 | 09/18 |
|
|
3 | 09/26 | Project Proposal Submission | Complete |
4 | 10/13 |
|
|
5 | 10/19 |
|
|
6 | 10/23 |
|
|
7 | 10/25 |
|
|
8 | 10/30 |
|
|
9 | 11/1 - 11/7 |
|
|
10 | 11/8 - 11/14 |
|
|
11 | 11/15 - 11/28 |
|
|
12 | 11/29 - 12/5 |
|
|
13 | 12/6 - 12/12 |
|
|
14 | 12/16 |
|
|
Parts List & Cost
Parts | Costs |
---|---|
SJ2 Board | $ 50.00 |
5 V Power Supply Module | $ 6.99 |
Audio FX Sound Board | $ 24.95 |
JBL Go Speaker | $ 29.99 |
LED Matrix | $ 71.40 |
Jumper wires1 Jumper wires2 | $ 11.24 |
Total | $ 194.57 |
P.S: Some prices included shipping
Design & Implementation
Hardware Design
The following is block diagram which shows the connection of the board to LED matrix and Audio FX.
The main controller of this project is the SJ2 board. Every hardware communicates through SJ2 board.
To begin with the project, the SJ2 board must first be connected properly to the LED matrix. To make wiring easier, the team tried to mimic the pinouts of the LED matrix.
The pins are configured down and are really useful in order to send signals from accelerometer to the LED Matrix. Since the accelerometer is embedded on the SJ2 board, then there are no connections neccessary.
The idea behind this design is to basically control the player using accelerometer, which sends a signal that is received by SJ2 board. Based on the signal, the player will move to the left/right. This is controlled by SJ2 board and the SJ2 board tasks after receiving the signal is to draw character at the new location and clear the character at the old location.
Besides that, the SJ2 board task is to generate random location for the virus/power-ups to spawn. In addition, the SJ2 are also responsible for detecting collision, adding / subtracting health and keeping track of the score.
The state machine of the game can be found below:
Adafruit Audio FX Sound Board
The Sound Board has a lot of amazing features If you want to control the Sound Board over UART Serial, you can do so by using the TX, RX and UG pins.
Specifications – No Arduino or other microcontroller required .Standalone.
Operated on 3 to 5.5VDC battery.
Small - only 1.9" x 0.85"
Built in storage, don’t even need an SD card
Built in Mass Storage USB
Compressed or Uncompressed audio ,Go with compressed Ogg Vorbis files for longer audio files, or uncompressed WAV files
High Quality Sound - You want 44.1KHz 16 bit . The decoding hardware can handle any bit/sample rate and mono or stereo
LED MATRIX
Brightness: 2800cd/square meter
Size: 160x160mm
Pitch: 2.5M
5 Addressable Pins
Compatible with M3 mounting screws
Scan: 1/32
Refresh Frequency: >=400HZ
Waterproof Class: IP43
Weight: 204.8g (only matrix panel). 255g (w/ components)
Best for indoor use
Hardware Interface
Our Approach to LED Matrix: LED display Matrix is divided into two sections
Section 1 - First 32 rows (Row 0 to row 31) Section 2 - Remaining 32 rows (Row 32 to row 63)
There are 6 data lines, 3 for each section are available to drive the RGB matrix.
Section 1: R1, G1, B1
Section 2. R2, G2 , B2
Other pins:
OE - Output Enable
Lat - Latch
Clk - clock signal
Vcc - High Voltage (5V)
Gnd - Ground (0V)
The RGB LED Display Matrix gets the input from Master Module which is our SJTwo board. It is driven by the GPIO Pins of the Master Module. However, these GPIO pins are not connected directly to the display matrix since 64x64 RGB LED Matrix drives on 5V input whereas the GPIO pins from SJTwo board output 3.3V. So, two level shifter ICs are used as an intermediate connection to convert 3.3V GPIO output from SJTwo board to 5V before giving it as an input to the RGB Led Matrix. Each LED can be independently addressed and controlled. This 64x64 LED matrix has 5 address lines viz. A, B, C, D, E. With 5 address lines, we get 2^5 = 32 unique addresses. But, the matrix has 64 rows. The scan rate of the LED Matrix is 1/32, i.e., 2/64. This indicates that by making each address line high, two rows will be driven at the same time. As shown R1,G1,B1 handles the section 1 of the LED Matrix and R2,G2,B2 handles the section 2 of the matrix.
Sr. No. | SJTwo board Pin | LED MATRIX | Function |
---|---|---|---|
1 | P1_31 | R1 | Upper half (Section 1) |
2 | P1_30 | G1 | Upper half (Section 1) |
3 | P1_23 | B1 | Upper half (Section 1) |
4 | P1_28 | R2 | Lower half (Section 2) |
5 | P1_29 | G2 | Lower half (Section 2) |
6 | P2_0 | B2 | Lower half (Section 2) |
7 | P2_2 | A | Address Line |
8 | P2_4 | B | Address Line |
9 | P2_5 | C | Address Line |
10 | P2_6 | D | Address Line |
11 | P2_0 | E | Address Line |
12 | P2_7 | Clk | For upper half and lower half |
13 | P2_8 | Latch | For upper half and lower half |
14 | P2_9 | OE | For upper half and lower half |
The row contains 5:32 decoder as shown in the figure .Here the rows are selected in parallel so if row 1 starts functioning then simultaneously 33 will start .There are five address which are marked as A,B,C,D,E ,only one input is active low at time .The pseudo code is presented below to give broad description how the row can be operated on LED matrix .
The column data is stored in a 64-bit serial-in, a parallel-out shift register. The shift register is designed to work with LEDs and implements a constant-current system that ensures the LED brightness remains uniform.R1,G1,B1 is for section 1 32x64 and R2,G2,B2 is for section 2 .There is single clock interfaced between them. Once the clocking and shifting the register is completed . We need to latch this data to the register. The register data is sent out to all the row lines and that Row line which is pulled low by the decoder will receive this data and corresponding pixels are turned on. The pseudo code is presented below to give broad description how the column can be operated on LED matrix .
Working of Accelerometer
The accelerometer makes use of transient detection channel of motion detection. Using this, we can set thresholds for the sensor to generate an interrupt.This is preferable to continually reading sensor values and trying to mimic the same thing in software on the SJ2. This saves CPU cycles for more intensive tasks.The interrupt pins unfortunately are not broken out on our board, so we must continually read the interrupt register.To configure the registers needed for the transient detection, we need to add some code to the acceleration.c file. The registers we need to set are below:
TRANSIENT_CFG = 0x12 | This register tells the board to latch the interrupt to the src register and enables this for the X axis.
TRANSIENT_THS = 0x08 | This value in the ths register sets the transient threshold to a half g. This is the parameter you would adjust for sensitivity.
TRANSIENT_TC = 50ms = 0x05 | writing this value to the tc register will debounce the signal by 50ms. Basically the board will not generate another interrupt within 50ms. This parameter should also be adjusted to remove the stopping reaction force from the input. This value I believe should be half the "sampling rate" of reading the interrupt src register.
TRANSIENT_CR4 = (1 << 5) | This value is the last register that needs to be set is to enable the transient detection feature. After this, the sensor can be brought up into active mode
To read the interrupt there is a function that performs a single read of the src register to see if there is an interrupt present. Reading this register clears the interrupt and the sensor continues. The src register is returned to the task that read it. The register contains the interrupt flag at bit 6 and if that bit is set, we look at bit 0 to determine the direction of the half g force that caused the interrupt. Depending on the value, a boolean is sent to a queue from the task.
The consumer of the queue reads this boolean and makes a decision based on it. In our case, moving the player.
Our Approach to Sound Effects: Below is pin connection table of Audio FX and SJTWO board.
PINS USED
SJ2 Pins | Audio FX Pins | Description |
---|---|---|
P0.8 | RST | RST pin is used to reset the audio FX board. This is connected to P0.8 that is set as GPIO to send signal high / low to RST |
P4.28 | RX | RX pin functions as the receiver for the UART communication. Since the audio FX board only receives signals from the SJ2 board, therefore, only RX pin needs to be connected to TX pin of the SJ2 board |
GND | GND | GND pin of the audio FX board is grounded on the SJ2 board |
GND | UG | UG pin determines whether the audio FX board is operating under GPIO mode or UART mode. Since this pin is connected to GND, the audio FX board will operate in UART mode |
3.3 V | Vin | Vin pin is used to receive power to power up the audio FX board from the outside source. The range of the input power allowed is 3 - 5.5 V |
The sound fx board by Adafruit makes it easy to add sound effects to your project. This board is able to play up to 16MB .ogg and .wav files that are added easily by dropping them to the onboard mass storage. The device supports two modes of playback, selectable with the UG pin.Normally nothing is connected to this pin on startup, and it initializes the device accept input on the 10 gpio trigger pins. When the pin is grounded, a file will be played depending on the file name conventions. The file names start with a "T" for trigger, plus the two digit pin number. For more advanced triggering there are options that can be added to this.
Appending HOLDL will continuously loop the track while the input is grounded. Appending LATCH will continuously loop the track until the input on that pin occurs again. Appending NEXT plus a one digit number (0-9) will play the tracks in order. Appending RAND plus a one digit number (0-9) will play the tracks randomly.
The other way to trigger is through the uart interface. To access the uart interface, the UG pin needs to be grouned on startup. Through the uart interface at 9600bps, the tracks can be played as needed by using the string: "P<capitalized file up to 8 chars right padded with spaces plus the extension in caps>"
Software Design
All about RGB LED matrix:
RGB led matrix is made up of standard tri-color led chips- red, green and blue. For our game we are using 64*64 matrix and there is no proper documentation for this one hence we have tried to provide as much as we could in this section.
64*64 stands for matrix having 64 rows and 64 columns, however rows are further divided in 2 sections.For upper 32 rows, each pixel has R1, G1 and B1 but for the lower half we need to pass values to R2, G2 and B2 in order to light it up.
To light up a particular led we need to keep a few things in mind. Each led is driven by 1 bit of shift register and there are several shift registers which are daisy chained to each other. This allows us to drive all leds of a row one by one by clocking in R1, G1, B1 or R2, G2, B2 in accordance to the position of led. To understand this, we first need to understand how a shift register works and this will help us figure out the pins we need to set low or high and at what instance (remember this is something that the matrix consists of and not on the surface for us to control except for understanding latch and clock pins).
Shift registers are the modules that allow us to control a large number of input or output by using only a few pins of the microcontroller. It basically can hold the register of bits in memory and the bits are shifted one at a time and hence the name shift register.The advantage is that we can connect as many shift registers as we need the output lines/leds to be and 1 shift register will use the same amount of pins as 50 shift registers would. Daisy chaining will involve shifting the data from one shift register to the next one.
Say, you have 8 leds at the output of the shift register and you intend to set and reset as such that only first and fourth led lights up and all the others are 0. You can do so by using latch, data and clock pins. So you will start by clocking the 0 first through shift register and it gets saved at eighth led. By clocking, I mean we need to set and reset the clock to let the hardware know that a new data is going to come out as output. And so every time we are sending a bit, we need to provide the clock signal. Next u need to push a 1(for 1st led to light up) and this will shift the 0 at 8th position to 7th led and hence the 7th led is 0 and the 8th is set or 1 . So this repeats and the bits are shifted each time you output from the shift register. Once all the bits are shifted ;i.e you have all 8 bits; we will enable the latch and this will send all the bits to output at once. So, we move data in serially and once all the data is gathered we latch it out all together; as in parallely moving the data out. The led number 1 and 4 wont turn on before we latch the bits out.
Now relating this to our 64*64 matrix, we have 64 columns for a single row and they contain shift registers. For a row, we need to drive these leds one by one by clocking R1, G1, B1 or R2, G2 and B2 based on if the row is >32 or row <33.After all the 64 columns are clocked in by color values, we will enable the latch and hence this will turn that particular row’s leds on or off based on what we passed as color bits.
Coming to rows back, the RGB matrix has 5:32 decoder and hence it takes 5 bits as input as in A, B, C, D and E and the resulting address will select a row. This will select 1 row from each section, that means when we select 1, we also select 33, similarly 2 and 34 are selected at the same time and so on.
LED Matrix driver:
As per the above discussion, we now have a clear idea of what pins need to be reset and set in order to get the matrix working. Here are the steps to be followed to get led driver :
1.Configure all the pins as gpio.
2.Create a matrix buffer to store color values
3.Set the output enable bit
4.Check if R1, G1, B1 or R2, G2, B2 are set of any particular element of the buffer.
5.Set or reset the color bits as per the above step.
6.Once a column is done, as per explained above we will do the clocking.
7.After checking all the columns of a row or completion of a row, we will disable OE and enable latch(as per explanation)
8.Lastly we will set A,B,C,D,E to set the row.
for (uint8_t row = 0; row < 32; row++) { lat_clear(); oe_set(); for (uint8_t col = 0; col < 64; col++) { if (matrixBuff[row][col] & 0x1) { LPC_GPIO1->SET = R1; } else { LPC_GPIO1->CLR = R1; } if (matrixBuff[row][col] & 0x2) { LPC_GPIO1->SET = G1; } else { LPC_GPIO1->CLR = G1; } if (matrixBuff[row][col] & 0x4) { LPC_GPIO1->SET = B1; } else { LPC_GPIO1->CLR = B1; } if (matrixBuff2[row][col] & 0x8) { LPC_GPIO1->SET = R2; } else { LPC_GPIO1->CLR = R2; } if (matrixBuff2[row][col] & 0x10) { LPC_GPIO1->SET = G2; } else { LPC_GPIO1->CLR = G2; } if (matrixBuff2[row][col] & 0x20) { LPC_GPIO2->SET = B2; } else { LPC_GPIO2->CLR = B2; } clk_set(); clocking(); } oe_clear(); lat_set(); LPC_GPIO2->CLR = A | B | C | D | E; if (row & 0x1) { LPC_GPIO2->SET = A; } if (row & 0x2) { LPC_GPIO2->SET = B; } if (row & 0x4) { LPC_GPIO2->SET = C; } if (row & 0x8) { LPC_GPIO2->SET = D; } if (row & 0x10) { LPC_GPIO2->SET = E; } } }
In the driver code, we utilized 2 separate matrix buffers. This was done to make the moving animation on the main menu screen. By having two separate matrix buffers, we were able to only make the top half of the screen moving without interfering the lower half of the matrix.
Implementation
Game development stages:
A.Initial development:
The initial section contains information of main screen where the title of project and basic animation and feature of rotating run is added . The idea was to divide the 64x64 led matrix into two sections . As seen the upper section is divided into 32x64 and lower half section is divided into 32x64 . In terms of programming two buffer are taken: This buffer is to deal with upper half of section :
matrixBuff[row][col]
The second buffer to deal with lower half of the section :
matrixBuff2[row][col].
The first half section as seen contains CORONA RUN in display of led matrix whereas the other half display animation like human standing wearing mask ,corona virus and Must wear a mask .
void coronaMainMenuScreen(void) {
for (int row = 0; row < 14; row++) for (int col = 0; col < 64; col++) matrixBuff[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col];
for (int row = 0; row < 14; row++) { for (int col = 0; col < 64; col++) { if ((col - 1) < 0) { matrixBuff[row][63] = matrixBuff2[row][col]; } else { matrixBuff[row][col - 1] = matrixBuff2[row][col]; } } delay__ms(5); }
} if (load_matrixbuff_done) {
for (int row = 15; row < 32; row++) for (int col = 0; col < 64; col++) { if ((col - 1) < 0) { matrixBuff[row][63] = matrixBuff[row][col]; } else { matrixBuff[row][col - 1] = matrixBuff[row][col]; } } delay__ms(2); }
}
void runMainMenuScreen(void) {
if (!load_matrixbuff_done) { for (int row = 15; row < 32; row++) for (int col = 0; col < 64; col++) matrixBuff[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col]; }
void animationMainMenuScreen(void) {
for (int row = 0; row < 32; row++) for (int col = 0; col < 64; col++) matrixBuff2[row][col] = (mainMenu[row + 32][col] << 3) + mainMenu[row][col];
}
B.Intermediate development:
This subsection will describe the game characters and basic movement. To get a character on screen we have implemented a function that can draw a pixel or basically turn just one led on as per the row and column value.
void draw_pixel(uint8_t row, uint8_t col, eight_color_t color) { if (col > 31) { matrixBuff2[col - 32][row] = (matrixBuff2[col - 32][row] & 0X07) | (color << 3); } else { matrixBuff[col][row] = (matrixBuff[col][row] & 0X38) | color; } }
Now to draw a character or pattern on the matrix, we call this draw_pixel() function multiple times such that it turns the leds on in a pattern by passing row, column and color values as we need. Our team went through a tough time to decide how the game characters will look like. Although not big of an issue, we had to make sure that the gamer didn't have to struggle to see or to understand the orientation of the game.We drew the main player when its alive, when he is dead (when it loses all the three lives and gets hit by the virus), the virus, a sanitizer to boost life up, a mask for easter egg and the game track and score system display; all by using draw_pixel function. Below are some images and gifs to show how they looked the very first time they were displayed.
C. Advance development :
In this stage, we combine every animation into the track. The character animation will always be visible, but the sanitizer animation will be rare since it provides health and extra score. The virus will always appear after it disappears. To make the game harder, we made the sanitizer to have only 5% chance of spawn. The mask, on the other hand, is an easter egg that can only be triggered when a certain requirement is met. In our game, the requirement to trigger the easter egg is to not grab the sanitizer, which will cause a mask to appear when score is 10.
After making everything appear on the matrix, then we proceed on making sure that the sanitizer and the virus will not appear in the same lane. This was pretty much achieved by checking if the starting spawn points of the two items are not equal.
After this step, we developed the collision detection in order to see whether the player would die when the virus touches the character. At this stage, the health was only set to 1 because we wanted to see the effect as soon as possible. During collision detection, we were facing technical issues in detecting the collision.
After we figured out the collision detection, we started working on the health system. We decided to save 6 pixels on the top of the matrix to display health and score. The team decided to have 3 health for the player and each virus hit will cost the player one health.
To work on the difficulty of the game, we designed the speed to decrease (faster) after every virus has re-spawned by 5. However, if the easter egg is grabbed, then the speed of the game will be increased by 50 and the speed decreasing rate will be changed to 1. This will make the game easier and allow the player to obtain higher score.
Then the team decided to work on the scoring system, which is maxed at 999. If the player reaches 999, then it will trigger the next state of the game, which is the game over state to let the player know that it has reached the maximum score.
Testing & Technical Challenges
TECHNICAL CHALLENGES
1. Wires Issues
When the team first received the LED matrix, the team was struggling to find out the problem. However, after some time, the team realized that OE wire was not working properly, which resulted in the team getting new jumper wires to solve this problem. Every time the LED matrix starts to glitch out, make sure that the wire is functioning properly.
2. Audio FX Issues
When working with audio FX board, the team encountered a problem where the audio would not play even after the reset pin has been asserted to enable UART mode. To solve this, the team only unplugged and plugged the usb cable to the SJ2 board, then it worked.
3. Collision Detection Issues
During writing collision detection code, the team encountered an issue where the collision would have been detected every time. This problem occurred because in the draw_pixel(), the row and col were switched.
BUGS
1. Collision Detection Bug
During development, the team encountered a bug where a collision would be detected even though the virus was still in the lower half of the matrix,
not touching the player.
Resolution: This bug was resolved by doing collision detection only after the virus gets into the top half of the matrix.
2. Accelerometer Bug
During development, the team encountered a bug where the player movement using accelerometer would not be in one of the three positions that
the team had coded.
Resolution: This bug was caused by the variable type of player_position. Initially it was uint8_t, therefore, if the position is at 0, the next position
will not be -1, but it will be some positive values because its uint8_t. The team fixed this issue by setting the variable type to int.
3. Mask Power Up Bug
There was a bug regarding an easter egg power-up (mask) where the mask would appear out of bounds. This was cause because the team tried to
spawn the mask power up at a location that is different from virus or sanitizer.
To fix this bug, the team decided that the easter egg must be triggered by a certain condition in order to spawn mask replacing sanitizer
at a specified score.
4. Collision Bug
There was a bug regarding collision with items/virus where the pixels of the character would go dark for a really short period of time after
touching virus/items. This bug was noticable when the speed of the game was still slow. However, as the speed goes faster, this bug could not be
found.
Conclusion
Overall, this project is a success because we successfully implemented the idea of "Minion Rush" game onto the LED matrix with an easter egg. The primary problem that the team encountered was mostly on collision detection where the game would detect a collision even though there was no collision occurred.
From this project, the team learned so much stuff and gained so much knowledge. Not only do the members know how to write the driver for LED matrix, but also everyone on the team learned how graphics work. From this project as well, the team learned how to implement collision detection in developing a game. In addition, the team also learned how to use sensors to control the movement in a game, which gives more options for the projects in the future.
There are some rooms for improvements that can be implemented in the future for this game, such as:
1. Spawn more enemies at max of 2 lanes
The game can be further improved by spawning more enemies at different lanes with the maximum of 2 enemies. This will make the game even
more difficult and challenging to play, but almost impossible to win
2. Asynchronous spawn
The game will be even more challenging if the virus and the sanitizer do not spawn at the same time. This was under consideration during
development, but it was never implemented because the game itself was already impossible to beat with synchronous spawn time.
3. Make Bigger/Longer Tracks
The game will be easier if the track is longer. However, to do this, two LED matrixs will be needed. During development, the team only had
1 LED Matrix, therefore, the track was smaller. In the future release, the game could be bigger by making the track longer.
4. Use Joystick instead of accelerometer
The game is harder to control with accelerometer. In the future release, the player could be given an option to use accelerometer or button
to move the player.
Project Video
Project Source Code
References
Acknowledgement
We would like to first thank Professor Preetpal Kang for providing us with the knowledge and skills that were used to develop this game. Without Prof. Kang, the team would have encountered so many issues and would not be able to expert freeRTOS. Secondly, we would like to thank Adafruit for providing us with 64x64 LED Matrix and the Audio FX board. We would also like to thank the creator of SJ2 board that we use for this game as the main controller. Lastly, we would like to thank Audacity that helped us in converting sound effects of the game from mp3 to Ogg. Thank you!
References Used
SJ2 Board
LPC408x/407x User Manual
MMA8452Q datasheet (Sparkfun)
Adafruit Sound FX Sound Board pinouts
Converting sound files with Audacity
Wiki formatting