Pong on Nokia Color LCD Display with PS/2 and mBed

Overview

I wrote a little Pong game to run on the Nokia Color LCD display. It's pretty simple, one player and a computer with one ball. You use the UP and DOWN arrow keys on a PS/2 keyboard to control the paddle. There is a scoreboard that shows lives left and current score.

Necessary Items

mBedhttp://mbed.org/media/img/mbedMicrocontroller.jpg
Nokia Color LCD Display/media/uploads/wjohnsto/nokialcd.jpg
PS/2 Breakouthttp://dlnmh9ip6v2uc.cloudfront.net/images/products/08651-05-L_i_th.jpg
PS/2 Keyboard

Schematic

BreakoutLibrary
PS/2 BreakoutPS/2 Class
Nokia Color LCD DisplayNokiaLCD Class

/media/uploads/wjohnsto/schematic.jpg

Commands

ButtonCommand
S1Play/Pause
S2Reset
Keyboard UP/DOWNMove Paddle UP/DOWN

Video

Code

Import programPong

Simple Pong game on NokiaLCD with PS2

Import program

00001 #include "NokiaLCD.h"
00002 
00003 class Ball {
00004   /* This class creates a ball object */
00005   
00006   // Attributes
00007   int x,y,width,height,color,xInc,yInc;
00008 
00009 public:
00010   // Constructors
00011   Ball();
00012   Ball(int x, int y, int w, int h, int c, int xi, int yi);
00013   
00014   // Member functions
00015   void move(NokiaLCD &lcd);
00016   void draw(NokiaLCD &lcd, bool isBlack) const;
00017   int getX() const;
00018   int getY() const;
00019   bool hitX();
00020   bool hitY();
00021   bool hitP1(int _x, int _y, int _height);
00022   bool hitP2(int _x, int _y, int _height);
00023   int size() const;
00024   void reverseX();
00025   void reverseY();
00026 };

Import program

00001 #include "mbed.h"
00002 #include "ball.h"
00003 
00004 Ball::Ball() {
00005   int x=y=width=height=color=xInc=yInc=0;
00006 }
00007 
00008 Ball::Ball(int x, int y, int w, int h, int c, int xi, int yi)
00009  : x(x), y(y), width(w), height(h), color(c), xInc(xi), yInc(yi) {}
00010  
00011  
00012 /* 
00013  * Member Function move:
00014  * Description: Colors in the previous ball black
00015  *  and moves ball to new position.
00016  */
00017 void Ball::move(NokiaLCD &lcd) {
00018   draw(lcd, true);
00019   x += xInc; y += yInc;
00020 }
00021 
00022 /* 
00023  * Member Function draw:
00024  * Description: Draws object on screen
00025  *  if isBlack, color in black.
00026  */
00027 void Ball::draw(NokiaLCD &lcd, bool isBlack) const {
00028   lcd.fill(x, y, width, height, (isBlack) ? 0x000000 : color);
00029 }
00030 
00031 int Ball::size() const {
00032   return width*height;
00033 }
00034 
00035 int Ball::getX() const {
00036   return x;
00037 }
00038 
00039 int Ball::getY() const {
00040   return y;
00041 }
00042 
00043 bool Ball::hitX() {
00044   return (x+width<=width) || (x+width>=132);
00045 }
00046 
00047 bool Ball::hitY() {
00048   return (y<=0) || (y+height>=130);
00049 }
00050 
00051 /* 
00052  * Member Function hitP1:
00053  * Description: Checks to see if there is
00054  *  a collision between paddle1 and the ball.
00055  *  Has special functionality for changing
00056  *  y-incline based on collision point.
00057  */
00058 bool Ball::hitP1(int _x, int _y, int _height) {
00059   bool hit = ((_x>=x) && (xInc<0)) && 
00060          (((_y<=y) && (_y+_height>=y+height)) ||
00061           ((_y>=y) && (_y<=y+height)) ||
00062           ((_y+_height>=y) && (_y+_height<=y+height))
00063          );
00064   if(hit) {
00065     if(_y+_height-y < 4 && yInc>0) yInc = 2;
00066     if(y-_y < 4 && yInc<0) yInc = -2;
00067     else yInc = (yInc>0) ? 1 : -1;
00068   }
00069   return hit;
00070 }
00071 
00072 
00073 /* 
00074  * Member Function hitP2:
00075  * Description: Checks to see if there is
00076  *  a collision between paddle2 and the ball.
00077  *  Has special functionality for changing
00078  *  y-incline based on collision point.
00079  */
00080 bool Ball::hitP2(int _x, int _y, int _height) {
00081   bool hit = ((_x<=x+width) && (xInc>0)) && 
00082          (((_y<=y) && (_y+_height>=y+height)) ||
00083           ((_y>=y) && (_y<=y+height)) ||
00084           ((_y+_height>=y) && (_y+_height<=y+height))
00085          );
00086   if(hit) {
00087     if(_y+_height-y < 4 && yInc>0) yInc = 2;
00088     if(y-_y < 4 && yInc<0) yInc = -2;
00089     else yInc = (yInc>0) ? 1 : -1;
00090   }
00091   return hit;
00092 }
00093 
00094 void Ball::reverseX() {
00095   xInc *= -1;
00096 }
00097 
00098 void Ball::reverseY() {
00099   yInc *= -1;
00100 }

