Guitar Hero MMMMDCCLX

Introduction:

We created our own version of Guitar Hero which can play any song that has a MIDI file by using our custom controller and UI. We are big fans of the original Guitar Hero game, but felt limited by its inability to only play a set of songs. We came up with the solution, Guitar Hero MMMMDCCLX (4760 in Roman numerals). By using MIDI files, a way to process songs through keeping track of frequencies and timing, we were able to create a game that can play any song as long as we pre-process its MIDI file.

High Level Design:

Much of our design is based on the original Guitar Hero game, but because we do not directly use any code, artwork, or sound from the game, we did not infringe upon any rights that the game has. Our game therefore complies with IEEE standards as we are not reproducing this game we are simply making our own version for fun.

We knew that we did not want to hard code a set of songs into our game, so we decided to use MIDI files which we have been told have never been used in this class before. We can process a MIDI file into notes that our game can play, so that anyone playing our game can give us a file they want to play. We would have to code in a set of songs for our demo, of course, but if someone wanted to they would indeed be able to play any song they wanted. The MIDI files use no background math, which saves us CPU time.

We needed a way to synthesize the notes being played. At first we considered using Karplus-Strong string synthesis to create the sound, which would sound close to a real instrument. This would require a lot of dynamic calculations though, so we decided to use Direct Digital Synthesis instead. Although this is a tradeoff for the sound quality, we do save memory space and CPU time. In fact, we are only using about 4 kB per song with this method so we could store around 32 songs in flash memory (we have 128 kB).

One other challenge we needed to overcome was how to time the notes on the screen. We considered saving the time of each note and what time it appeared on the screen, but did not have time to complete the overall timing aspect in the end. Instead, we found a modified solution described in the Note Timing section. This was not a trade off for the GUI, as the notes still fell on time, but it was a trade off for the sound as the timing of all of our songs are currently the same.

Program/Hardware Design:

Software:

Python Script for MIDI Files

We used Python to translate the MIDI files into a format that our program could read. MIDI (Musical Instrument Digital Interface) files are used to store song information encoded into 2 byte lines where each line can take the form of different messages telling the system how to behave. For our purposes, we only looked at the note on and note off messages because these contain the note number which we use to produce the corresponding sound from the game, and play the note on screen in the correct column. The note numbers go from 0 to 127 so by modding each number by 12 (the number of keys per octave), we could sort the notes into 5 bins to determine which color note the note should be in the game. We print the original note number and the modded note number in the following format to a .txt file that we directly cut and paste into our songs header file:

Direct Digital Synthesis and the DAC

First, we needed an array of frequencies to use for the Direct Digital Synthesis. We use frequencies that a piano plays to create an array to choose from for playing our songs. This, along with our song array are stored in a separate header file called sounds.h. At first, we wanted each song to have a separate array to choose from, however in using C we were not able to switch between the arrays appropriately. Instead, we had to combine all the songs into one array and keep track of their starting and ending points. Depending on which song the user selects, we start indexing at that certain part in the master songs array.

We use Direct Digital Synthesis (DDS) to create the music for the game. For DDS, we create a digital sine wave and then use the Digital to Analog Converter (DAC) to convert this to an analog output as sound from the speaker. To achieve this, we have a phase accumulator which is increased by one every time an Interrupt Service Routine is called. We set up Timer 2 to trigger this ISR at 50kHz. Within the ISR, we are able to produce the sound. On each call of the ISR, we set the DAC data output to be the sine table indexed at the phase accumulator values shifted by 24. We shift by 24 so that the phase accumulator value is on the scale of 0 to 256. After this, we add 2048 to this value so that the values we output to the DAC range from 0 to 4096, then OR with the DAC control bits. Finally, we send this out over the SPI to the DAC.

Button Debouncing

We had a few buttons that required debouncing. Debouncing is a way to make sure that the user actually pressed a button, and to make sure that multiple signals indicating a “press” only appear as a single long press in our program. Debouncing uses a switch statement with different states that indicate whether a button has been pressed, held down, or not pressed. We followed the pseudocode for debouncing provided by Professor Bruce Land on the course website. For each of the five note buttons and each of the limit switches, we have separate threads to keep track of their switch statements for if the button has been pressed. If a fret button is pressed, the corresponding circle at the bottom lights up with that color to indicate to the user that the button has been pressed. If a limit switch has been pressed, this sets a variable and allows the user to earn points if they are also pressing the correct fret.

Note Timing

Each note is separated by 200 ms in our implementation. This is controlled by using a PT_YIELD_TIME_msec(200 – (PT_GET_TIME() – (start time of the thread))) allowing each note to be very close to 200 ms apart. Even though the MIDI files contain timing information in them, we did not have time to implement this into our code. 200 ms is used because it produces music at approximately the right BPM for Sweet Child of Mine to sound correct, which was the original song we had running. In the future, we hope to integrate the timing information from the MIDI files to make each song sound more realistic. This would be pretty simple after we process the MIDI files to extract the timing information because we would simply replace the 200 in the yield time expression with the correct time each note should be played for.

Earning Points

To earn points, the user must be holding down the correct colored note while either strumming either up or down, all at the correct timing. The correct timing is when any part of the falling note is within the correct area at the bottom of the screen. To achieve this, we go through each row in our notes array to see if the correct button is being pressed while the falling note is within range of the circle at the bottom of the screen. The value of strumming up or strumming down must be true also. If the user is correct, the points increase by one and update at the top of the screen.

Graphics

We ran our GUI on 15 frames per second. We were able to get limited flickering with this frame rate. At first our circles for notes were larger than they currently are, but this caused some significant flickering. This is why we reduced the circle sizes to their current state, which you can see in this picture of the game play:

Figure 1: Game Play

It was somewhat difficult to get a good image of Figure 1, as you can see some lag in the image. We added white dots in the center of each note to make it easier for the user to track whether or not they pressed the fret at the correct spot.

We needed a struct to keep track of the notes falling on the screen. Below is an overview of the note struct:

Figure 2: Note struct

We create an array of notes at the beginning of the game with 20 notes in each of the 5 color columns. As we go through the song, we determine which column each note should be placed in based on the display note number in our song array. Depending on which column we need to put the note in, we find a note in the notes array in the corresponding column that is currently not being displayed and initialize it to be displayed. To initialize the note, we set its x value and display value so that it appears on the right side of the screen.We then go through the array in order, updating the x position to make the note move down the screen. If the user earns a point, the note will disappear when they earn the point. Otherwise, it will run the entire length of the screen until it goes off screen and we erase it.

