Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/* NTSC, take 3.14159265
 * -------------------------------------
 * Created Sun Aug 26 14:59:29 EDT 2007
 *
 * (cleft) 2007 by Matthieu Lalonde
 *
 * Note: Ideal values for the resistors are 300ohm and 900ohm.
 *          Using this circuit: http://www.eyetap.org/ece385/vinfo_da00.png
 * Bug(s):
 *      - Jittering of the first one of two lines (should they be skipped?)
 */

#include "WProgram.h"

/**
 * Constants definition
**/
// Debug levels
#define _debugLevelDisable      0
#define _debugLevelGeneric      1
#define _debugLevelSignal       10
#define _debugLevelSimul        20
/**
 * Notes about _debugLevelSignal:
 * 
 * Using this mode you can go through each voltage levels (Sync, black, gray, white).
 * This is useful for testing the circuit as you can make sure the voltages are right by using a multimeter
**/

// Select either of the above debug levels (_debugLevelDisable to disable debugging)
#define _debugMode              _debugLevelGeneric

// Pins definition
#if _debugMode > _debugLevelDisable
#define _pinLED                 5   // Do not use a pin from 8 to 13 or it'll be turn off automatically
#endif
#define _pinSync                8
#define _pinVideo               9

// 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, yet)
#define _tvMode                 _NTSC

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

#define _ntscDelayHSyncStart    4.7
#define _ntscDelayBackPorch     6   /* Normally 5.9, but this fixes a timing issue */
#define _ntscDelayFrontPorch    1.4
#define _ntscDelayPerLine       51.5
#define _ntscDelayVSync         58.8
#endif

#if _tvMode == _PAL
    /* Nien */
#endif

#define _tvPixelWidth           21
#define _tvPixelHeight          16

#define _serialBauteRate        19200

/**
 * Prototypes definition
**/
void generateVSync(void);
void writeBufferLine(int position);
void writeTVLines(void);
void clearFrameBuffer(void);
void fillWhiteFrameBuffer(void);
void fillGrayFrameBuffer(void);
void fillPatternFrameBuffer(void);
void loadSprite(void);

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

// Defines a static sprite (this could come from EEPROM also)
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) {
    clearFrameBuffer();
    
    pinMode(_pinVideo, OUTPUT);
    pinMode(_pinSync, OUTPUT);
    
#if _debugMode > _debugLevelDisable
    pinMode(_pinLED, OUTPUT);
    digitalWrite(_pinLED, HIGH);
    
    // Draws a dot in the middle
    frameBuffer[(_tvPixelWidth / 2)][(_tvPixelHeight / 2)] = _WHITE;
#endif
    
    Serial.begin(_serialBauteRate);
    Serial.print("Mode: ");
}

void loop(void) {
#if _debugMode < _debugLevelSignal
    // Write the tv lines
    writeTVLines();
#endif
    
    // Check for commands
    if (Serial.available())
    {
        c = Serial.read();
        Serial.println(c);
        
        switch (c)
        {
#if _debugMode < _debugLevelSignal
            case 'w' :
            fillWhiteFrameBuffer();     // Fill the display with white
            break;
            case 'g' :
            fillGrayFrameBuffer();      // Fill the display with gray
            break;
            case 'p' :
            fillPatternFrameBuffer();   // Fill the display with a calculated patter
            break;
            case 'c' :
            clearFrameBuffer();         // Clear the display (all black)
            break;
            case 's' :
            loadSprite();               // Load a sprite to the display
            break;
#else
        /**
            This section is used for sinal debug mode only!
            Use a multimeter to check the tv signal voltages
        **/
            case '1' :
            PORTB   = _SYNC;
            break;
            case '2' :
            PORTB   = _BLACK;
            break;
            case '3' :
            PORTB   = _GRAY;
            break;
            case '4' :
            PORTB   = _WHITE;
            break;
#endif      
            /**
             * Display usage
            **/
            default :
#if _debugMode < _debugLevelSignal
            Serial.println("c: clear screen | p: fill with pattern 1 | s: fill with sprite | w: fill with white | g: fill with gray");
#else
            Serial.println("Debug Levels >> 1: _SYNC | 2: _BLACK | 3: _GRAY | 4: _WHITE");
#endif
            break;
        }
        
        Serial.print("Mode: ");
    }
}

/**
 * Begin the NTSC Specific block
**/
#if _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 every line from what's inside the frameBuffer
 * 
 * Warning: You cannot take the beginning and end sync out of this functions or the sync will fail
 *          (This has been tested on a Mega8)
**/
void writeTVLines(void)
{
    unsigned int i;
    for (i=0;i<(_tvNbrLines - _tvVSyncNbrLines);i++) // Correction for the 20 lines of vertical sync
    {
        /* Begin Line Sync */
        
        // H Sync
        PORTB = _SYNC;
        delayMicroseconds(_ntscDelayHSyncStart);
        
        // Back Porch
        PORTB = _BLACK;
        delayMicroseconds(_ntscDelayBackPorch);

        writeBufferLine(i>>4);  // Visible scan (51.5µs)

        /* End Line Sync (Front Porch) */
        PORTB = _BLACK;
        delayMicroseconds(_ntscDelayFrontPorch);
    }

    // Vertical Sync follows
    generateVSync();    // _tvVSyncNbrLines lines for vertical sync
}

/**
 * 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 during the proper timing
        delayMicroseconds((_ntscDelayPerLine / _tvPixelWidth));
    }
}

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

        PORTB = _SYNC;  delayMicroseconds(_ntscDelayVSync);
    }
}

/**
 * End of the NTSC Specific block
**/
#endif

/**
 * Clears the frame buffer (if mode is true it will fill it with a chess board pattern)
**/
void clearFrameBuffer(void)
{
    byte index, index2;
    
    for (index2 = 0; index2 < _tvPixelHeight; index2++)
    {
        for (index = 0; index < _tvPixelWidth; index++)
        {
            frameBuffer[index][index2] = _BLACK;
        }
    }
}

/**
 * Fills the frame buffer with white
**/ 
void fillWhiteFrameBuffer(void)
{
    byte index, index2;
    
    for (index2 = 0; index2 < _tvPixelHeight; index2++)
    {
        for (index = 0; index < _tvPixelWidth; index++)
        {
            frameBuffer[index][index2] = _WHITE;
        }
    }
}

/**
 * Fills the frame buffer with gray
**/ 
void fillGrayFrameBuffer(void)
{
    byte index, index2;
    
    for (index2 = 0; index2 < _tvPixelHeight; index2++)
    {
        for (index = 0; index < _tvPixelWidth; index++)
        {
            frameBuffer[index][index2] = _GRAY;
        }
    }
}

/**
 * Fills the frame buffer with a pattern
**/ 
void fillPatternFrameBuffer(void)
{
    byte index, index2;
    
    for (index2 = 0; index2 < _tvPixelHeight; index2++)
    {
        for (index = 0; index < _tvPixelWidth; index++)
        {
            frameBuffer[index][index2] = (index + index2) % 3 + 1;
        }
    }
}

/**
 * Copies a sprite to the frameBuffer
**/
void loadSprite(void)
{
    unsigned int ii,jj;
    for (ii=0;ii<_tvPixelWidth;ii++)
    {
        for(jj=0;jj<_tvPixelHeight;jj++)
        {
            frameBuffer[ii][jj] = graphic[ii][jj];
        }
    }
}