Import program

00001 #include "NokiaLCD.h"
00002 
00003 class Paddle {
00004   /* This class creates a paddle object */
00005  
00006   // Attributes
00007   int x,y,width,height,color,lives,score;
00008 
00009 public: 
00010   // Constructors
00011   Paddle();
00012   Paddle(int x, int y, int w, int h, int c, int l, int s);
00013   
00014   // Member functions
00015   void move(NokiaLCD &lcd, int increment);
00016   void moveCPU(NokiaLCD &lcd, int _y);
00017   void draw(NokiaLCD &lcd, bool isBlack) const;
00018   bool loseLife();
00019   void addPoint();
00020   int size() const;
00021   int getWidth() const;
00022   int getHeight() const;
00023   int getX() const;
00024   int getY() const;
00025   int getLives() const;
00026   int getScore() const;
00027   void setLives(int l);
00028   
00029 };

Import program

00001 #include "mbed.h"
00002 #include "paddle.h"
00003 
00004 Paddle::Paddle() {
00005   int x=y=width=height=color=score=0;
00006   lives = 3;
00007 }
00008 
00009 Paddle::Paddle(int x, int y, int w, int h, int c, int l, int s)
00010  : x(x), y(y), width(w), height(h), color(c), lives(l), score(s) {}
00011  
00012 /* 
00013  * Member Function move:
00014  * Description: Colors in the previous paddle black
00015  *  and moves paddle to new position.
00016  */
00017 void Paddle::move(NokiaLCD &lcd, int increment) {
00018   draw(lcd, true);
00019   y += increment;
00020 }
00021 
00022 
00023 /* 
00024  * Member Function moveCPU:
00025  * Description: Colors in the previous paddle black
00026  *  and moves paddle to new position.
00027  *  inc variable allows paddle to only move every
00028  *  other function call.
00029  */
00030 void Paddle::moveCPU(NokiaLCD &lcd, int _y) {
00031   static int inc = 1;
00032   draw(lcd, true);
00033   if(_y>y+height/2 && y+height<130) y += inc;
00034   else if(_y+5<y+height/2 && y>0) y -= inc;
00035   inc = (inc) ? 0 : 1;
00036 }
00037 
00038 void Paddle::draw(NokiaLCD &lcd, bool isBlack) const {
00039   lcd.fill(x, y, width, height, (isBlack) ? 0x000000 : color);
00040 }
00041 
00042 bool Paddle::loseLife() {
00043   return --lives;
00044 }
00045 
00046 void Paddle::addPoint() {
00047   ++score;
00048 }
00049 
00050 int Paddle::size() const {
00051   return width*height;
00052 }
00053 
00054 int Paddle::getWidth() const {
00055   return width;
00056 }
00057 
00058 int Paddle::getHeight() const {
00059   return height;
00060 }
00061 
00062 int Paddle::getX() const {
00063   return x;
00064 }
00065 
00066 int Paddle::getY() const {
00067   return y;
00068 }
00069 
00070 int Paddle::getLives() const {
00071   return lives;
00072 }
00073 
00074 int Paddle::getScore() const {
00075   return score;
00076 }
00077 
00078 void Paddle::setLives(int l) {
00079   lives = l;
00080 }

Import program