We also needed to create a menu with menu options. Our menu is displayed when the program starts, and when the song is over and the game resets. The points from the last run of the game are displayed on the screen, so that when it first starts there are 0 points on the screen. See Figure 3 below for this design:

Figure 3: Home Screen

To get to the next screen, the user can press green the green fret button. This will take the user to a list of songs to choose from. To scroll through the songs, the user can use the strummer button which will correspondingly highlight a song in red. The user can press green to select the song they want which will take them to the next screen. See Figure 4 below for the song selection design:

Figure 4: Song Selection

The next screen, shown in Figure 5 below, displays the difficulty levels. The user can scroll through the difficulty levels to select one by using the green button, which will start the song and go to the Game Play screen. If the user decides they want to choose a different song to shred on, they can press the red button to go back and select a different song. When selecting a difficulty, the user is really selecting  how many notes will be displayed on screen during the game. We mod the note number by either 1, 4, or 8 and check whether the result is 0 to determine whether or not to display the note. 1 is for hard because any number modulo 1 is 0 so each note that is heard is also displayed on the screen, and 8 is for easy because it displays every eighth note. Regardless of the difficulty, each note is played through the DAC so that the entire song is played, but the user will only have to play the notes according to the difficulty value.

Figure 5: Difficulty Selection

State Machine:

We used state machines not only to debounce, but to store information for the menu. We use them for the difficulty, menu screen, and song. For difficulty, we had three states with easy, medium, and difficult. For menu screen, we have the main menu, songs menu, and difficulty menu. Lastly for the songs, we have our four songs that can be played. State Machines were extremely helpful because they allowed us to modify global variables that allowed us to display the correct menu on the screen, indicate to the user which option they were selecting, and change the settings for the other threads such as the difficulty and song variables to set how many notes the user would have to play, and for which song.

Hardware:

Button Circuit
Figure 6: Button Protoboard

For our note circuit, we used 5 push buttons from SparkFun and soldered them on a proto board. See Appendix B for the schematic of the hardware.

Strummer

We wanted to be able to recreate the classic strummer from the Guitar Hero game. This meant we needed some sort of rocker that rocks between two switches to indicate a strum up or a strum down. Bruce gave us two limit switches, which you can press up and down with a lever. We designed and 3D printed a chassis for the switches with the rocker to go on top. This is the final printed design:

Figures 7 & 8: 3D Printed Strummer

Controller

To create the controller, we laser cut cardboard in the shape of a guitar. We laser cut 4 layers for structural integrity, and used hot glue to connect the pieces together. We were going to use acrylic for the guitar base, but this would put us over the budget. We cut a hole to put the strummer in. We then used heat shrink to cover the wires and make one long wire for both the buttons and the strummer. This was soldered to a small proto board which was used to plug into the breadboard when the user wanted to play. This can be seen in Figure 9 below.

Figures 9: Final Controller

Results

We found that our game was convincing and quite effective in terms of the number of songs we could store. By representing each note as only two ints, we found that each song was about 4 kB so we would be able to store around 32 songs. In addition to this, we were able to realize a difficulty selection system that only required a single int and a few clock cycles to calculate a modulo each 200 ms. By spending a significant amount of time working on the menu button interactions, we ended up with a menu system that was very easy to navigate. Holding down the red or green buttons would only count as a single button click meaning that you wouldn’t advance multiple screens accidentally by clicking too slowly. Similarly for the rocker we made sure that the cursor always could be moved with a single click.

For the game play, we were able to get 15 frames per second consistently throughout the game. Additionally, we combined the note display spawn thread with the sound generation thread so that we could guarantee the notes and sound were synced together. With our system of using bins to determine which of the five colors each note should be, we ended up with an even distribution of notes throughout the songs. As for the user inputs, we found that our method of polling the input ports for button inputs every 10 ms gave a very responsive game. When it appeared on the screen that the user had pressed the correct note and strummed at the correct time, the note would disappear and the points would increment.

Building our guitar controller:

Final demo:

Conclusions

Safety and Ethics

As far as safety is concerned, Guitar Hero MMMMDCCLX has a few, relatively minor considerations. The first is related to the physical, tactile nature of the buttons. We recognized that ergonomics plays a pivotal role in their layout and accessibility, realizing only when it was fully assembled that our design may induce slight cramping and difficulty playing the game. The buttons are spaced relatively far apart, a design decision that initially made it easier to play on a breadboard, but when mounted onboard the guitar, those with average-sized hands expressed difficulty after prolonged periods of play. Additionally the strum switch mechanism consists of two limit switches. Given the limited testing time available, we were unable to appropriately size a comfortable resistance for these switches. So, while they provide a very satisfying click as feedback, they are, at least initially, hard to strum.

Beyond the button interaction standpoint, we made use of the LCD TFT display. While a critical element of our gameplay involves moving shapes and changing colors, we made a concerted effort to minimize rapid flash-like animations, so as to reduce possible risk of inducing seizures in those prone to such events.

Such is also the case with accessibility for color-blind players. While we could have included a shape-based gameplay mode, we instead opted for a simpler layout of fret buttons corresponding directly to their on-screen order.

Our music in our program, while based upon commercially written titles, is synthesized from royalty-free MIDI files.

No radio communications were used in this project. As far as we know, no other regulatory body or other government regulations apply to this venture.

Our game reached our expectations by the end of the project. We were hoping to make a game that played and looked like Guitar Hero by using MIDI files and DDS for music generation. In the end, we ended up with a game that checked off all these boxes. It is fun and easy to play, has a start menu, can play multiple songs, and can play songs that are pretty recognizable through DDS.

In terms of meeting applicable standards, our game was very responsive to user inputs and was simple to play. The threads responsible for button inputs ran once every 10 ms which is significantly faster than a human can detect any latency. Additionally, our life-size guitar controller makes playing the game more similar to the original game. Unfortunately the buttons were a bit too spaced out as we have mentioned so it was slightly harder to play with this controller than with the Guitar Hero official guitars.

Future Modifications

To modify the game in the future, we would like to correct the timing for the notes so that we use the timing aspect of the MIDI file to play the songs. This is our main fix. We would also like to make the controller more user friendly by changing the buttons. It would also be nice to display the GUI on an actual monitor rather than the TFT, which is quite hard to see, although Bruce says this would be its own separate project! We would also like to eliminate the pre-processing aspect of our game, so that the C program can interact with the Python script such that we do not have to do multiple steps to play any song on the game. These are all potential additions if we decide to continue our project as an independent study, or side project, in the future.

