You are on page 1of 14

arduinopong - alastairparker

Arduino Pong

Who doesn't like pong?

After playing with composite video signal generation, the next step was to use if for something
'useful'.
Programming was not difficult, but there were some challenges.
Given that so much time is spent on generating the correct video signals, any major delays (1-5us)
would cause the video to lose sync and nothing would be displayed.

Essentially the code is multi-threaded, with the left players position being updated in one loop, and
the right updated in another.

Due to the timing constraints, I decided not to use any floating point arithmetic for the velocity of the
ball. This meant that I needed to have some other way of controlling how often the ball would change
positions. In the end I used a delay loop to limit how often the balls position would be updated,
changing the number of main loops to skip would effectively change the velocity of the ball.

The video resolution is 35*14, the arduino is capable of generating video lines of about 100-105
pixels, but the real killer is the amount of ram available, 1024/100 = 10 lines of 100 pixels, with 24
bytes of memory available for general use. This would have been ok, but it wouldn't have looked very
nice having a ball that is 1/10 of the screen height and 1/100 of the screen width.

The code still isn't completely finished, presently the ball moves with a constant velocity. This can be
easily changed by varying the ballXVel and ballYVel values whenever there is a collision with the ball

I only had 1 10k pot lying around, so I tied both players inputs to the same pin (analogIn #2), adding
a second player is as simple as adding another pot to another pin (say analogIn #3) and changing the
constant PLAYER_RIGHT_PIN to that pin (again 3).

1 of 14
arduinopong - alastairparker

Setup
In order to get the timings exact, you will need to disable the timer interrupt on the atmega168

this is no longer correct, use cli(); as the first line in setup();


Edit the file lib/targets/arduino/wiring.c
and block comment out the following

// enable timer 0 overflow interrupt


#if defined(__AVR_ATmega168__)
sbi(TIMSK0, TOIE0);
#else
sbi(TIMSK, TOIE0);
#endif

ie:

make it look like this

/*
// enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
sbi(TIMSK0, TOIE0);
#else
sbi(TIMSK, TOIE0);
#endif
*/

The 75ohm is required, but the 450 and 900 arent so critical, more common values like 430 and 1k
could probably be used instead, just remember to check the volatges before you plug anthing into the
tv !

have a read of http://www.rickard.gunee.com/projects/video/pic/howto.php , he explains the overall


process quite well, including voltage info

Code
//Arduino Tv framebuffer
//Alastair Parker
//2007

// Video out voltage levels


#define _SYNC 0x00
#define _BLACK 0x01
#define _GRAY 0x02
#define _WHITE 0x03

// dimensions of the screen


#define WIDTH 38
#define HEIGHT 14

2 of 14
arduinopong - alastairparker

//number of lines to display


#define DISPLAY_LINES 240

// update speed for the main loop of the game


#define UPDATE_INTERVAL 1

// positions of the player paddles


#define PLAYER_RIGHT_X 35
#define PLAYER_LEFT_X 2

// maximum velocity of the ball


#define MAX_VEL 10

// locations of the top and bottom virtual bars


#define SCORE_BAR 3
#define BASE_BAR 13

// time to wait while paused


#define DONE_WAITING 30
#define SCORE_WAITING 30

// max player life


#define MAX_LIFE 6

//positions of player life bars


#define PLAYER_LEFT_LIFE 2
#define PLAYER_RIGHT_LIFE 23

// input pins for the player pots


#define PLAYER_LEFT_PIN 2
// currently the same pin
#define PLAYER_RIGHT_PIN 3

//video pins
#define DATA_PIN 9
#define SYNC_PIN 8

// the video frameBuffer


byte frameBuffer[WIDTH][HEIGHT];

// loop indices
byte index, index2;

// pal video line loop


byte line;
// current drawing line in framebuffer
byte newLine;

// flag used in wait loop to indicate that player just scored


boolean justScored=false;

// positions for the left paddle


byte playerLeft;

3 of 14
arduinopong - alastairparker

byte playerLeftOld=-1;
// life for the left player
byte playerLeftLife = MAX_LIFE;

// positions of the right paddle


byte playerRight;
byte playerRightOld=-1;
// life for the right player
byte playerRightLife = MAX_LIFE;

// positions and velocity of the ball


int ballXVel = -1;
int ballYVel = -5;
byte ballXPos = 14;
byte ballYPos = 7;

// loop counters to control the velocity of the ball


byte ballXLoop =MAX_VEL;
byte ballYLoop =MAX_VEL;

// loop counter to for the main loop delay


int waitingCount = 0;

// start postion of the loading bar


byte loadingBar = 2;

// if the left player should be updated or the right


boolean playerLeftTurn=true;

// if we should be waiting for something to happen


boolean waiting=true;
// if displaying the title
boolean showingTitle = true;

// value of the counter controlling the freq of updates


byte updateCounter=0;

// init the variables


void initGame()
{
playerLeftOld=-1;
playerLeftLife = MAX_LIFE;
playerRightOld=-1;
playerRightLife = MAX_LIFE;
ballXVel = -1;
ballYVel = -5;
ballXPos = 14;
ballYPos = 7;
ballXLoop =MAX_VEL;
ballYLoop =MAX_VEL;
waitingCount = 0;
loadingBar = 2;
playerLeftTurn=true;
waiting=true;

4 of 14
arduinopong - alastairparker

showingTitle = true;
justScored=false;
updateCounter=0;
}

// draw a pixel to the buffer


void setPixel(byte x,byte y)
{
frameBuffer[x][y]= _WHITE;
}

void grayPixel(byte x, byte y)


{
frameBuffer[x][y]= _GRAY;
}

// draw a black pixel to the buffer


void clearPixel(byte x,byte y)
{
frameBuffer[x][y]= _BLACK;
}

// draw a paddle
void drawPaddle(byte x,byte y,byte col)
{
frameBuffer[x][y]= col;
frameBuffer[x][y+1]= col;
}

//draw the title message


void drawArduinoPong()
{
//arduino
setPixel(7,3);
setPixel(8,3);
setPixel(15,3);
setPixel(21,3);
setPixel(6,4);
setPixel(8,4);
setPixel(14,4);
setPixel(15,4);
setPixel(28,4);
setPixel(6,5);
setPixel(7,5);
setPixel(8,5);
setPixel(10,5);
setPixel(11,5);
setPixel(13,5);
setPixel(15,5);
setPixel(17,5);
setPixel(19,5);
setPixel(21,5);
setPixel(23,5);

5 of 14
arduinopong - alastairparker

setPixel(24,5);
setPixel(27,5);
setPixel(29,5);
setPixel(6,6);
setPixel(8,6);
setPixel(10,6);
setPixel(14,6);
setPixel(15,6);
setPixel(18,6);
setPixel(19,6);
setPixel(21,6);
setPixel(23,6);
setPixel(25,6);
setPixel(28,6);

//pong
setPixel(10,8);
setPixel(11,8);
setPixel(15,8);
setPixel(16,8);
setPixel(19,8);
setPixel(22,8);
setPixel(24,8);
setPixel(25,8);
setPixel(26,8);
setPixel(10,9);
setPixel(12,9);
setPixel(14,9);
setPixel(17,9);
setPixel(19,9);
setPixel(20,9);
setPixel(22,9);
setPixel(24,9);
setPixel(10,10);
setPixel(11,10);
setPixel(14,10);
setPixel(17,10);
setPixel(19,10);
setPixel(21,10);
setPixel(22,10);
setPixel(24,10);
setPixel(26,10);
setPixel(10,11);
setPixel(15,11);
setPixel(16,11);
setPixel(19,11);
setPixel(22,11);
setPixel(24,11);
setPixel(25,11);
setPixel(26,11);
}

// do the main game logic regarding the ball and collisions


void updateBall(boolean xEvent)

6 of 14
arduinopong - alastairparker

{
// if checking the x coords of the ball
if(xEvent)
{
/*
TODO

have the yvel start 1t 1

*/

//check for collision with left player


if(ballXPos <PLAYER_LEFT_X+2 & (playerLeft == ballYPos or playerLeft+1 == ballYPos))
{
ballXVel = -1 * ballXVel;
ballXPos=PLAYER_LEFT_X+1;
}
//check for collision with right player
if(ballXPos > PLAYER_RIGHT_X-2 & (playerRight == ballYPos or playerRight+1 == ballYPos))
{
ballXVel = -1 * ballXVel;
ballXPos=PLAYER_RIGHT_X-1;
}

//move the ball in the x direction


if(ballXVel < 1)
{
ballXPos--;
}
else
{
ballXPos++;
}
}

//if checking the y coords


if(!xEvent)
{
//check for top of screen hit
if( ballYPos <= SCORE_BAR )
{
ballYVel = -1 * ballYVel;
ballYPos = SCORE_BAR;
}
//check for bottom of screen hit
if( ballYPos>BASE_BAR )
{
ballYVel = -1 * ballYVel;
ballYPos = BASE_BAR+1;
}

// move the ball in the y direction


if(ballYVel < 1)
{

7 of 14
arduinopong - alastairparker

ballYPos--;
}
else
{
ballYPos++;
}
}

//always check for scoring of points

//right player scores


if(ballXPos==PLAYER_LEFT_X)//0
{
//reset the position of the ball
ballXPos = PLAYER_RIGHT_X - 5;
ballYPos=7;

// change the other players life


clearPixel(PLAYER_LEFT_LIFE+2*playerLeftLife-2,SCORE_BAR-1);
playerLeftLife--;

// indicate that someone scored so the wait doesn't start a new game
justScored=true;
// indicate that we should wait next loop
waiting=true;

// check if game is over


if(playerLeftLife==0)
justScored=false;
}

//left player scores


if(ballXPos==PLAYER_RIGHT_X)//38
{
//reset position of ball
ballXPos = PLAYER_LEFT_X + 5;
ballYPos=7;

//update other players score


clearPixel(PLAYER_RIGHT_LIFE+2*(MAX_LIFE-playerRightLife)+2,SCORE_BAR-1);
playerRightLife--;

// indicate that someone scored so the wain doesn't start a new game
justScored=true;
// indicate that we should wait next loop
waiting=true;

// check for game over


if(playerRightLife==0)
justScored=false;
}

8 of 14
arduinopong - alastairparker

// draw the player life bars


void initScreen()
{
for(index=1;index<=MAX_LIFE;++index)
{
grayPixel(index*2,SCORE_BAR-1);
grayPixel(index*2+PLAYER_RIGHT_LIFE,SCORE_BAR-1);
}
}

// draw the ball


void drawBall(byte col)
{
frameBuffer[ballXPos][ballYPos]=col;
}

// clear the screen


void clearScreen()
{
for (index = 0; index < WIDTH; index++)
for (index2=0;index2<=HEIGHT;++index2)
{
frameBuffer[index][index2] = _BLACK;
}
}

// the setup routine


void setup()
{
cli();
pinMode (SYNC_PIN, OUTPUT);
pinMode (DATA_PIN, OUTPUT);
digitalWrite (SYNC_PIN, HIGH);
digitalWrite (DATA_PIN, HIGH);
clearScreen();
drawArduinoPong();
}

void loop()
{
// iterate over the lines on the tv
for ( line =0;line< DISPLAY_LINES;++line)
{

// HSync
// front porch (1.5 us)
PORTB = _BLACK;
delayMicroseconds(1.5);
//sync (4.7 us)
PORTB = _SYNC;
delayMicroseconds(4.7);
// breezeway (.6us) + burst (2.5us) + colour back borch (1.6 us)
PORTB = _BLACK;

9 of 14
arduinopong - alastairparker

delayMicroseconds(0.6+2.5+1.6);

//calculate which line to draw to


newLine = line >>4;
delayMicroseconds(1);

//display the array for this line


// a loop would have been smaller, but it messes the timing up
PORTB = frameBuffer[0][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[1][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[2][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[3][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[4][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[5][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[6][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[7][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[8][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[9][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[10][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[11][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[12][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[13][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[14][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[15][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[16][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[17][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[18][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[19][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[20][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[21][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[22][newLine];

10 of 14
arduinopong - alastairparker

delayMicroseconds(1);
PORTB = frameBuffer[23][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[24][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[25][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[26][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[27][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[28][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[29][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[30][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[31][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[32][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[33][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[34][newLine];
delayMicroseconds(1);
PORTB = frameBuffer[35][newLine];
delayMicroseconds(1);

// klugdge to correct timings


PORTB = frameBuffer[36][newLine];
PORTB=PORTB;
PORTB=PORTB;
PORTB=PORTB;
delayMicroseconds(3);
}

//vsync
PORTB = _SYNC;

// if delaying for some reason


if(waiting)
{
// increment the waiting counter
waitingCount++;

// if waiting is over for a new game


if(waitingCount==DONE_WAITING & !justScored)
{
waitingCount=0;
// if on title screen
if(showingTitle)
{
// draw the loading bar

11 of 14
arduinopong - alastairparker

grayPixel(loadingBar,BASE_BAR);
loadingBar+=2;

// done loading
if (loadingBar > 37)
{
showingTitle=false;
clearScreen();
initGame();
initScreen();
waiting=false;
}
}
}

// waiting because someone scored


if(waitingCount==SCORE_WAITING & justScored)
{
justScored=false;
waitingCount=0;
waiting=false;
}
// wait for the remainder of the sync period
delayMicroseconds(305);
}
// not waiting
else
{
// increment the update delay counter
updateCounter++;
if( updateCounter >= UPDATE_INTERVAL)
{
//player movement
if(playerLeftTurn)
{
drawPaddle(PLAYER_LEFT_X,playerLeftOld,_BLACK);
playerLeft = analogRead(PLAYER_LEFT_PIN)>>6;
if(playerLeft<SCORE_BAR)
playerLeft=SCORE_BAR;
if(playerLeft>BASE_BAR)
playerLeft=BASE_BAR;
drawPaddle(PLAYER_LEFT_X,playerLeft,_WHITE);
playerLeftOld=playerLeft;
playerLeftTurn=false;
}
else
{
drawPaddle(PLAYER_RIGHT_X,playerRightOld,_BLACK);
playerRight = analogRead(PLAYER_RIGHT_PIN)>>6;
if(playerRight<SCORE_BAR)
playerRight=SCORE_BAR;
if(playerRight>BASE_BAR)
playerRight=BASE_BAR;
drawPaddle(PLAYER_RIGHT_X,playerRight,_WHITE);

12 of 14
arduinopong - alastairparker

playerRightOld=playerRight;
playerLeftTurn=true;
}
// remainder of sync period
delayMicroseconds(10);
updateCounter=0;
}
else
// remainder of sync period
delayMicroseconds(250);

// perform ball x velocity delay


ballXLoop--;
if(ballXLoop <= abs(ballXVel) and ballXVel != 0)
{
//ball movement;
drawBall(_BLACK);
updateBall(true);
ballXLoop=MAX_VEL;
}

// perform ball y velocity delay


ballYLoop--;
if(ballYLoop <= abs(ballYVel) and ballYVel != 0)
{
//ball movement;
drawBall(_BLACK);
updateBall(false);
ballYLoop=MAX_VEL;
}
// draw new ball
drawBall(_GRAY);
}
}

Schematic

13 of 14
arduinopong - alastairparker

Action Shot

alastair <dot> parker <at> gmail <dot> com

Σύνδεση σε Πρόσφατη δραστηριότητα ιστοτόπου Όροι Αναφορά κατάχρησης Εκτύπωση σελίδας | Με την
υποστήριξη των Ιστότοπων Google

14 of 14

You might also like