00001 #include "mbed.h"
00002 #include "NokiaLCD.h"
00003 #include "PS2Keyboard.h"
00004 #include "ball.h"
00005 #include "paddle.h"
00006 
00007 // State enumerator
00008 typedef enum {
00009   RESET, RUN, PAUSE
00010 } STATES;
00011 
00012 NokiaLCD lcd(p5, p7, p8, p9, NokiaLCD::LCD6610); // mosi, sclk, cs, rst, type
00013 
00014 PS2Keyboard ps2kb(p12, p11); // CLK, DAT
00015 
00016 DigitalIn sw2(p24);
00017 DigitalIn sw1(p25);
00018 
00019 PwmOut g(p21);
00020 PwmOut b(p22);
00021 PwmOut r(p23);
00022 
00023 // Button enumerator for PS/2 keyboard
00024 enum BUTTONS{
00025   UP = 0xe75,
00026   DOWN = 0xe72,
00027 };
00028 
00029 /* 
00030  * Subroutine drawScreen:
00031  * Description: Draws both paddles
00032  *  and the ball.
00033  */
00034 void drawScreen(Paddle paddle1, Paddle paddle2, Ball theBall, bool isBlack) {
00035   paddle1.draw(lcd, isBlack);
00036   paddle2.draw(lcd ,isBlack);
00037   theBall.draw(lcd ,isBlack);
00038 }
00039 
00040 /* 
00041  * Subroutine drawScores:
00042  * Description: Draws the scoreboard
00043  */
00044 void drawScores(Paddle paddle1, Paddle paddle2) {
00045   lcd.locate(7,0);
00046   lcd.putc('0' + paddle1.getScore());
00047   lcd.locate(9,0);
00048   lcd.putc('0' + paddle2.getScore());
00049   lcd.fill(66,0,2,130,0xFFFFFF);
00050   lcd.locate(7,15);
00051   lcd.putc('0' + paddle1.getLives());
00052   lcd.locate(9,15);
00053   lcd.putc('0' + paddle2.getLives());
00054 }
00055 
00056 int main() {
00057   PS2Keyboard::keyboard_event_t evt_kb; // Setup keyboard interrupt
00058   lcd.background(0x000000);
00059   lcd.cls();
00060   Paddle paddle1, paddle2;
00061   Ball theBall;
00062   int temp, count=0;
00063   drawScreen(paddle1, paddle2, theBall, false);
00064   drawScores(paddle1, paddle2);
00065   STATES state = RESET; // Initial state is RESET
00066   while(1) {
00067     switch(state) {
00068       case RESET: // Reset objects, draw the screen, state = PAUSE
00069         lcd.cls();
00070         paddle1 = Paddle(1,10,5,25,0xFFFFFF,paddle1.getLives(),paddle1.getScore());
00071         paddle2 = Paddle(125,3,5,25,0xFFFFFF,paddle2.getLives(),paddle2.getScore());
00072         theBall = Ball(6,25,5,5,0xFFFF00,1,1);
00073         drawScreen(paddle1, paddle2, theBall, false);
00074         drawScores(paddle1, paddle2);
00075         state = PAUSE;
00076         break;
00077       case PAUSE: // Set RGB LED to Red, wait for switch input
00078         r = 0;
00079         b = g = 1;
00080         if(!sw2) {
00081           while(!sw2);
00082           paddle2.setLives(3);
00083           paddle1.setLives(3);
00084           state = RESET;
00085           break;
00086         }
00087         if(!sw1) {
00088           while(!sw1);
00089           state = RUN;
00090         }
00091         break;
00092       case RUN: // Set RGB LED to Blue and run program
00093         r = g = 1;
00094         b = 0;
00095         if(!sw2) { // Reset if SW2 is pressed
00096           while(!sw2);
00097           paddle2.setLives(3);
00098           paddle1.setLives(3);
00099           state = RESET;
00100           break;
00101         }
00102         if(!sw1) { // Pause if SW1 is pressed
00103           while(!sw1);
00104           state = PAUSE;
00105           break;
00106         }
00107         if (ps2kb.processing(&evt_kb)) { // Executes if a key is pressed
00108           temp = evt_kb.scancode[0];
00109           for (int i = 1; i < evt_kb.length; i++) { // Parse keyboard input into a key
00110             temp <<= 4;
00111             temp |= evt_kb.scancode[i];
00112           }
00113           switch(temp) { // Use key enumerator to move paddle1
00114             case UP:
00115               if(paddle1.getY()>2)
00116                 paddle1.move(lcd, -2);
00117               break;
00118             case DOWN: 
00119               if(paddle1.getY()+paddle1.getHeight()<128)
00120                 paddle1.move(lcd, 2);
00121               break;
00122           }
00123         }
00124         if(count%2) // Only let CPU move once every 2 times through the loop
00125           paddle2.moveCPU(lcd, theBall.getY());
00126         if(++count==5) { // Only move the ball once every 5 times through the loop
00127           count = 0;
00128           if(theBall.hitP1((paddle1.getX()+paddle1.getWidth()), paddle1.getY(), paddle1.getHeight()))
00129             theBall.reverseX();
00130           if(theBall.hitP2(paddle2.getX(), paddle2.getY(), paddle2.getHeight()))
00131             theBall.reverseX();
00132           if(theBall.hitX()) { // If the ball hits one of the sides of the screen
00133             if(theBall.getX()<7) { // If the ball hit paddle1's side
00134               if(!paddle1.loseLife()) { // If paddle1 has no more lives
00135                 paddle1.setLives(3);
00136                 paddle2.setLives(3);
00137                 paddle2.addPoint();
00138               }
00139             }
00140             else if(theBall.getX()>120) { // If the ball hit paddle2's side
00141               if(!paddle2.loseLife()) { // If paddle2 has no more lives
00142                 paddle2.setLives(3);
00143                 paddle1.setLives(3);
00144                 paddle1.addPoint();
00145               }
00146             }
00147             theBall.reverseX();
00148             state = RESET; // Reset the objects
00149           }
00150           if(theBall.hitY()) 
00151             theBall.reverseY();
00152           theBall.move(lcd);
00153         }
00154         break;
00155     }
00156     drawScreen(paddle1, paddle2, theBall, false);
00157     drawScores(paddle1, paddle2);
00158   }
00159 }


Please log in to post comments.