Intellectual Property Concerns

We did not reuse anyone’s code nor did we use any code from the public domain. We were reverse engineering Guitar Hero, the video game, but since we did not use any of their code or use any of their artwork (only the idea behind the game), we should be safe from any copyright issues. Because our project is based on a game that is vastly popular our project is not worthy of a patent. Going into the future, if we add the Karplus-Strong synthesis algorithm and add more timing functionality, our game could definitely be publishable. We did not have to sign a non-disclosure form as we did not sample any parts.

Pictures:

Schematic

Commented MIDI Converter Code

from mido import MidiFile, Message, MidiTrack, MetaMessage
filename = “songname”
mid = MidiFile(‘midi/’+filename+‘.mid’)
#new_str = “”
notes_on = “”
notes_list = “”
notes = 0
notes_mod = 0
final_notes_list = []
ones = 0
twos = 0
threes = 0
fours = 0
fives = 0
notes_list2 = []
for entries in mid.tracks[0v]:
new_str = str(entries)
#print new_str
note_onoff = new_str[0:8]
if ( note_onoff == “note_on “ ):
#notes_on = notes_on + str(entries) + ‘\n’
#print new_str[23:25]
notes_list2.append(int(new_str[23:25]))
notes_list = new_str[23:25]
notes = int(notes_list)
notes_mod = notes % 12
if ( notes_mod <= 1 ):
final_notes_list.append(1)
ones = ones + 1
elif ( notes_mod <= 5 & notes_mod >=2 ):
final_notes_list.append(2)
twos = twos + 1
elif ( notes_mod <= 7 & notes_mod >=6 ):
final_notes_list.append(3)
threes = threes + 1
elif ( notes_mod == 8 ):
final_notes_list.append(4)
fours = fours + 1
else:
final_notes_list.append(5)
fives = fives + 1
#print notes_list2
#print final_notes_list
file = open( filename+“.txt”, “w” )
for i in range(len(notes_list2)/2):
file.write(str(“{ “) + str(final_notes_list[i*2]) + str(“, “) + str(notes_list2[i*2]) + str(” }, \n))
#print str(ones) + “, ” + str(twos) + “, ” + str(threes) + “, ” + str(fours) + “, ” + str(fives)

Commented PIC32 Code

