ECE 4180 @ Georgia Tech
Dependencies: mbed mbed-rtos 4DGL-uLCD-SE MMA8452
Diff: main.cpp
- Revision:
- 10:6f15dcee0a72
- Parent:
- 8:31e63caf37e2
--- a/main.cpp Wed Oct 28 15:06:56 2015 +0000 +++ b/main.cpp Thu Apr 30 01:01:47 2020 +0000 @@ -1,171 +1,409 @@ -// uLCD-144-G2 demo program for uLCD-4GL LCD driver library -// #include "mbed.h" #include "uLCD_4DGL.h" +#include "rtos.h" +#include "MMA8452.h" +#define LEFT_X 45 +#define CENTER_X 65 +#define RIGHT_X 85 + +#define JUMP_Y 96 +#define CENTER_Y 108 +#define DUCK_Y 120 + +#define RADIUS 4 + +#define SPEED 5 +#define VOLUME 0.1 + +Serial pc(USBTX,USBRX); // included for debugging accelerometer +MMA8452 acc(p28, p27, 100000); uLCD_4DGL uLCD(p9,p10,p11); // serial tx, serial rx, reset pin; +PwmOut jack(p23); +DigitalIn jump_button(p22); +DigitalIn duck_button(p21); + +// mutex for writing to the uLCD +Mutex LCD; + +volatile int score; +volatile int high_score; +volatile int steps; +volatile int strikes; +double acc_x; +volatile int player_x; +volatile int player_y; + +volatile int obstacle_x; +volatile int obstacle_y; +volatile int dodge_x; + +int ob_x_roots[] = {61, 65, 69}; +const char* strikes_arr[4] = { " ", " X", " XX", "XXX" }; +enum ob_type {dodge, duck, jump}; +ob_type ob_type_arr[] = {dodge, duck, jump}; + +volatile ob_type obstacle_type; + +volatile bool play_leftright; +volatile bool play_jump; +volatile bool play_duck; +volatile bool play_strikes; +volatile bool play_dead; + +void read_sound(void const *argument) { + while(1) { + + // different sounds in order of priority to audio output + if (play_dead) { + jack.period(1.0/440.0); + jack = VOLUME/2.0; + Thread::wait(750); + jack = 0.0; + Thread::wait(100); + jack = VOLUME/2.0; + Thread::wait(750); + jack = 0.0; + Thread::wait(100); + jack = VOLUME/2.0; + Thread::wait(750); + jack = 0.0; + play_dead = false; + play_strikes = false; + play_duck = false; + play_jump = false; + play_leftright = false; + } else if (play_strikes) { + jack.period(1.0/440.0); + jack = VOLUME/2.0; + Thread::wait(750); + jack = 0.0; + play_strikes = false; + play_duck = false; + play_jump = false; + play_leftright = false; + } else if (play_duck) { + jack.period(1.0/130.8); + jack = VOLUME/2.0; + Thread::wait(1000); + jack = 0.0; + play_duck = false; + play_jump = false; + play_leftright = false; + } else if (play_jump) { + jack.period(1.0/261.6); + jack = VOLUME/2.0; + Thread::wait(150); + jack.period(1.0/523.3); + Thread::wait(300); + jack = 0.0; + play_jump = false; + play_leftright = false; + } else if (play_leftright) { + jack.period(1.0/261.6); + jack = VOLUME/2.0; + Thread::wait(250); + jack = 0.0; + play_leftright = false; + } + } +} + +void read_acc(void const *argument){ + while(1) { + acc.readXGravity(&acc_x); + if (acc_x > 0.5) { + if (player_x == CENTER_X) { + player_x = LEFT_X; + } else if (player_x == RIGHT_X) { + player_x = CENTER_X; + } + play_leftright = true; + Thread::wait(500); + } else if (acc_x < -0.5) { + if (player_x == CENTER_X) { + player_x = RIGHT_X; + } else if (player_x == LEFT_X) { + player_x = CENTER_X; + } + play_leftright = true; + Thread::wait(500); + } + Thread::wait(250); + } +} + +void read_score(void const *argument) { + while(1) { + score = score + 10; + LCD.lock(); + uLCD.locate(11, 0); + uLCD.printf("%7D", score); // maximum 7-digit score, cap at 9,999,900 + LCD.unlock(); + Thread::wait(1000); + } +} + +void read_high_score(void const *argument) { + while(1) { + LCD.lock(); + uLCD.locate(0, 0); + uLCD.printf("HIGH:\n%D", high_score); + LCD.unlock(); + Thread::wait(1500); + } +} + +void read_strikes(void const *argument) { + while(1) { + LCD.lock(); + uLCD.locate(15, 1); + uLCD.printf(strikes_arr[strikes % 4]); + LCD.unlock(); + Thread::wait(1000); + } +} + +// check for button press +void read_duck(void const *argument) { + while(1) { + if (!duck_button) { + player_y = DUCK_Y; + play_duck = true; + Thread::wait(1000); + player_y = CENTER_Y; + } + } +} + +// check for button press +void read_jump(void const *argument) { + while(1) { + if (!jump_button && player_y != DUCK_Y) { + player_y = JUMP_Y; + play_jump = true; + Thread::wait(1000); + if (player_y == JUMP_Y) { + player_y = CENTER_Y; + } + } + } +} + +// check if it's game over +void read_alive(void const *argument) { + while(1) { + if (strikes >= 3 || score >= 9999900) { + if (score > high_score) { + high_score = score; + } + score = 0; + strikes = 0; + player_x = CENTER_X; + player_y = CENTER_Y; + } + } +} + +void read_position(void const *argument) { + while(1) { + LCD.lock(); + + // cover up positions on screen where there's no player or obstacle + if (player_x != LEFT_X) { + if (obstacle_y != JUMP_Y) { + uLCD.filled_circle(LEFT_X, JUMP_Y, RADIUS, 0x003057); + } + if (obstacle_y != CENTER_Y) { + uLCD.filled_circle(LEFT_X, CENTER_Y, RADIUS, 0x003057); + } + if (obstacle_y != DUCK_Y) { + uLCD.filled_circle(LEFT_X, DUCK_Y, RADIUS, 0x003057); + } + } + if (player_x != CENTER_X) { + if (obstacle_y != JUMP_Y) { + uLCD.filled_circle(CENTER_X, JUMP_Y, RADIUS, 0x003057); + } + if (obstacle_y != CENTER_Y) { + uLCD.filled_circle(CENTER_X, CENTER_Y, RADIUS, 0x003057); + } + if (obstacle_y != DUCK_Y) { + uLCD.filled_circle(CENTER_X, DUCK_Y, RADIUS, 0x003057); + } + } + if (player_x != RIGHT_X) { + if (obstacle_y != JUMP_Y) { + uLCD.filled_circle(RIGHT_X, JUMP_Y, RADIUS, 0x003057); + } + if (obstacle_y != CENTER_Y) { + uLCD.filled_circle(RIGHT_X, CENTER_Y, RADIUS, 0x003057); + } + if (obstacle_y != DUCK_Y) { + uLCD.filled_circle(RIGHT_X, DUCK_Y, RADIUS, 0x003057); + } + } + if (player_y != JUMP_Y && obstacle_y != JUMP_Y) { + if (player_x == RIGHT_X) { + uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057); + } else if (player_x == LEFT_X) { + uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057); + } else if (player_x == CENTER_X) { + uLCD.filled_circle(player_x, JUMP_Y, RADIUS, 0x003057); + } + } + if (player_y != CENTER_Y && obstacle_y != CENTER_Y) { + uLCD.filled_circle(player_x, CENTER_Y, RADIUS, 0x003057); + } + if (player_y != DUCK_Y && obstacle_y != DUCK_Y) { + if (player_x == RIGHT_X) { + uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057); + } else if (player_x == LEFT_X) { + uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057); + } else if (player_x == CENTER_X) { + uLCD.filled_circle(player_x, DUCK_Y, RADIUS, 0x003057); + } + } + uLCD.filled_circle(player_x, player_y, RADIUS, 0xB3A369); + LCD.unlock(); + } +} + +void read_obstacle(void const *argument) { + while(1) { + LCD.lock(); + uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0x003057); + steps++; + obstacle_y = obstacle_y + 12; + + // move obstacle along slope down the track + if (obstacle_type == dodge) { + if (dodge_x >= ob_x_roots[2]) { + dodge_x = dodge_x + 2; + } else if (dodge_x <= ob_x_roots[0]) { + dodge_x = dodge_x - 2; + } + } + + // reset obstacle if it has exited the screen + if (obstacle_y >= 130) { + obstacle_type = ob_type_arr[rand() % 3]; + obstacle_x = ob_x_roots[1]; + if (obstacle_type == dodge) { + dodge_x = ob_x_roots[rand() % 3]; + } + obstacle_y = 12; + steps = 0; + } + + // draw dodge obstacle + if (obstacle_type != dodge) { + uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0xEAAA00); + } else { + if (dodge_x >= ob_x_roots[2]) { + // draw rectangle on left side, dodge on the right + uLCD.filled_rectangle(obstacle_x - 10 - (steps * 2), obstacle_y + 7, dodge_x - 10, obstacle_y - 7, 0xEAAA00); + } else if (dodge_x <= ob_x_roots[0]) { + // draw rectangle on right side, dodge on the left + uLCD.filled_rectangle(dodge_x + 10, obstacle_y + 7, obstacle_x + 10 + (steps * 2), obstacle_y - 7, 0xEAAA00); + } else { + // draw two rectangles on either side, dodge in the middle + uLCD.filled_rectangle(ob_x_roots[0] - 5 - (steps * 2), obstacle_y + 7, ob_x_roots[0] - steps, obstacle_y - 7, 0xEAAA00); + uLCD.filled_rectangle(ob_x_roots[2] + steps, obstacle_y + 7, ob_x_roots[2] + 5 + (steps * 2), obstacle_y - 7, 0xEAAA00); + } + } + + // draw duck/jump obstacle + if (obstacle_type == duck) { + uLCD.filled_rectangle(obstacle_x - 8 - (steps * 2), obstacle_y + 7, obstacle_x + 8 + (steps * 2), obstacle_y - 5, 0x003057); + } else if (obstacle_type == jump) { + uLCD.filled_rectangle(obstacle_x - 8 - (steps * 2), obstacle_y + 5, obstacle_x + 8 + (steps * 2), obstacle_y - 7, 0x003057); + } + + // check for collisions, increment strikes if so + if (obstacle_y == CENTER_Y) { + if ((obstacle_type == duck && player_y != DUCK_Y) || (obstacle_type == jump && player_y != JUMP_Y) || (obstacle_type == dodge && dodge_x != player_x)) { + strikes++; + if (strikes >= 3) { + play_dead = true; + } else { + play_strikes = true; + } + } + } + LCD.unlock(); + Thread::wait(SPEED); + } +} int main() { - // basic printf demo = 16 by 18 characters on screen - uLCD.printf("\nHello uLCD World\n"); //Default Green on black text - uLCD.printf("\n Starting Demo..."); - uLCD.text_width(4); //4X size text - uLCD.text_height(4); - uLCD.color(RED); - for (int i=10; i>=0; --i) { - uLCD.locate(1,2); - uLCD.printf("%2D",i); - wait(.5); - } - uLCD.cls(); - uLCD.printf("Change baudrate......"); - uLCD.baudrate(3000000); //jack up baud rate to max for fast display - //if demo hangs here - try lower baud rates - // - // printf text only full screen mode demo - uLCD.background_color(BLUE); - uLCD.cls(); - uLCD.locate(0,0); - uLCD.color(WHITE); - uLCD.textbackground_color(BLUE); - uLCD.set_font(FONT_7X8); - uLCD.text_mode(OPAQUE); - int i=0; - while(i<64) { - if(i%16==0) uLCD.cls(); - uLCD.printf("TxtLine %2D Page %D\n",i%16,i/16 ); - i++; //16 lines with 18 charaters per line - } - wait(0.5); - //demo graphics commands - uLCD.background_color(BLACK); + uLCD.background_color(0x003057); uLCD.cls(); - uLCD.background_color(DGREY); - uLCD.filled_circle(60, 50, 30, 0xFF00FF); - uLCD.triangle(120, 100, 40, 40, 10, 100, 0x0000FF); - uLCD.line(0, 0, 80, 60, 0xFF0000); - uLCD.filled_rectangle(50, 50, 100, 90, 0x00FF00); - uLCD.pixel(60, 60, BLACK); - uLCD.read_pixel(120, 70); - uLCD.circle(120, 60, 10, BLACK); - uLCD.set_font(FONT_7X8); - uLCD.text_mode(TRANSPARENT); - uLCD.text_bold(ON); - uLCD.text_char('B', 9, 8, BLACK); - uLCD.text_char('I',10, 8, BLACK); - uLCD.text_char('G',11, 8, BLACK); - uLCD.text_italic(ON); - uLCD.text_string("This is a test of string", 1, 4, FONT_7X8, WHITE); - wait(2); + uLCD.text_width(4); + uLCD.text_height(4); + uLCD.textbackground_color(0x003057); + uLCD.color(0xB3A369); + uLCD.printf("\nBuzz Run"); -//Bouncing Ball Demo - float fx=50.0,fy=21.0,vx=1.0,vy=0.4; - int x=50,y=21,radius=4; - uLCD.background_color(BLACK); - uLCD.cls(); - //draw walls - uLCD.line(0, 0, 127, 0, WHITE); - uLCD.line(127, 0, 127, 127, WHITE); - uLCD.line(127, 127, 0, 127, WHITE); - uLCD.line(0, 127, 0, 0, WHITE); - for (int i=0; i<1500; i++) { - //draw ball - uLCD.filled_circle(x, y, radius, RED); - //bounce off edge walls and slow down a bit? - if ((x<=radius+1) || (x>=126-radius)) vx = -.90*vx; - if ((y<=radius+1) || (y>=126-radius)) vy = -.90*vy; - //erase old ball location - uLCD.filled_circle(x, y, radius, BLACK); - //move ball - fx=fx+vx; - fy=fy+vy; - x=(int)fx; - y=(int)fy; - } - wait(0.5); -//draw an image pixel by pixel - int pixelcolors[50][50]; - uLCD.background_color(BLACK); + wait(2); uLCD.cls(); -//compute Mandelbrot set image for display -//image size in pixels - const unsigned ImageHeight=128; - const unsigned ImageWidth=128; - //"c" region to display - double MinRe = -0.75104; - double MaxRe = -0.7408; - double MinIm = 0.10511; - double MaxIm = MinIm+(MaxRe-MinRe)*ImageHeight/ImageWidth; - double Re_factor = (MaxRe-MinRe)/(ImageWidth-1); - double Im_factor = (MaxIm-MinIm)/(ImageHeight-1); - unsigned MaxIterations = 2048; - for(unsigned y=0; y<ImageHeight; ++y) { - double c_im = MaxIm - y*Im_factor; - for(unsigned x=0; x<ImageWidth; ++x) { - double c_re = MinRe + x*Re_factor; - double Z_re = c_re, Z_im = c_im; - int niterations=0; - for(unsigned n=0; n<MaxIterations; ++n) { - double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im; - if(Z_re2 + Z_im2 > 4) { - niterations = n; - break; - } - Z_im = 2*Z_re*Z_im + c_im; - Z_re = Z_re2 - Z_im2 + c_re; - } - if (niterations!=(MaxIterations-1)) - uLCD.pixel(x,y,((niterations & 0xF00)<<12)+((niterations & 0xF0)<<8)+((niterations & 0x0F)<<4) ); - } + uLCD.baudrate(3000000); // high baud rate for fast refresh + + // Left-side guardrails + uLCD.line(23, 130, 43, 10, 0xFFFFFF); + uLCD.line(24, 130, 44, 10, 0xB3A369); + uLCD.line(25, 130, 45, 10, 0xB3A369); + uLCD.line(26, 130, 46, 10, 0xB3A369); + uLCD.line(27, 130, 47, 10, 0xFFFFFF); + + // Right-side guardrails + uLCD.line(103, 130, 83, 10, 0xFFFFFF); + uLCD.line(104, 130, 84, 10, 0xB3A369); + uLCD.line(105, 130, 85, 10, 0xB3A369); + uLCD.line(106, 130, 86, 10, 0xB3A369); + uLCD.line(107, 130, 87, 10, 0xFFFFFF); + + score = 0; + high_score = 0; + strikes = 0; + steps = 0; + + // make sure sounds are off + play_leftright = false; + play_jump = false; + play_duck = false; + play_strikes = false; + play_dead = false; + + // initialize player position + player_x = CENTER_X; + player_y = CENTER_Y; + + // initialize obstacle coordinates + obstacle_x = ob_x_roots[1]; + dodge_x = ob_x_roots[rand() % 3]; + obstacle_y = 12; + obstacle_type = ob_type_arr[rand() % 3]; + + // pull up push_buttons + jump_button.mode(PullUp); + duck_button.mode(PullUp); + + // start threads + Thread acc_thread(read_acc); + Thread score_thread(read_score); + Thread duck_thread(read_duck); + Thread jump_thread(read_jump); + Thread alive_thread(read_alive); + Thread position_thread(read_position); + Thread obstacle_thread(read_obstacle); + Thread strikes_thread(read_strikes); + Thread sound_thread(read_sound); + Thread high_score_thread(read_high_score); + + while(1) { + // loop forever } - wait(5); -// PLASMA wave BLIT animation -//draw an image using BLIT (Block Image Transfer) fastest way to transfer pixel data - uLCD.cls(); - int num_cols=50; - int num_rows=50; - int frame=0; - double a,b,c=0.0; - while(frame<75) { - for (int k=0; k<num_cols; k++) { - b= (1+sin(3.14159*k*0.75/(num_cols-1.0)+c))*0.5; - for (int i=0; i<num_rows; i++) { - a= (1+sin(3.14159*i*0.75/(num_rows-1.0)+c))*0.5; - // a and b will be a sine wave output between 0 and 1 - // sine wave was scaled for nice effect across array - // uses a and b to compute pixel colors based on rol and col location in array - // also keeps colors at the same brightness level - if ((a+b) <.667) - pixelcolors[i][k] = (255-(int(254.0*((a+b)/0.667)))<<16) | (int(254.0*((a+b)/0.667))<<8) | 0; - else if ((a+b)<1.333) - pixelcolors[i][k] = (0 <<16) | (255-(int (254.0*((a+b-0.667)/0.667)))<<8) | int(254.0*((a+b-0.667)/0.667)); - else - pixelcolors[i][k] = (int(255*((a+b-1.333)/0.667))<<16) | (0<<8) | (255-(int (254.0*((a+b-1.333)/0.667)))); - } - } - uLCD.BLIT(39, 39, 50, 50, &pixelcolors[0][0]); - c = c + 0.0314159*3.0; - if (c > 6.2831) c = 0.0; - frame++; - } - //Load Image Demo - uLCD.cls(); - //SD card needed with image and video files for last two demos - uLCD.cls(); - uLCD.media_init(); - uLCD.printf("\n\nAn SD card is needed for image and video data"); - uLCD.set_sector_address(0x001D, 0x4C01); - uLCD.display_image(0,0); - wait(10); - //Play video demo - while(1) { - uLCD.cls(); - uLCD.media_init(); - uLCD.set_sector_address(0x001D, 0x4C42); - uLCD.display_video(0,0); - } + } - - -