Tuesday, May 1, 2012

Arduino Composite TV out

I've been wanting to interface my Arduino with a monitor of some sort with RGB, but it didn't seem to be much of a possibility for such a memory limited device. But then i found this tvout library that uses composite video! It is only in black and white, but still pretty awesome. The first thing i thought of to do was make a space invaders type game with that Parallax joystick from some time ago...and its coming along quite well.

I have created a "starry" background and a ship bitmap so far(its a simple monochrome bitmap made in paint that can be converted to code and displayed using the library). Basically how it works: the loop reads the stick values, changes the X/Y coordinates of where the ship will be, then draws the "stars" and then the ship. This obviously happens many times a second so it looks fairly fluid on screen. So, currently I can move the ship around on a starry background and i'm starting to work on projectiles.

Here's some code in progress.
////////////////////BITMAPS!/////////////////////////////////////
//120 pixels wide,96 pixels high
PROGMEM const unsigned char BckGnd[] = {
  120,96,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x40,0x00,0x40,0x20,0x04,0x08,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x00,0x00,0x00,
  0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x00,0x40,0x00,
  0x04,0x02,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
  0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
  0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
  0x20,0x02,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x10,
  0x00,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x40,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
  0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
  0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
  0x20,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x10,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x02,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x10,
  0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
  0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
  0x00,0x40,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,
  0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
  0x20,0x00,0x40,0x00,0x08,0x10,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
  0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x04,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x08,0x10,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x00,
  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x10,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x04,
  0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
  0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x80,
  0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x02,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
  0x04,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
  0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,
  0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x00,0x10,
  0x00,0x00,0x00,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,
  0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x80,0x04,0x00,0x00,0x08,0x00,0x01,0x00,
  0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

PROGMEM const unsigned char Ship[] = {
  8,7,
  0x18,
  0x18,
  0x3C,
  0xBD,
  0xBD,
  0xFF,
  0xFF};

////////////////////END OF BITMAPS!/////////////////////////////////////

/*
RCA video plug:
 Sync(Pin 9) -> 1k -> center prong
 Video(pin 7) -> 470ohm -> center prong
 Gnd -> outside casing
 */

#include <TVout.h>
#include <fontALL.h>

TVout TV; //init the TV object

//resolution of output
#define TVresW 120
#define TVresH 96

// x/y limits for ship
#define MaxRight (TVresW-8)
#define MaxDown (TVresH -7)

//this program uses a lot of memory, so i use bytes here because they are large enough for this purpose.
//where the ship is on screen
byte CurXPos = (TVresW-8)/2; //centers ship horizontally
byte CurYPos = TVresH -7; //(total height) - 7(ship height), moves ship to bottom

#define XaxisPin A2
int Xval=0; //raw stick data, x axis
#define YaxisPin A1
int Yval=0; //raw stick data, y axis

#define FireBtn 0 //aka digital 2 interrupt
volatile long lastDebounceTime = 0;   // the last time the interrupt was triggered
#define debounceDelay  10    // the debounce time; decrease if quick button presses are ignored
volatile int fireState = LOW; //initial button state
volatile long lastFire = 0;
byte FireY =0;
byte FireX = 0;

void setup()
{
  //Serial.begin(115200);

  attachInterrupt(FireBtn,FireEvent, RISING); //fire button interrupt

  TV.begin(NTSC,TVresW,TVresH); //initialize TV
  delay(1000); //wait for TV to detect input

  TV.bitmap(0,0,BckGnd); //displays the "stars"
  TV.select_font(font8x8); //each char is 8x8 pixels
  TV.print((TVresW-80)/2,(TVresH-8)/2,"Ship Test!"); //80 pixel long message, 8 tall
  TV.bitmap(CurXPos,CurYPos,Ship); //displays the ship

  delay(250);
}
void loop()
{
  GetSticks(); //get raw data
  MoveShip(); //move ship accordingly on screen
  CheckFire(); //testing
}

void GetSticks()
{
  Yval = analogRead(YaxisPin);
  Xval = analogRead(XaxisPin);
  /*
  Serial.print(Xval);
   Serial.print(" ");
   Serial.println(Yval);
   */
  if(Xval < 512) //move left
  {
    Xval = map(Xval,512,0,0,-4);
  }
  else//its >= 512
  {
    Xval = map(Xval,512,1023,0,4); //move right
  }

  if(Yval < 512) //move down
  {
    Yval = map(Yval,512,0,0,4);
  }
  else//its >= 512 //move up
  {
    Yval = map(Yval,512,1023,0,-4);
  }
}

void MoveShip()
{
  //get new pos
  CurXPos += Xval;
  CurYPos += Yval;

  if (CurXPos > MaxRight) //check horizontal limits
  {
    CurXPos = 0;
  }
  else if(CurXPos <= 0)
  {
    CurXPos = MaxRight;
  }

  if (CurYPos > MaxDown) //check vertical limits
  {
    CurYPos = 0;
  }
  else if(CurYPos <= 0)
  {
    CurYPos = MaxDown;
  }
  //clear screen + display new ship
  TV.bitmap(0,0,BckGnd); // used to be "TV.clear_screen();", but the BckGnd bitmap covers the enite screen, so not needed.
  TV.bitmap(CurXPos,CurYPos,Ship);
  /*
  Serial.print(CurXPos);
   Serial.print(",");
   Serial.println(CurYPos);
   */
}
/*
void DrawFire() //works well at drawing a bullet, but stops the program whiles its doing it because of the for loop.
{
  byte FireX = CurXPos + 4;
  byte FireY = CurYPos;

  for(byte i = FireY; i>=0; i--)
  {
    TV.set_pixel(FireX,i+1,0);
    delay(2);
    TV.set_pixel(FireX,i,1);
    delay(2);
  }
}
*/


//untested attempt at "drawing" fire(a bullet)
void FireEvent()
{
  if ((millis() - lastDebounceTime) > debounceDelay)
  {
    lastDebounceTime = millis();
 
    FireX = CurXPos + 4;
    FireY = CurYPos;
    fireState = true;
 
    TV.set_pixel(FireX,FireY+1,0);
    TV.set_pixel(FireX,FireY,1);
  }
}

void CheckFire()
{
  if(fireState && millis() > (lastFire + 2) && FireY >0)
  {
    FireY--;
    lastFire = millis();
 
    TV.set_pixel(FireX,FireY+1,0);
    TV.set_pixel(FireX,FireY,1);
  }
}

No comments:

Post a Comment