Composite NTSC 40x16 Video, 100% Straight C on a One-Dollar MCU! This thing almost sent me around the twist trying to figure out. The C language isn't meant for this; but it is doing it! 
Fig1. Modern Clarion TFT screen showing signal
Fig2. Electrical Schematic
Why the OR gates? This is what makes this project possible. The OR gates save the MCU a lot of processing time, and resources by not having to do the same job in software; convert a byte to a bit!
Fig3. Rough Prototype Build
Fig4. Full Res 40x16 Showing
Fig1. Modern Clarion TFT screen showing signal
Fig2. Electrical Schematic
Why the OR gates? This is what makes this project possible. The OR gates save the MCU a lot of processing time, and resources by not having to do the same job in software; convert a byte to a bit!
Fig3. Rough Prototype Build
Fig4. Full Res 40x16 Showing
Rich (BB code):
/*
* PROJECT: ---------------------------------------------------
-- Monochrome Composite Video Frame Buffer 40x16 --
---------------------------------------------------
* Copyright: None
* MCU:
PIC16F628a @20MHz
* Compiler:
mikroC PRO C ...
* Revision:
0.1a | 04/12/11
* Status:
Tested and working
(additional requirements to be later implemented)
*/
// ---------------------------------------------------
// - NTSC Composite Video Technical Specifications -
// ---------------------------------------------------
// Interlaced lines: 524 | Mode not used
// Lines per field: 262 | 128 lines used
// Active video: 52.6uS | 52uS used
// Scanline: 63.5uS | 64US used
// HSYNC: 4.8us | ~4uS used
// HBLANK: 10.9uS | ~12uS used
// VRefresh: 60Hz | ~60Hz used
// VSYNC: 3 x HSYNC + 3 x HBLANK
// + 3 x HSYNC pulses
// ---------------
// :: Video DAC ::
// ---------------
// 270R
// Video o--/\/\--|
// 1.0K |
// Sync o--/\/\--|----o TV
// \
// / 75R
// \
// o
// GND
#define Sync PORTA.F0
unsigned short cache[5][16];
unsigned short segmentColumn;
unsigned short segmentHeight;
unsigned short totalScanlines;
unsigned short i;
void verticalSYNCa()
{
// Vertical blanking every 60 x p/second / 60Hz
// a. Pre-equalizing pulses
for (i = 0; i <6; i++)
{
Sync = 0; Delay_us(2);
asm nop; asm nop;
Sync = 1; Delay_us(31);
asm nop; asm nop;
}
// b. Equalizing pulses ...
for (i = 0; i <6; i++)
{
Sync = 0; Delay_us(27);
asm nop;
Sync = 1; Delay_us(4);
asm nop; asm nop;
}
// c. Post-equalizing pulses
for (i = 0; i <6; i++)
{
Sync = 0; Delay_us(2);
asm nop; asm nop;
Sync = 1; Delay_us(31);
asm nop; asm nop;
}
}
void main()
{
CMCON = 7; // Disable analog comparators
TRISA = 0x00; // Set port as all outputs
TRISB = 0x00; // All outputs ...
PORTA = 0x00; // Init port -- all pins low
PORTB = 0x00; // Init port -- all pins low
// The 40x15 Bitmap to render (default = "HELLO")
cache[0][0] =0b11111111; cache[1][0] =0b11111111; cache[2][0] =0b11111111; cache[3][0] =0b11111111; cache[4][0] =0b11111111;
cache[0][1] =0b00000000; cache[1][1] =0b00000000; cache[2][1] =0b00000000; cache[3][1] =0b00000000; cache[4][1] =0b00000000;
cache[0][2] =0b00000000; cache[1][2] =0b00000000; cache[2][2] =0b00000000; cache[3][2] =0b00000000; cache[4][2] =0b00000000;
cache[0][3] =0b00000000; cache[1][3] =0b00000000; cache[2][3] =0b00000000; cache[3][3] =0b00000000; cache[4][3] =0b00000000;
cache[0][4] =0b00000000; cache[1][4] =0b00000000; cache[2][4] =0b00000000; cache[3][4] =0b00000000; cache[4][4] =0b00000000;
cache[0][5] =0b10000010; cache[1][5] =0b01111110; cache[2][5] =0b01000000; cache[3][5] =0b01000000; cache[4][5] =0b01111111;
cache[0][6] =0b10000010; cache[1][6] =0b01000000; cache[2][6] =0b01000000; cache[3][6] =0b01000000; cache[4][6] =0b01000001;
cache[0][7] =0b11111110; cache[1][7] =0b01111110; cache[2][7] =0b01000000; cache[3][7] =0b01000000; cache[4][7] =0b01000001;
cache[0][8] =0b10000010; cache[1][8] =0b01000000; cache[2][8] =0b01000000; cache[3][8] =0b01000000; cache[4][8] =0b01000001;
cache[0][9] =0b10000010; cache[1][9] =0b01111110; cache[2][9] =0b01111110; cache[3][9] =0b01111110; cache[4][9] =0b01111111;
cache[0][10]=0b00000000; cache[1][10]=0b00000000; cache[2][10]=0b00000000; cache[3][10]=0b00000000; cache[4][10]=0b00000000;
cache[0][11]=0b00000000; cache[1][11]=0b00000000; cache[2][11]=0b00000000; cache[3][11]=0b00000000; cache[4][11]=0b00000000;
cache[0][12]=0b00000000; cache[1][12]=0b00000000; cache[2][12]=0b00000000; cache[3][12]=0b00000000; cache[4][12]=0b00000000;
cache[0][13]=0b00000000; cache[1][13]=0b00000000; cache[2][13]=0b00000000; cache[3][13]=0b00000000; cache[4][13]=0b00000000;
cache[0][14]=0b00000000; cache[1][14]=0b00000000; cache[2][14]=0b00000000; cache[3][14]=0b00000000; cache[4][14]=0b00000000;
cache[0][15]=0b11111111; cache[1][15]=0b11111111; cache[2][15]=0b11111111; cache[3][15]=0b11111111; cache[4][15]=0b11111111;
// Produce stream of vertical sync pules denoting the start of a new field / frame
verticalSYNCa();
while(1)
{
// ----------------------------
// - Horizontal Synchronization -
// ----------------------------
Sync = 0; Delay_us(2);
asm nop; asm nop; // 0V
// ----------------------------
Sync = 1; Delay_us(4);
asm nop; asm nop; // .3V
// ----------------------------
// Horizontal Scanlines ...
// a. Bitwise compare the bytes to see if the bits are set for the current X,Y screen segments
// b. Write value of bit to PORTB is set (has to be done like this to get the speed)
if (totalScanlines > 75 & totalScanlines < 203) // Drawing on lines 75 thru 203 total of 128 traces
{
// Bits mask and BLAST out the bits in the bitmap for each line trace!
PORTB = cache[0][segmentColumn] & 128; PORTB = cache[0][segmentColumn] & 64; PORTB = cache[0][segmentColumn] & 32; PORTB = cache[0][segmentColumn] & 16;
PORTB = cache[0][segmentColumn] & 8; PORTB = cache[0][segmentColumn] & 4; PORTB = cache[0][segmentColumn] & 2; PORTB = cache[0][segmentColumn] & 1;
PORTB = cache[1][segmentColumn] & 128; PORTB = cache[1][segmentColumn] & 64; PORTB = cache[1][segmentColumn] & 32; PORTB = cache[1][segmentColumn] & 16;
PORTB = cache[1][segmentColumn] & 8; PORTB = cache[1][segmentColumn] & 4; PORTB = cache[1][segmentColumn] & 2; PORTB = cache[1][segmentColumn] & 1;
PORTB = cache[2][segmentColumn] & 128; PORTB = cache[2][segmentColumn] & 64; PORTB = cache[2][segmentColumn] & 32; PORTB = cache[2][segmentColumn] & 16;
PORTB = cache[2][segmentColumn] & 8; PORTB = cache[2][segmentColumn] & 4; PORTB = cache[2][segmentColumn] & 2; PORTB = cache[2][segmentColumn] & 1;
PORTB = cache[3][segmentColumn] & 128; PORTB = cache[3][segmentColumn] & 64; PORTB = cache[3][segmentColumn] & 32; PORTB = cache[3][segmentColumn] & 16;
PORTB = cache[3][segmentColumn] & 8; PORTB = cache[3][segmentColumn] & 4; PORTB = cache[3][segmentColumn] & 2; PORTB = cache[3][segmentColumn] & 1;
PORTB = cache[4][segmentColumn] & 128; PORTB = cache[4][segmentColumn] & 64; PORTB = cache[4][segmentColumn] & 32; PORTB = cache[4][segmentColumn] & 16;
PORTB = cache[4][segmentColumn] & 8; PORTB = cache[4][segmentColumn] & 4; PORTB = cache[4][segmentColumn] & 2; PORTB = cache[4][segmentColumn] & 1;
// Inc segment height (the lines repeat with the same bits (n) times allowing to fill the whole screen)
segmentHeight++;
}
else
{
Delay_us(43);
asm nop;
asm nop;
}
// Trace off ...
PORTB = 0;
// Fine tine delay to arrive at 64uS / 15.625KHz per horizontal sweep
Delay_us(6);
// Inc scan line no. (current line in scope)
totalScanlines++;
// Must always have equalized time very critical / tight timing (one IF condition per test)
if (segmentHeight == 8)
{
segmentColumn++;
}
if (segmentHeight == 8)
{
segmentHeight = 0;
}
// Frame complete if this IS scanline no.250
if (totalScanlines == 250)
{
// Fine tine delay to arrive at 60Hz vertical refresh
Delay_us(10); // close enough with this offset
// Reset vars ready for next frame ...
totalScanlines = 0;
segmentHeight = 0;
segmentColumn = 0;
// Tell the screen that a new image is coming
verticalSYNCa();
}
}
}
Attachments
-
142.4 KB Views: 188
-
133.3 KB Views: 182
-
161.7 KB Views: 195
-
127.2 KB Views: 174
-
8.2 KB Views: 25
-
146.8 KB Views: 173
Last edited: