Composite (complex) Video 100% Straight C on a One-Dollar MCU!

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
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! :eek:


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

Last edited:

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
Phew, I still don't have to learn ASM (an insane person's language)

Possible FAQs:

Q: Can the resolution be increased?
A: Using an MCU with more ram will allow for this, particularly on the Y-axis. An over-clocked MCU will allow for even more yet again.

Q: Can I interface this to other projects which tells it what to put on the screen?
A: Yes, this is coming.

Q: Can the OR gates be substituted with diodes?
A: I think so. If they have a fast enough recovery time. Pretty sure.
 
Last edited:

SgtWookie

Joined Jul 17, 2007
22,230
As soon as I saw all those OR gates, diodes popped into my head.

Use eight 1N4146/1N914 switching diodes. Dirt cheap, low parasitic capacitance, and extremely fast - you'll be very hard-pressed to find something better/cheaper. You'll get rid of the propagation delay those three gates in series have, and you can eliminate those relatively long runs of W/W to the OR gates. BAT54's would also work for this; less of a Vf, also very fast switching, but they are SMD.

You'll need to decrease the value of the base resistor due to the diode drop. You should also add a base return resistor to speed the turn-off of Q1.

ASM isn't so bad; there are only about 37 instructions to learn for PICs. In an application like this, you need to wring every last bit of performance out of that uC possible - and that will be mighty tough to do in anything but assembler.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
Yeah I think that the diodes should be ok too. Although, the other option is the CD4078.

ASM isn't so bad? I think that most of its users are bananas. They're making video games by counting clock cycles with the source code so 'scattered' around the place it is unbelievable. It is totally unclear, and I cannot for the life of me understand how they manage to keep track of their programs without having some 'clarity' I managed to run into only but one piece of ASM code, which directly lead to towards success with this project. It actually made some sense and had some relevant comments in it.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
I think it could be useful 'as is', modified to suit the requirement -- but I do plan on doing another version with a serial interface which will allow people to interface their own projects to it.

Then possibly a third at a later stage that offers greater resolution; 80x45 would be a ripper.
 

Wendy

Joined Mar 24, 2008
23,421
I remember the Sinclar computer doing something similar, with a Z80! Gotta love computers.

Nice job.

If color could be added it would be a good video troubleshooting tool.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
Some guys have already cloned an 80's like computer using an 18F series PIC. Monochrome display, PS2 keyboard and an operating system similar to QBASIC.

Color? Oh, we'd need an MCU running at like 50MIPS for that.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
At least if there is a generic interface such as this, everyone wanting to make a game of Pong (or similar) -- can do it in a high-level language.

Google "PIC Pong" and it returns like 50K hits. We increase the resolution a bit and everyone can make a game of Pong probably.
 

SgtWookie

Joined Jul 17, 2007
22,230
Yeah I think that the diodes should be ok too. Although, the other option is the CD4078.
You'd still have a good bit of propagation delay through the '4078. If you use 1N4148/1N914 switching diodes, there is virtually no delay, and the turn-off time is around 4nS.

Just went to check on prices; a CD4078 will run you around $0.48/ea in small quantities.
Mouser has 1N4148's in stock for $0.03/ea if you are buying < 25:
http://mouser.com/search/ProductDet...y8ApvpFxPZg==&cm_mmc=findchips-_-na-_-na-_-na

Construction would be more of a pain with 8 diodes vs 1 IC.
You used to be able to buy barrier diode arrays, but the only such 8-diode arrays I've seen lately have common anodes. BAT54's are available in dual 2-diode common cathode configurations, but they're ~$0.55/ea in small quantities.

ASM isn't so bad? I think that most of its users are bananas.
Not really.
If you want to get to know how your uC works, and be able to put every machine cycle to use, then program in assembler. It's tough to do that with C, as even though many of the library functions are documented in how many cycles they'll take, there is more overhead associated with higher-level languages.

They're making video games by counting clock cycles with the source code so 'scattered' around the place it is unbelievable. It is totally unclear, and I cannot for the life of me understand how they manage to keep track of their programs without having some 'clarity' I managed to run into only but one piece of ASM code, which directly lead to towards success with this project. It actually made some sense and had some relevant comments in it.
You can program Assembler in a structured style. You can also put lots of comments in it, and use meaningful variable names. Unfortunately, people get lazy and fall into bad habits, leaving sloppy and undocumented code with cryptic variable names that's a nightmare to maintain.

I know that six months or six years down the road, I'm not going to remember all of the details about a program, so I better have darn good documentation in it, unless I want to have to basically code it all over again from scratch. I just don't have that much time available.

The further you get away from the machine by using high-level languages, the more computing power you will require to accomplish the same task as you would in a well-written low level language.
 

joeyd999