/*
* File: Guitar Hero MMMMDCCLX
* Author: Brian Dempsey, Katarina Martucci, Liam Patterson
* Target PIC: PIC32MX250F128B
* NOTE: Based on code written by Bruce Land.
*/
////////////////////////////////////
// clock AND protoThreads configure!
// You MUST check this file!
#include config.h
// threading library
#include pt_cornell_1_2.h
////////////////////////////////////
// graphics libraries
#include tft_master.h
#include tft_gfx.h
#include math.h
#include <stdlib.h>
#include sounds.h
////////////////////////////////////
// pullup/down macros for keypad
// PORT B
#define EnablePullDownB(bits) CNPUBCLR=bits; CNPDBSET=bits;
#define DisablePullDownB(bits) CNPDBCLR=bits;
#define EnablePullUpB(bits) CNPDBCLR=bits; CNPUBSET=bits;
#define DisablePullUpB(bits) CNPUBCLR=bits;
//PORT A
#define EnablePullDownA(bits) CNPUACLR=bits; CNPDASET=bits;
#define DisablePullDownA(bits) CNPDACLR=bits;
#define EnablePullUpA(bits) CNPDACLR=bits; CNPUASET=bits;
#define DisablePullUpA(bits) CNPUACLR=bits;
////////////////////////////////////
#define NINETYDEG 420
#define THIRTYDEG 500
#define NEGTHIRTYDEG 360
// DAC ISR
// A-channel, 1x, active
#define DAC_config_chan_A 0b0011000000000000
// === thread structures ============================================
// thread control structs
//print lock
static struct pt_sem print_sem ;
// note that UART input and output are threads
static struct pt pt_notehit, pt_print, pt_button1, pt_button2,
pt_button3, pt_button4, pt_button5, pt_draw, pt_time, pt_menu,
pt_spawnandsound, pt_rockerbuttondown, pt_rockerbuttonup;
// system 1 second interval tick
int sys_time_seconds ;
// variables to store whether buttons pressed
int greenPushed = 0;
int redPushed = 0;
int yellowPushed = 0;
int bluePushed = 0;
int orangePushed = 0;
// global variable telling how many points
int points = 0;
// variables to tell whether the menu is on
int menuOn = 1;
int done_with_menu = 0;
// note struct
typedef struct {
//position
int x;
int y;
// whether to display or not
int to_display;
//color of note
unsigned short color;
} note;
// maximum number of notes per row so that we don’t crash CPU
static const int num_per_row = 20;
// notes array, 5 notes for each color
note notes_array[5][20];
////////////////////////////////////
// DAC ISR
// A-channel, 1x, active
#define DAC_config_chan_A 0b0011000000000000
// B-channel, 1x, active
#define DAC_config_chan_B 0b1011000000000000
// DDS sine table
#define sine_table_size 256
int sin_table[sine_table_size];
// Frequency to Output
//static float Fout = 400.0;
// Sampling frequency
#define Fs 50000.0
#define two32 4294967296.0 // 2^32
int i;
//== Timer 2 interrupt handler ===========================================
// actual scaled DAC
volatile unsigned int DAC_data;
// the DDS units:
volatile unsigned int phase_accum_main1, phase_incr_main1=0;
// variables to help us keep track of which notes are on
int note_counter = 0, disp_note_counter = 0;
// difficulty setting
int difficulty = 4;
// enum to help us go through the menu state machine
typedef enum {
main_menu,
songs,
difficulty_menu
} menu_state;
// initialize menu_status
menu_state menu_status = main_menu;
// == Timer 2 ISR =====================================================
// just toggles a pin for timing strobe
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
phase_accum_main1 += phase_incr_main1 ;
// set DAC_to the sin_table value
DAC_data = (((sin_table[phase_accum_main1>>24])));
// === Channel A =============
// CS low to start transaction
mPORTBClearBits(BIT_4); // start transaction
// test for ready
//while (TxBufFullSPI2());
// write to spi2
WriteSPI2( DAC_config_chan_A | ( DAC_data + 2048 ));
while (SPI2STATbits.SPIBUSY); // wait for end of transaction
// CS high
mPORTBSetBits(BIT_4); // end transaction
mT2ClearIntFlag();
}
int start_music = 0;
// where the song is contained
int start_index = 0, end_index = 0;
// time thread
static PT_THREAD ( protothread_time( struct pt *pt ) )
{
// static int spawn_loop;
PT_BEGIN(pt);
// wait while the menu is on
while( menuOn ) {
PT_YIELD_TIME_msec(10);
}
// once menu is off, wait 2 seconds for the notes to reach the bottom of the screen
PT_YIELD_TIME_msec(2000);
// now start playing the music
start_music = 1;
// start time of the thread
static int start_time_time = 0;
// while the menu is not on, increment the time by 200 milliseconds
while(!menuOn) {
// start time of the thread so we can increment the time by 200 ms
start_time_time = PT_GET_TIME();
if ( !menuOn ) {
// increment
sys_time_seconds++ ;
}
// yield for exactly 200 ms
PT_YIELD_TIME_msec( 200 – ( PT_GET_TIME() – start_time_time ) );
}
PT_END(pt);
}
// numbers of notes in each row to make sure we have less than 20 in each row
int num_green = 0, num_red = 0, num_yellow = 0, num_blue = 0, num_orange = 0;
//spawn thread to get notes on screen
int freq_to_play = 0;
static PT_THREAD (protothread_spawnandsound(struct pt *pt))
{
PT_BEGIN(pt);
// loop variables
static int spawn_loop;
static int spawn_loop_i = 0;
static int spawn_loop_break;
static int start_time_spawn = 0;
while(1) {
start_time_spawn = PT_GET_TIME();
// only go into this if the menu is off
if ( !menuOn ) {
spawn_loop_break = 0;
// loop through the notes array
for ( spawn_loop_i = 0; spawn_loop_i < num_per_row && spawn_loop_break == 0; spawn_loop_i++ ) {
// check if we should be spawning a note
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && ( disp_note_counter % difficulty == 0 ) ) {
// put the note in the corresponding color and row
switch( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].color ) {
// green
case ILI9340_GREEN :
// check if the note is not currently displayed and the number of notes in this row is less than 20
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && num_green < 20 ) {
// set the to display value to 1
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display = 1;
// set the x value to 240
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].x = 240;
// break out of the loop
spawn_loop_i = num_per_row;
spawn_loop_break = 1;
// add 1 to the number of green in the row
num_green += 1;
}
break;
// red
case ILI9340_RED :
// check if the note is not currently displayed and the number of notes in this row is less than 20
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && num_red < 20 ) {
// set the to display value to 1
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display = 1;
// set the x value to 240
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].x = 240;
// break out of the loop
spawn_loop_i = num_per_row;
// add 1 to the number of red in the row
num_red += 1;
}
break;
// yellow
case ILI9340_YELLOW :
// check if the note is not currently displayed and the number of notes in this row is less than 20
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && num_yellow < 20 ) {
// set the to display value to 1
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display = 1;
// set the x value to 240
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].x = 240;
// break out of the loop
spawn_loop_i = num_per_row;
// add 1 to the number of yellow in the row
num_yellow += 1;
}
break;
// blue
case ILI9340_BLUE :
// check if the note is not currently displayed and the number of notes in this row is less than 20
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && num_blue < 20 ) {
// set the to display value to 1
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display = 1;
// set the x value to 240
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].x = 240;
// break out of the loop
spawn_loop_i = num_per_row;
// add 1 to the number of blue in the row
num_blue += 1;
}
break;
// orange
default:
// check if the note is not currently displayed and the number of notes in this row is less than 20
if ( notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display == 0 && num_orange < 20 ) {
// set the to display value to 1
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].to_display = 1;
// set the x value to 240
notes_array[SONGS[disp_note_counter][0] – 1][spawn_loop_i].x = 240;
// break out of the loop
spawn_loop_i = num_per_row;
// add 1 to the number of orange in the row
num_orange += 1;
}
break;
}
}
}
// if we should be starting the music, we give the ISR the correct value to play
// out of the DAC
if ( start_music ) {
freq_to_play = (int)freqs[SONGS[note_counter][1] – 15];
phase_incr_main1 = (int)(freq_to_play*(float)two32/Fs);
note_counter += 1;
}
// if we are at the end of the song, go here
if ( note_counter == end_index – 10 ) {
// go back to the menu and reset all the values
menuOn = 1;
done_with_menu = 0;
menu_status = main_menu;
disp_note_counter = 0;
note_counter = 0;
freq_to_play = 0; phase_incr_main1 = 0; start_music = 0;
static int inner_spawn_loop = 0;
static int spawn_loop = 0;
for(spawn_loop = 0; spawn_loop < 5; spawn_loop++ ){
for ( inner_spawn_loop = 0; inner_spawn_loop < num_per_row; inner_spawn_loop++ ) {
notes_array[spawn_loop][inner_spawn_loop].to_display = 0;
notes_array[spawn_loop][inner_spawn_loop].x = 240;
}
}
num_red = 0; num_green = 0; num_blue = 0; num_yellow = 0; num_orange = 0;
tft_fillRoundRect(0,0,400,400,1,ILI9340_BLACK);
}
else {
// increment the displayed note counter
disp_note_counter += 1;
}
}
// yield for exactly 200 msec
PT_YIELD_TIME_msec(200 – ( PT_GET_TIME() – start_time_spawn ) );
}
PT_END(pt);
}
// thread time
int time_beginning = 0;
static PT_THREAD (protothread_draw(struct pt *pt))
{
PT_BEGIN(pt);
// loop variables
static int spawn_loop, inner_spawn_loop;
while(1){
// get the time at the beginning of the thread
time_beginning = PT_GET_TIME();
// only go into this thread if the menu is off
if ( !menuOn ) {
// loop through all the notes
for(spawn_loop = 0; spawn_loop < 5; spawn_loop++ ){
for ( inner_spawn_loop = 0; inner_spawn_loop < num_per_row; inner_spawn_loop++ ) {
// if the notes are being displayed but their location is less than 10, they should be erased
if(notes_array[spawn_loop][inner_spawn_loop].to_display == 1 && notes_array[spawn_loop][inner_spawn_loop].x < 10 ) {
// set the to display value to 0
notes_array[spawn_loop][inner_spawn_loop].to_display = 0;
// erase the note
tft_fillRoundRect(notes_array[spawn_loop][inner_spawn_loop].x, notes_array[spawn_loop][inner_spawn_loop].y, 20, 20, 7, ILI9340_BLACK);
// reduce the number of that color note
switch ( notes_array[spawn_loop][inner_spawn_loop].color ) {
// green
case ILI9340_GREEN :
num_green = num_green – 1;
break;
// red
case ILI9340_RED :
num_red = num_red – 1;
break;
// yellow
case ILI9340_YELLOW :
num_yellow = num_yellow – 1;
break;
// blue
case ILI9340_BLUE :
num_blue = num_blue – 1;
break;
// orange
default:
num_orange = num_orange – 1;
break;
}
}
// simply update the location of the note on the screen
if(notes_array[spawn_loop][inner_spawn_loop].to_display == 1){
tft_fillRoundRect(notes_array[spawn_loop][inner_spawn_loop].x, notes_array[spawn_loop][inner_spawn_loop].y, 20, 20, 7, ILI9340_BLACK);
notes_array[spawn_loop][inner_spawn_loop].x = notes_array[spawn_loop][inner_spawn_loop].x8;
tft_fillRoundRect(notes_array[spawn_loop][inner_spawn_loop].x, notes_array[spawn_loop][inner_spawn_loop].y, 20, 20, 7, notes_array[spawn_loop][inner_spawn_loop].color);
tft_fillRoundRect(notes_array[spawn_loop][inner_spawn_loop].x+7, notes_array[spawn_loop][inner_spawn_loop].y+7, 6, 6, 1, ILI9340_WHITE);
}
}
}
}
// yield so we get as close to 15 FPS as possible
PT_YIELD_TIME_msec(67 – (PT_GET_TIME() – time_beginning) );
}
PT_END(pt);
}
// the value of the button we read from the I/O
int button_val;
// Different states for our pushbutton debouncing state machine
typedef enum {
NoPush,
MaybePush,
Pushed,
MaybeNoPush
} State;
// Different states for user input modifications
typedef enum {
Pterm,
Iterm,
Dterm,
Angle
} button_push_state;
////////////// Button Debouncing Threads \\\\\\\\\\\\\\
// All the threads are the same, only the first will be commented fully
State buttonState1 = NoPush;
// button 1 debouncing thread
static PT_THREAD (protothread_button1(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
// read the value of the I/O port
button_val = mPORTAReadBits( BIT_3 ) >> 3;
// debouncing switch case
switch( buttonState1 ) {
// if the button isn’t pushed
case NoPush:
// if the button is still pressed, go to MaybePush
if ( !button_val ) buttonState1 = MaybePush;
// otherwise stay at NoPush
else buttonState1 = NoPush;
break;
// if the button might be pressed
case MaybePush:
// check if the button is still pressed,
// go to Pushed and set greenPushed to 1
if ( !button_val ) {
buttonState1 = Pushed;
if ( greenPushed == 0 ) {
greenPushed = 1;
}
else greenPushed = 0;
}
// otherwise go to NoPush
else buttonState1 = NoPush;
break;
// if the button is pushed
case Pushed:
// if the button is still pressed, stay here
if ( !button_val ) buttonState1 = Pushed;
// otherwise set greenPushed to 0 and go to MaybeNoPush state
else{
greenPushed = 0;
buttonState1 = MaybeNoPush;
}
break;
// if the button might not be pressed anymore
case MaybeNoPush:
// if the button is pressed, go back to pushed state
if ( !button_val ) buttonState1 = Pushed;
// otherwise go to NoPush
else buttonState1 = NoPush;
break;
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 4
// SAME AS THE ABOVE THREAD, NOT COMMENTED
State buttonState2 = NoPush;
// button 2 debouncing thread
static PT_THREAD (protothread_button2(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
button_val = mPORTAReadBits( BIT_4 ) >> 4;
switch( buttonState2 ) {
case NoPush:
if ( !button_val ) buttonState2 = MaybePush;
else buttonState2 = NoPush;
break;
case MaybePush:
if ( !button_val ) {
buttonState2 = Pushed;
if ( redPushed == 0 ) {
redPushed = 1;
}
else redPushed = 0;
}
else buttonState2 = NoPush;
break;
case Pushed:
if ( !button_val ) buttonState2 = Pushed;
else{
redPushed = 0;
buttonState2 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( !button_val ) buttonState2 = Pushed;
else buttonState2 = NoPush;
break;
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 4
// SAME AS THE ABOVE THREAD, NOT COMMENTED
State buttonState3 = NoPush;
// button 3 debouncing thread
static PT_THREAD (protothread_button3(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
button_val = mPORTAReadBits( BIT_2 ) >> 2;
switch( buttonState3 ) {
case NoPush:
if ( !button_val ) buttonState3 = MaybePush;
else buttonState3 = NoPush;
break;
case MaybePush:
if ( !button_val ) {
buttonState3 = Pushed;
if ( yellowPushed == 0 ) {
yellowPushed = 1;
}
else yellowPushed = 0;
}
else buttonState3 = NoPush;
break;
case Pushed:
if ( !button_val ) buttonState3 = Pushed;
else{
yellowPushed = 0;
buttonState3 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( !button_val ) buttonState3 = Pushed;
else buttonState3 = NoPush;
break;
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 4
// SAME AS THE ABOVE THREAD, NOT COMMENTED
State buttonState4 = NoPush;
// button 4 debouncing thread
static PT_THREAD (protothread_button4(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
button_val = mPORTAReadBits( BIT_1 ) >> 1;
switch( buttonState4 ) {
case NoPush:
if ( !button_val ) buttonState4 = MaybePush;
else buttonState4 = NoPush;
break;
case MaybePush:
if ( !button_val ) {
buttonState4 = Pushed;
if ( bluePushed == 0 ) {
bluePushed = 1;
}
else bluePushed = 0;
}
else buttonState4 = NoPush;
break;
case Pushed:
if ( !button_val ) buttonState4 = Pushed;
else{
bluePushed = 0;
buttonState4 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( !button_val ) buttonState4 = Pushed;
else buttonState4 = NoPush;
break;
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 4
// SAME AS THE ABOVE THREAD, NOT COMMENTED
State buttonState5 = NoPush;
// button 5 debouncing thread
static PT_THREAD (protothread_button5(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
button_val = mPORTBReadBits( BIT_7 ) >> 0;
switch( buttonState5 ) {
case NoPush:
if ( !button_val ) buttonState5 = MaybePush;
else buttonState5 = NoPush;
break;
case MaybePush:
if ( !button_val ) {
buttonState5 = Pushed;
if ( orangePushed == 0 ) {
orangePushed = 1;
}
else orangePushed = 0;
}
else buttonState5 = NoPush;
break;
case Pushed:
if ( !button_val ) buttonState5 = Pushed;
else{
orangePushed = 0;
buttonState5 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( !button_val ) buttonState5 = Pushed;
else buttonState5 = NoPush;
break;
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 4
// values to tell us whether the strummer is activated
int StrummerUpOn = 0;
int Strummermenu = 0;
// same idea as the above debouncing threads but we only keep the strummer value
// high for only one cycle. Also we have a handshake value called strummermenu
// that prevents changing the value of the strummer while the menu is on. This
// makes it much easier to use the menu, but after the game has started we turn
// this off so that you can’t hold the strummer down and continue to get points
State buttonState6 = NoPush;
// strummer debouncing thread
static PT_THREAD (protothread_rockerbuttonup(struct pt *pt))
{
PT_BEGIN(pt);
static int rockerup_val = 0;
while(1) {
if ( !Strummermenu ) {
// yield time 1 second
rockerup_val = mPORTBReadBits( BIT_13 ) >> 13;
switch( buttonState6 ) {
case NoPush:
if ( rockerup_val ) buttonState6 = MaybePush;
else buttonState6 = NoPush;
break;
case MaybePush:
if ( rockerup_val ) {
buttonState6 = Pushed;
if ( StrummerUpOn == 0 ) {
StrummerUpOn = 1;
if ( menuOn ) Strummermenu = 1;
}
else StrummerUpOn = 0;
}
else buttonState6 = NoPush;
break;
case Pushed:
StrummerUpOn = 0;
if ( rockerup_val ) buttonState6 = Pushed;
else{
StrummerUpOn = 0;
buttonState6 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( rockerup_val ) buttonState6 = Pushed;
else buttonState6 = NoPush;
break;
}
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 6
// SAME AS THE THREAD ABOVE
int StrummerDownOn = 0;
State buttonState7 = NoPush;
// strummer debouncing thread
static PT_THREAD (protothread_rockerbuttondown(struct pt *pt))
{
PT_BEGIN(pt);
static int rockerdown_val = 0;
while(1) {
if ( !Strummermenu ) {
// yield time 1 second
rockerdown_val = mPORTBReadBits( BIT_8 );
switch( buttonState7 ) {
case NoPush:
if ( rockerdown_val ) buttonState7 = MaybePush;
else buttonState7 = NoPush;
break;
case MaybePush:
if ( rockerdown_val ) {
buttonState7 = Pushed;
if ( StrummerDownOn == 0 ) {
StrummerDownOn = 1;
if ( menuOn ) Strummermenu = 1;
}
else StrummerDownOn = 0;
}
else buttonState7 = NoPush;
break;
case Pushed:
StrummerDownOn = 0;
if ( rockerdown_val ) buttonState7 = Pushed;
else{
StrummerDownOn = 0;
buttonState7 = MaybeNoPush;
}
break;
case MaybeNoPush:
if ( rockerdown_val ) buttonState7 = Pushed;
else buttonState7 = NoPush;
break;
}
}
PT_YIELD_TIME_msec(10);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // thread 7
// string buffer to write
char buffer1[60];
// check note hits
static PT_THREAD (protothread_notehit(struct pt *pt))
{
// initialize variables for hitting notes
static int green_hit = 0;
static int red_hit = 0;
static int yellow_hit = 0;
static int blue_hit = 0;
static int orange_hit = 0;
PT_BEGIN(pt);
while(1) {
// only run this thread if the menu is off
if ( !menuOn ) {
// iterate through the notes in each row
for ( i = 0; i < num_per_row; i++ ) {
// check if the notes in each row are in the correct position for a point to be gained and the user
// strums, give them a point and erase the note
if( greenPushed && (notes_array[0][i].x<40 && notes_array[0][i].x>0) && ( StrummerUpOn || StrummerDownOn ) ){
points += 1;
notes_array[0][i].to_display = 0;
tft_fillRoundRect(notes_array[0][i].x, notes_array[0][i].y, 20, 20, 7, ILI9340_BLACK);
notes_array[0][i].x = 240;
num_green = num_green – 1;
}
else points += 0;
if ( redPushed && (notes_array[1][i].x<40 && notes_array[1][i].x>0 ) && ( StrummerUpOn || StrummerDownOn ) ) {
points += 1;
notes_array[1][i].to_display = 0;
tft_fillRoundRect(notes_array[1][i].x, notes_array[1][i].y, 20, 20, 7, ILI9340_BLACK);
notes_array[1][i].x = 240;
num_red = num_red – 1;
}
else points += 0;
if ( yellowPushed && (notes_array[2][i].x<40 && notes_array[2][i].x>0 ) && ( StrummerUpOn || StrummerDownOn ) ) {
points += 1;
notes_array[2][i].to_display = 0;
tft_fillRoundRect(notes_array[2][i].x, notes_array[2][i].y, 20, 20, 7, ILI9340_BLACK);
notes_array[2][i].x = 240;
num_yellow = num_yellow – 1;
}
else points += 0;
if ( bluePushed && (notes_array[3][i].x<40 && notes_array[3][i].x>0 ) && ( StrummerUpOn || StrummerDownOn )) {
points += 1;
notes_array[3][i].to_display = 0;
tft_fillRoundRect(notes_array[3][i].x, notes_array[3][i].y, 20, 20, 7, ILI9340_BLACK);
notes_array[3][i].x = 240;
num_blue = num_blue – 1;
}
else points += 0;
if ( orangePushed && (notes_array[4][i].x<40 && notes_array[4][i].x>0 ) && ( StrummerUpOn || StrummerDownOn ) ) {
points += 1;
notes_array[4][i].to_display = 0;
tft_fillRoundRect(notes_array[4][i].x, notes_array[4][i].y, 20, 20, 7, ILI9340_BLACK);
notes_array[4][i].x = 240;
num_orange = num_orange – 1;
}
else points += 0;
}
// print out the points
tft_setCursor(80, 10); tft_setTextSize(2); tft_setTextColor(ILI9340_WHITE); tft_setRotation(2);
sprintf(buffer1, Points: %d, points);
tft_writeString(buffer1);
tft_setRotation(1);
// NEVER exit while
}
PT_YIELD_TIME_msec(10);
} // END WHILE(1)
PT_END(pt);
}
// thread to print the frets at the bottom of the screen. If you are pressing
// the corresponding button on the guitar, the fret will light up
static PT_THREAD (protothread_print(struct pt *pt))
{
PT_BEGIN(pt);
while (1) {
if (!menuOn) {
tft_fillRoundRect(295,172, 15, 80, 1, ILI9340_BLACK);
tft_fillRoundRect(200, 100, 10, 10, 1, ILI9340_BLACK);
tft_fillRoundRect(18,15, 20, 20, 7, ILI9340_WHITE);
if ( greenPushed == 1 ) tft_fillRoundRect(18,15,20, 20, 7, ILI9340_GREEN); // x,y,w,h,radius,color
tft_fillRoundRect(18,63, 20, 20, 7, ILI9340_WHITE);
if ( redPushed == 1 ) tft_fillRoundRect(18,63, 20, 20, 7, ILI9340_RED);
tft_fillRoundRect(18,111, 20, 20, 7, ILI9340_WHITE);
if ( yellowPushed == 1 ) tft_fillRoundRect(18,111, 20, 20, 7, ILI9340_YELLOW);
tft_fillRoundRect(18,159, 20, 20, 7, ILI9340_WHITE);
if ( bluePushed == 1 ) tft_fillRoundRect(18,159, 20, 20, 7, ILI9340_BLUE);
tft_fillRoundRect(18,207, 20, 20, 7, ILI9340_WHITE);
if ( orangePushed == 1 ) tft_fillRoundRect(18,207, 20, 20, 7, 0xFB00);
}
PT_YIELD_TIME_msec(67);
}
PT_END(pt);
} // print thread
// enum to go with the state machine for difficulty setting
typedef enum {
easy,
medium,
hard
} difficulty_state;
// variable to help us move through the menu
int proceed = 0;
// enum to go with the state machine for songs to play
typedef enum {
Sweetchild,
Fireflies,
CliffsofDover,
Reptilia
} which_song;
// buffer to print the points
char buffer5123545[60];
// start menu
static PT_THREAD (protothread_menu(struct pt *pt))
{
PT_BEGIN(pt);
// initialize variables for difficulty and song
static difficulty_state difficulty_setting = easy;
static which_song song_to_play = Sweetchild;
while(1) {
// run the menu while we aren’t done with the menu
while(menuOn && !done_with_menu) {
// if either of the buttons is still pressed and proceed is already
// 0, don’t continue
if ( ( redPushed | greenPushed ) && ( proceed == 0 ) ) {
proceed = 0;
}
else {
// otherwise set proceed to 1 and move on
proceed = 1;
// depends on where we currently are in the menu
switch( menu_status ) {
// if in main_menu, display welcome back message and points
case main_menu:
tft_setCursor(30, 10); tft_setTextSize(2); tft_setTextColor(ILI9340_WHITE); tft_setRotation(2);
tft_writeString(Welcome back to:);
tft_setCursor(55, 30);
tft_writeString(Guitar Hero);
tft_setCursor(65, 50);
tft_writeString(MMMMDCCLX);
tft_setCursor(40,70);
tft_writeString(Previous Score:);
tft_setCursor(110,90);
sprintf(buffer5123545, %d, points);
tft_writeString(buffer5123545);
// if the user pushes green, move onto songs
if ( greenPushed ) {
tft_fillRoundRect( 30,10, 200, 100, 1, ILI9340_BLACK );
menu_status = songs;
proceed = 0;
}
// otherwise stay at main_menu
else {
menu_status = main_menu;
}
break;
// if we are on the songs menu
case songs:
// display the currently selected song in red and set
// the global song variable depending on selected song.
// Move up or down according to current song
switch( song_to_play ) {
case Sweetchild:
start_index = 0;
end_index = 689;
tft_setTextColor(ILI9340_RED);
tft_setCursor(30, 100);
tft_writeString(Sweet Child);
tft_setTextColor(ILI9340_WHITE);
tft_setCursor(30, 120);
tft_writeString(Fireflies);
tft_setCursor(30, 140);
tft_writeString(Cliffs of Dover);
tft_setCursor(30, 160);
tft_writeString(Reptilia);
if ( StrummerDownOn ) {
song_to_play = Fireflies;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
song_to_play = Reptilia;
Strummermenu = 0;
}
break;
case Fireflies:
start_index = 1233;
end_index = 1460;
tft_setCursor(30, 100);
tft_writeString(Sweet Child);
tft_setCursor(30, 120);
tft_setTextColor(ILI9340_RED);
tft_writeString(Fireflies);
tft_setTextColor(ILI9340_WHITE);
tft_setCursor(30, 140);
tft_writeString(Cliffs of Dover);
tft_setCursor(30, 160);
tft_writeString(Reptilia);
if ( StrummerDownOn ) {
song_to_play = CliffsofDover;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
song_to_play = Sweetchild;
Strummermenu = 0;
}
break;
case CliffsofDover:
start_index = 690;
end_index = 1232;
tft_setCursor(30, 100);
tft_writeString(Sweet Child);
tft_setCursor(30, 120);
tft_writeString(Fireflies);
tft_setCursor(30, 140);
tft_setTextColor(ILI9340_RED);
tft_writeString(Cliffs of Dover);
tft_setTextColor(ILI9340_WHITE);
tft_setCursor(30, 160);
tft_writeString(Reptilia);
if ( StrummerDownOn ) {
song_to_play = Reptilia;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
song_to_play = Fireflies;
Strummermenu = 0;
}
break;
case Reptilia:
start_index = 1461;
end_index = 2061;
tft_setCursor(30, 100);
tft_writeString(Sweet Child);
tft_setCursor(30, 120);
tft_writeString(Fireflies);
tft_setCursor(30, 140);
tft_writeString(Cliffs of Dover);
tft_setCursor(30, 160);
tft_setTextColor(ILI9340_RED);
tft_writeString(Reptilia);
tft_setTextColor(ILI9340_WHITE);
if ( StrummerDownOn ) {
song_to_play = Sweetchild;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
song_to_play = CliffsofDover;
Strummermenu = 0;
}
break;
default:
break;
}
if ( greenPushed ) {
tft_fillRoundRect( 30,100, 210, 80, 1, ILI9340_BLACK );
menu_status = difficulty_menu;
proceed = 0;
disp_note_counter = start_index;
note_counter = start_index;
}
else if ( redPushed ) {
tft_fillRoundRect( 30,100, 210, 80, 1, ILI9340_BLACK );
menu_status = main_menu;
proceed = 0;
}
break;
// if we are on the difficulty menu
case difficulty_menu:
// highlight the difficulty the user is hovering over
// and if they press green, set the global difficulty
// value. Move between the settings based on current
// position
switch ( difficulty_setting ) {
case easy:
difficulty = 8;
tft_setCursor(100, 100);
tft_setTextColor(ILI9340_RED);
tft_writeString(Easy);
tft_setTextColor(ILI9340_WHITE);
tft_setCursor(100, 120);
tft_writeString(Medium);
tft_setCursor(100, 140);
tft_writeString(Hard);
// down
if ( StrummerDownOn ) {
difficulty_setting = medium;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
difficulty_setting = hard;
Strummermenu = 0;
}
break;
case medium:
difficulty = 4;
tft_setCursor(100, 100);
tft_writeString(Easy);
tft_setCursor(100, 120);
tft_setTextColor(ILI9340_RED);
tft_writeString(Medium);
tft_setTextColor(ILI9340_WHITE);
tft_setCursor(100, 140);
tft_writeString(Hard);
// down
if ( StrummerDownOn ) {
difficulty_setting = hard;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
difficulty_setting = easy;
Strummermenu = 0;
}
break;
case hard:
difficulty = 1;
tft_setCursor(100, 100);
tft_writeString(Easy);
tft_setCursor(100, 120);
tft_writeString(Medium);
tft_setCursor(100, 140);
tft_setTextColor(ILI9340_RED);
tft_writeString(Hard);
tft_setTextColor(ILI9340_WHITE);
// down
if ( StrummerDownOn ) {
difficulty_setting = easy;
Strummermenu = 0;
}
//up
else if ( StrummerUpOn ) {
difficulty_setting = medium;
Strummermenu = 0;
}
break;
default:
difficulty_setting = easy;
break;
}
// if they press green, start the game
if ( greenPushed ) {
tft_fillRoundRect( 0, 0, 300, 300, 2, ILI9340_BLACK );
tft_setRotation(1);
done_with_menu = 1;
menuOn = 0;
Strummermenu = 0;
points = 0;
}
else if ( redPushed ) {
tft_fillRoundRect( 100, 100, 80, 100, 2, ILI9340_BLACK );
menu_status = songs;
proceed = 0;
}
break;
default:
menu_status = main_menu;
break;
}
}
PT_YIELD_TIME_msec(67);
}
}
PT_END(pt);
} // thread 4
// === Main ======================================================
int main(void)
{
// === Config timer and output compare to make PWM ========
// set up timer2 to generate the wave period — SET this to 1 mSec!
OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, 800);
// Need ISR to compute PID controller
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
mT2ClearIntFlag(); // and clear the interrupt flag
// set up compare3 for PWM mode
//OpenOC3(OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE , pwm_on_time, pwm_on_time); //
// OC3 is PPS group 4, map to RPB9 (pin 18)
//PPSOutput(4, RPB9, OC3);
// === config the uart, DMA, vref, timer5 ISR ===========
PT_setup();
// === setup system wide interrupts ====================
INTEnableSystemMultiVectoredInt();
// === set up i/o port pin ===============================
mPORTBSetPinsDigitalIn( BIT_13 | BIT_8 ); //Set port as output
EnablePullUpB( BIT_13 | BIT_8 );
// init the display
tft_init_hw();
tft_begin();
tft_fillScreen(ILI9340_BLACK);
//240×320 vertical display
tft_setRotation(1); // Use tft_setRotation(1) for 320×240
//tft_fillRoundRect(0,0, 320, 240, 1, ILI9340_GREEN);// x,y,w,h,radius,color
//bottom barrier
//tft_drawLine(80, 1, 80, 60, ILI9340_GREEN);
// Buttons
mPORTASetPinsDigitalIn( BIT_2 | BIT_3 );
EnablePullUpA( BIT_2 | BIT_3 );
// SCK2 is pin 26
// SDO2 (MOSI) is in PPS output group 2, could be connected to RB5 which is pin 14
PPSOutput(2, RPB5, SDO2);
// control CS for DAC
mPORTBSetPinsDigitalOut(BIT_4);
mPORTBSetBits(BIT_4);
// divide Fpb by 2, configure the I/O ports. Not using SS in this example
// 16 bit transfer CKP=1 CKE=1
// possibles SPI_OPEN_CKP_HIGH; SPI_OPEN_SMP_END; SPI_OPEN_CKE_REV
// For any given peripherial, you will need to match these
// clk divider set to 2 for 20 MHz
SpiChnOpen(SPI_CHANNEL2, SPI_OPEN_ON | SPI_OPEN_MODE16 | SPI_OPEN_MSTEN | SPI_OPEN_CKE_REV , 2);
// end DAC setup
// initialize the notes
unsigned int color_array[5] = {ILI9340_GREEN, ILI9340_RED, ILI9340_YELLOW, ILI9340_BLUE, 0xFB00};
int k;
for(k = 0; k < 5; k ++) {
for ( i = 0; i < num_per_row; i++ ) {
notes_array[k][i].x = 240;
notes_array[k][i].y = 15 + (48*k);
notes_array[k][i].color = color_array[k];
notes_array[k][i].to_display = 0;
}
}
// generate the sine table
int ii;
for (ii = 0; ii < sine_table_size; ii++) {
sin_table[ii] = (int)(1023*sin((float)ii*6.283/(float)sine_table_size));
}
// === now the threads ===================================
// init the threads
PT_INIT(&pt_print);
PT_INIT(&pt_button1);
PT_INIT(&pt_button2);
PT_INIT(&pt_button3);
PT_INIT(&pt_button4);
PT_INIT(&pt_button5);
PT_INIT(&pt_notehit);
PT_INIT(&pt_draw);
PT_INIT(&pt_time);
PT_INIT(&pt_menu);
PT_INIT(&pt_spawnandsound);
PT_INIT(&pt_rockerbuttondown);
PT_INIT(&pt_rockerbuttonup);
// schedule the threads
while(1) {
PT_SCHEDULE(protothread_print(&pt_menu));
PT_SCHEDULE(protothread_print(&pt_print));
PT_SCHEDULE(protothread_button1(&pt_button1));
PT_SCHEDULE(protothread_button2(&pt_button2));
PT_SCHEDULE(protothread_button3(&pt_button3));
PT_SCHEDULE(protothread_button4(&pt_button4));
PT_SCHEDULE(protothread_button5(&pt_button5));
PT_SCHEDULE(protothread_notehit(&pt_notehit));
PT_SCHEDULE(protothread_draw(&pt_draw));
PT_SCHEDULE(protothread_time(&pt_time));
PT_SCHEDULE(protothread_spawnandsound(&pt_spawnandsound));
PT_SCHEDULE(protothread_rockerbuttondown(&pt_rockerbuttondown));
PT_SCHEDULE(protothread_rockerbuttonup(&pt_rockerbuttonup));
}
} // main

Source: Guitar Hero MMMMDCCLX

About The Author

Muhammad Bilal

I am a highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.