/* NTSC, take 2
* ---------------
* Created Sun Aug 26 14:59:29 EDT 2007
*
* (cleft) 2007 by Matthieu Lalonde
*
* Note: Ideal values for the resistors are 900ohm and 330ohm!
*
*/
#include "WProgram.h"

#define _debugMode true

// Définition des pins
#define _pinSync 8
#define _pinVideo 9
#define _pinLED 12

// Definition of signal levels for the AD 2-bit converter
#define _SYNC 0x00 /* 0.00v */
#define _BLACK 0x01 /* 0.33v */
#define _GRAY 0x02 /* 0.67v */
#define _WHITE 0x03 /* 1.00v */

// Defines TV Modes
#define _NTSC 1
#define _PAL 2

// Select a TV mode (PAL isn't implemented)
#define _tvMode _NTSC

#ifdef _tvMode == _NTSC
#define _tvNbrLines 262 /* Includes the last 20 lines for the vertical sync! */
#define _tvVSyncNbrLines 20 /* These 20 lines... */
#endif

#define _tvPixelWidth 21
#define _tvPixelHeight 16

#define _serialBauteRate 19200

// Prototypes
void startHSync(void);
void endHSync(void);
void generateVSync(void);
void writeBufferLine(int position);
void writeTVLines(void);
void clearFrameBuffer(boolean mode);

byte frameBuffer[_tvPixelWidth][_tvPixelHeight]; // Video frame buffer
char c; // Serial buffer

#ifdef _tvMode == _NTSC
/**
NTSC Signal definition:
-- Horizontal sync (hsync) pulse: Start each scanline with 0.3V,
then 0V for 4.7us (microseconds), and then back to 0.3V. This tells the TV to start drawing a new scanline

-- The "Back Porch": A transition region of 0.3V for 5.9us between the
hsync pulse and the visible region, off the left edge of the TV

-- Visible scan region: This is the part you actually see.
0.3V shows up as black, 1V as white, everything in between is greyscale. The visible region lasts for 51.5us

-- The "Front Porch": A transition region of 0.3V for 1.4us before
the hsync pulse of the next line, off the right edge of the TV

-- Vertical sync (vsync) pulse: Lines 243-262 of each frame (off the bottom of the TV)
start with 0.3V for 4.7us, and the rest is 0V. This tells the TV to prepare for a new frame.
Think of it as just 0V with an inverted sync pulse

*** http://www.eyetap.org/ece385/lab5.htm ***
**/

// Writes each pixel of a line
void writeBufferLine(int position)
{
unsigned int ii;

for (ii = 0; ii < _tvPixelWidth; ii++)
{
PORTB = frameBuffer[ii][position];

// Waste some time to make sure the line is sent with proper timing
if (_tvPixelWidth < 51)
delayMicroseconds((51.5 / _tvPixelWidth));
else
delayMicroseconds((_tvPixelWidth / 51.5));
}
}

// Generate sync pulse for a new line
void startHSync(void)
{
// Begin H Sync
PORTB = _SYNC; delayMicroseconds(4.7);

// Back Porch
PORTB = _BLACK; delayMicroseconds(5.9);

// Visible scan (51.5µs) follows
}

// Generate sync pulse for the end of a line
void endHSync(void)
{
// Front Porch
PORTB = _BLACK; delayMicroseconds(1.4);
}

// Generate sync pulse for the virtual sync (lasts 20 lines)
void generateVSync(void)
{
unsigned int ii;
for (ii = 0; ii < _tvVSyncNbrLines; ii++)
{
// Begin V Sync
PORTB = _BLACK; delayMicroseconds(4.7);

PORTB = _SYNC; delayMicroseconds(58.8);
}
}
#endif

// Writes every line from what's inside the frameBuffer
void writeTVLines(void)
{
unsigned int i;
for (i=0;i<(_tvNbrLines - _tvVSyncNbrLines);i++) // Includes correction for the vertical sync
{
startHSync();

// Write the line
writeBufferLine(i>>4);

endHSync();
}

// Vertical Sync
generateVSync();
}

// Clears the frame buffer (if mode is true it will fill it with a chess board pattern)
void clearFrameBuffer(boolean mode)
{
byte index, index2;

for (index2 = 0; index2 < _tvPixelHeight; index2++)
{
for (index = 0; index < _tvPixelWidth; index++)
{
if (!mode) {
frameBuffer[index][index2] = _BLACK;
} else {
frameBuffer[index][index2] = (index + index2) % 3 + 1;
}
}
}
}

// Defines a static sprite
const static byte graphic[_tvPixelWidth][_tvPixelHeight] = {
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY },
{_BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE, _BLACK, _WHITE },
{_GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY, _GRAY }};

void setup(void) {
Serial.begin(_serialBauteRate);
Serial.println("Arduino Initialized!");
Serial.println((long int) (sizeof frameBuffer));

Serial.print("Mode: ");

pinMode(_pinVideo, OUTPUT);
pinMode(_pinSync, OUTPUT);

clearFrameBuffer(false);
frameBuffer[(_tvPixelWidth / 2)][(_tvPixelHeight / 2)] = _WHITE;
}

void loop(void) {
// Write the tv lines
writeTVLines();

// Check for commands
if (Serial.available())
{
c = Serial.read();
switch (c)
{
case 'c' :
Serial.println(); Serial.println("Clearing screen");
clearFrameBuffer(false);
break;

case 'f' :
Serial.println(); Serial.println("Filling with pattern");
clearFrameBuffer(true);
break;

case 'g' :
Serial.println(); Serial.println("Filling with graphic");
int ii,jj;
for (ii=0;ii<_tvPixelWidth;ii++)
{
for(jj=0;jj<_tvPixelHeight;jj++)
{
frameBuffer[ii][jj] = graphic[ii][jj];
}
}
break;
default :
Serial.println();
Serial.println("c: clear screen | f: fille with pattern 1 | g: fill graphic pattern");
break;
}

Serial.print("Mode: ");
}
}