Joined Jun 6, 2011
5,283
Seems like quite a lot of bit banging to do something so (relatively) simple. Why not use the (synchronous mode) USART serial port to shift out your bits on one wire without the need to extract/recombine? This would save you about 24 to 32 instruction cycles per byte (back of the napkin calculation!), the timing will be entirely governed by hardware (as opposed to instruction cycles), and you'll have time to do something more than just show static video. In addition, the interrupt will give you a time base with which to generate your sync info...no need to keep such careful track of instruction cycles. You could then use the blanking intervals in the mainline code to update your bitmap.

BTW, i take great offence at being called bananas! I've made it clear here before that i only code in assembly. For some, C may be easier, but I can do things in assembly that would be impossible with C (without the addition of inline assembly code). A typical app for me consists of at least 10,000 lines of code, is highly readable, maintainable, and extensible. And, no, I don't generally have to keep track of individual instruction cycles (and i very rarely depend on instruction cycles to manage time for me). I let the hardware do that for me. That's what it is for.
 

thatoneguy

Joined Feb 19, 2009
6,359
It is a descent project, we haven't had a PIC to TV composite display project here other than pong a long while back.

Though there are some optimizations that could be made if coding in assembly, this is at least a starting point for others to work off of or expand from.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
BTW, i take great offence at being called bananas! I've made it clear here before that i only code in assembly. For some, C may be easier, but I can do things in assembly that would be impossible with C (without the addition of inline assembly code). A typical app for me consists of at least 10,000 lines of code, is highly readable, maintainable, and extensible. And, no, I don't generally have to keep track of individual instruction cycles (and i very rarely depend on instruction cycles to manage time for me). I let the hardware do that for me. That's what it is for.
You sound like the ASM programmer that I am looking for. I totally agree that C cannot / shouldn't be used for some things, in which case I would prefer to seek the services of someone who already knows ASM. Cheaper for me to pay for the odd app than spend 2-years learning another language. it took me a good 5 to become half decent with Visual Basic. I don't have another 5 to spend learning ASM. How much do you charge to code 10,000 lines?
 
Last edited:

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
It is a descent project, we haven't had a PIC to TV composite display project here other than pong a long while back.

Though there are some optimizations that could be made if coding in assembly, this is at least a starting point for others to work off of or expand from.
Thanks for the thumbs up. A much better one is coming which, will use an Atmel MCU. While not exactly a dollar, they do run at 20MIPS and have more ram.

For this project, more ram = more vertical resolution while more speed = more horizontal resolution.
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
There are enough gaps in it to create an application. Not all of the scan lines are used either. If you know C, then you could do a pong game with what I have posted. Res is a bit low though for a game.

Ultimately what I wanted was a way to do away with some output components on some projects, such as LCDs and LEDs, and instead display information on a TV. Everyone has a TV. Every project has both inputs and outputs. I cut a corner with cost if I use pre-existing outputs.

Cars now have screens too!
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,283
You sound like the ASM programmer that I am looking for. I totally agree that C cannot / shouldn't be used for some things, in which case I would prefer to seek the services of someone who already knows ASM. Cheaper for me to pay for the odd app than spend 2-years learning another language. it took me a good 5 to become half decent with Visual Basic. I don't have another 5 to spend learning ASM. How much do you charge to code 10,000 lines?
I am not here to sell my services...I have enough on my plate wrt my own product development. But thanks for the offer...

I understand what you mean re spending time that could be more valuable spent elsewhere. But, if you are a decent C programmer (VB is irrelevant, IMHO), I don't know why you think it would be so difficult to learn asm, especially PIC asm. There are only 30-odd instructions to learn (or at least know where to look up). Pretty much all the program flow structures in C are easily translated to BRAs, GOTOs, and "computed" GOTOs.

As someone who's programmed in dozens of different languages (and many different ASMs), I think PIC asm is the easiest of all. That's one of the reasons I pretty much dedicated my career to PICs since the first PIC silicon back in the early '90s.

I think one of C's greatest strengths is its memory management capabilities (especially run-time mm), but these capabilities are rarely required on such small pieces of hardware.

Yes, I'll admit that complex data structures (when required) can get complicated. But in many applications, it is these data structures that can gum up the works and cause execution bottlenecks, and there is little visibility as to how the compiler is approaching the problem (without referring to the compiler output, which is ASM, which you don't understand!). One nice thing about ASM is you can devise short-cuts that can solve the same problem as C would, but with far fewer instruction cycles.

BTW, I *love* coding in ASM. I'd still do it for fun if it didn't make me any money!
 

Thread Starter

T.Jackson

Joined Nov 22, 2011
328
I understand what you mean re spending time that could be more valuable spent elsewhere. But, if you are a decent C programmer (VB is irrelevant, IMHO), I don't know why you think it would be so difficult to learn asm, especially PIC asm
For me, VB has been totally relevant. I could not have gone straight onto doing C without having priors with BASIC and Java. I am not a genius, what can I say? Gifted perhaps, but I am no genius. We take all of this stuff for granted. I see red when I read the word "simple" -- this is BS hard stuff in my opinion.

I technically do know ASM, but I never applied it. I have a C grade (65%) pass on paper for my studies with the Z80 processor back in 99 at college.
 
Top