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

Discussion in 'The Projects Forum' started by T.Jackson, Dec 3, 2011.

  1. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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

    Code ( (Unknown Language)):
    3. /*
    4.  * PROJECT:   ---------------------------------------------------
    5.               -- Monochrome Composite Video Frame Buffer 40x16 --
    6.               ---------------------------------------------------
    8.  * Copyright: None
    10.  * MCU:
    11.               PIC16F628a @20MHz
    12.  * Compiler:
    13.               mikroC PRO C ...
    14.  * Revision:
    15.               0.1a | 04/12/11
    16.  * Status:
    17.               Tested and working
    18.               (additional requirements to be later implemented)
    20. */
    21. //            ---------------------------------------------------
    22. //            -  NTSC Composite Video Technical Specifications -
    23. //            ---------------------------------------------------
    24. //                Interlaced lines: 524    |  Mode not used
    25. //                Lines per field:  262    |  128 lines used
    26. //                Active video:     52.6uS |  52uS used
    27. //                Scanline:         63.5uS |  64US used
    28. //                HSYNC:            4.8us  |  ~4uS used
    29. //                HBLANK:           10.9uS | ~12uS used
    30. //                VRefresh:         60Hz   | ~60Hz used
    31. //                VSYNC:            3 x HSYNC + 3 x HBLANK
    32. //                                  + 3 x HSYNC pulses
    34. //            ---------------
    35. //            :: Video DAC ::
    36. //            ---------------
    37. //            270R
    38. //  Video  o--/\/\--|
    39. //            1.0K  |
    40. //  Sync   o--/\/\--|----o TV
    41. //                  \
    42. //                  / 75R
    43. //                  \
    44. //                  o
    45. //                 GND
    47. #define Sync PORTA.F0
    49. unsigned short cache[5][16];
    50. unsigned short segmentColumn;
    51. unsigned short segmentHeight;
    52. unsigned short totalScanlines;
    53. unsigned short i;
    55. void verticalSYNCa()
    56. {
    57.      // Vertical blanking every 60 x p/second / 60Hz
    59.      // a. Pre-equalizing pulses
    60.      for (i = 0; i <6; i++)
    61.      {
    62.         Sync = 0; Delay_us(2);
    63.         asm nop; asm nop;
    64.         Sync = 1; Delay_us(31);
    65.         asm nop; asm nop;
    66.      }
    68.      // b. Equalizing pulses ...
    69.      for (i = 0; i <6; i++)
    70.      {
    71.         Sync = 0; Delay_us(27);
    72.         asm nop;
    73.         Sync = 1; Delay_us(4);
    74.         asm nop; asm nop;
    75.      }
    77.      // c. Post-equalizing pulses
    78.      for (i = 0; i <6; i++)
    79.      {
    80.         Sync = 0; Delay_us(2);
    81.         asm nop; asm nop;
    82.         Sync = 1; Delay_us(31);
    83.         asm nop; asm nop;
    84.      }
    85. }
    87. void main()
    88. {
    89.   CMCON = 7;     // Disable analog comparators
    90.   TRISA = 0x00;  // Set port as all outputs
    91.   TRISB = 0x00;  // All outputs ...
    92.   PORTA = 0x00;  // Init port -- all pins low
    93.   PORTB = 0x00;  // Init port -- all pins low
    95.   // The 40x15 Bitmap to render (default = "HELLO")
    96.   cache[0][0] =0b11111111; cache[1][0] =0b11111111; cache[2][0] =0b11111111; cache[3][0] =0b11111111; cache[4][0] =0b11111111;
    97.   cache[0][1] =0b00000000; cache[1][1] =0b00000000; cache[2][1] =0b00000000; cache[3][1] =0b00000000; cache[4][1] =0b00000000;
    98.   cache[0][2] =0b00000000; cache[1][2] =0b00000000; cache[2][2] =0b00000000; cache[3][2] =0b00000000; cache[4][2] =0b00000000;
    99.   cache[0][3] =0b00000000; cache[1][3] =0b00000000; cache[2][3] =0b00000000; cache[3][3] =0b00000000; cache[4][3] =0b00000000;
    100.   cache[0][4] =0b00000000; cache[1][4] =0b00000000; cache[2][4] =0b00000000; cache[3][4] =0b00000000; cache[4][4] =0b00000000;
    101.   cache[0][5] =0b10000010; cache[1][5] =0b01111110; cache[2][5] =0b01000000; cache[3][5] =0b01000000; cache[4][5] =0b01111111;
    102.   cache[0][6] =0b10000010; cache[1][6] =0b01000000; cache[2][6] =0b01000000; cache[3][6] =0b01000000; cache[4][6] =0b01000001;
    103.   cache[0][7] =0b11111110; cache[1][7] =0b01111110; cache[2][7] =0b01000000; cache[3][7] =0b01000000; cache[4][7] =0b01000001;
    104.   cache[0][8] =0b10000010; cache[1][8] =0b01000000; cache[2][8] =0b01000000; cache[3][8] =0b01000000; cache[4][8] =0b01000001;
    105.   cache[0][9] =0b10000010; cache[1][9] =0b01111110; cache[2][9] =0b01111110; cache[3][9] =0b01111110; cache[4][9] =0b01111111;
    106.   cache[0][10]=0b00000000; cache[1][10]=0b00000000; cache[2][10]=0b00000000; cache[3][10]=0b00000000; cache[4][10]=0b00000000;
    107.   cache[0][11]=0b00000000; cache[1][11]=0b00000000; cache[2][11]=0b00000000; cache[3][11]=0b00000000; cache[4][11]=0b00000000;
    108.   cache[0][12]=0b00000000; cache[1][12]=0b00000000; cache[2][12]=0b00000000; cache[3][12]=0b00000000; cache[4][12]=0b00000000;
    109.   cache[0][13]=0b00000000; cache[1][13]=0b00000000; cache[2][13]=0b00000000; cache[3][13]=0b00000000; cache[4][13]=0b00000000;
    110.   cache[0][14]=0b00000000; cache[1][14]=0b00000000; cache[2][14]=0b00000000; cache[3][14]=0b00000000; cache[4][14]=0b00000000;
    111.   cache[0][15]=0b11111111; cache[1][15]=0b11111111; cache[2][15]=0b11111111; cache[3][15]=0b11111111; cache[4][15]=0b11111111;
    113.   // Produce stream of vertical sync pules denoting the start of a new field / frame
    114.   verticalSYNCa();
    116.   while(1)
    117.   {
    118.      //  ----------------------------
    119.      // - Horizontal Synchronization -
    120.      //  ----------------------------
    121.            Sync = 0; Delay_us(2);
    122.            asm nop; asm nop; //  0V
    123.      //  ----------------------------
    124.            Sync = 1; Delay_us(4);
    125.            asm nop; asm nop; // .3V
    126.      //  ----------------------------
    128.      // Horizontal Scanlines ...
    129.      // a. Bitwise compare the bytes to see if the bits are set for the current X,Y screen segments
    130.      // b. Write value of bit to PORTB is set (has to be done like this to get the speed)
    132.      if (totalScanlines > 75 & totalScanlines < 203)  // Drawing on lines 75 thru 203 total of 128 traces
    133.      {
    134.         // Bits mask and BLAST out the bits in the bitmap for each line trace!
    135.         PORTB = cache[0][segmentColumn] & 128; PORTB = cache[0][segmentColumn] & 64; PORTB = cache[0][segmentColumn] & 32; PORTB = cache[0][segmentColumn] & 16;
    136.         PORTB = cache[0][segmentColumn] &   8; PORTB = cache[0][segmentColumn] &  4; PORTB = cache[0][segmentColumn] &  2; PORTB = cache[0][segmentColumn] &  1;
    137.         PORTB = cache[1][segmentColumn] & 128; PORTB = cache[1][segmentColumn] & 64; PORTB = cache[1][segmentColumn] & 32; PORTB = cache[1][segmentColumn] & 16;
    138.         PORTB = cache[1][segmentColumn] &   8; PORTB = cache[1][segmentColumn] &  4; PORTB = cache[1][segmentColumn] &  2; PORTB = cache[1][segmentColumn] &  1;
    139.         PORTB = cache[2][segmentColumn] & 128; PORTB = cache[2][segmentColumn] & 64; PORTB = cache[2][segmentColumn] & 32; PORTB = cache[2][segmentColumn] & 16;
    140.         PORTB = cache[2][segmentColumn] &   8; PORTB = cache[2][segmentColumn] &  4; PORTB = cache[2][segmentColumn] &  2; PORTB = cache[2][segmentColumn] &  1;
    141.         PORTB = cache[3][segmentColumn] & 128; PORTB = cache[3][segmentColumn] & 64; PORTB = cache[3][segmentColumn] & 32; PORTB = cache[3][segmentColumn] & 16;
    142.         PORTB = cache[3][segmentColumn] &   8; PORTB = cache[3][segmentColumn] &  4; PORTB = cache[3][segmentColumn] &  2; PORTB = cache[3][segmentColumn] &  1;
    143.         PORTB = cache[4][segmentColumn] & 128; PORTB = cache[4][segmentColumn] & 64; PORTB = cache[4][segmentColumn] & 32; PORTB = cache[4][segmentColumn] & 16;
    144.         PORTB = cache[4][segmentColumn] &   8; PORTB = cache[4][segmentColumn] &  4; PORTB = cache[4][segmentColumn] &  2; PORTB = cache[4][segmentColumn] &  1;
    146.         // Inc segment height (the lines repeat with the same bits (n) times allowing to fill the whole screen)
    147.         segmentHeight++;
    148.      }
    149.      else
    150.      {
    151.         Delay_us(43);
    152.         asm nop;
    153.         asm nop;
    154.      }
    156.      // Trace off ...
    157.      PORTB = 0;
    159.      // Fine tine delay to arrive at 64uS / 15.625KHz per horizontal sweep
    160.      Delay_us(6);
    162.      // Inc scan line no. (current line in scope)
    163.      totalScanlines++;
    165.      // Must always have equalized time very critical / tight timing (one IF condition per test)
    166.      if (segmentHeight == 8)
    167.      {
    168.         segmentColumn++;
    169.      }
    170.      if (segmentHeight == 8)
    171.      {
    172.         segmentHeight = 0;
    173.      }
    175.      // Frame complete if this IS scanline no.250
    176.      if (totalScanlines == 250)
    177.      {
    178.         // Fine tine delay to arrive at 60Hz vertical refresh
    179.         Delay_us(10); // close enough with this offset
    181.         // Reset vars ready for next frame ...
    182.         totalScanlines = 0;
    183.         segmentHeight = 0;
    184.         segmentColumn = 0;
    186.         // Tell the screen that a new image is coming
    187.         verticalSYNCa();
    188.      }
    189.   }
    190. }
    Last edited: Dec 4, 2011
  2. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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: Dec 3, 2011
  3. SgtWookie


    Jul 17, 2007
    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.
  4. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.
  5. Georacer


    Nov 25, 2009
    Is this a completed project, T.Jackson? Should I transfer it to the Completed Projects section?
  6. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.
  7. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    Knew I forgot something. Yeah if you build this, then put a 47K from base to ground.
  8. Wendy


    Mar 24, 2008
    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.
  9. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.
  10. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.
  11. SgtWookie


    Jul 17, 2007
    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:

    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.

    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.

    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.
  12. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    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 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.
  13. thatoneguy


    Feb 19, 2009
    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.
  14. THE_RB

    AAC Fanatic!

    Feb 11, 2008
  15. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    I know 2% of composite video and nothing about VGA.

    25uS scan line is it? :eek:
  16. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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: Dec 3, 2011
  17. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.
  18. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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: Dec 3, 2011
  19. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    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!
  20. T.Jackson

    Thread Starter New Member

    Nov 22, 2011
    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.