NTSC video out library

I was looking at that earlier today actually. Interesting stuff, I don't know if ill implement osd at all. I do find the idea of it interesting and that's what got me started on this library to begin with.

I have a few other things I want to add. Right now I'm working on making the resolution selectable during run time. I also want to speed up the ISR so I can poll the serial connection, generate simple sounds ect.

Being able to use straight asm files would be very helpful with this....

Nice work mdmetzle. :wink:

After testing, I was trying to place a small binary image on the TV Screen.
For this test I made two small sketches. In this test I used an small image of 10 x 10 px, as we see in figure 1.

Figure 1 - Test image 10 x 10 px

The image was created on Gimp, and saving it as grayscale image, format pgm (in ASCII mode).

Using Processing, the test image is loaded to create our vector to use in the Arduino sketch.

String [] img; // Image is m x n px
PrintWriter salida;

void setup(){

  int i;
  int Mini = 4 ;  // Begin from posc 4 of pnm or pgm file
  int Maxi = 100; // Image is 10 x 10 px 

  salida = createWriter("imagen.txt"); 
  img = loadStrings("mA2.pgm");  //Format pnm or pgm

for (i = Mini; i <= Maxi; i++) {
  
  if ( int(img[i]) == 255) { 
         
     if (i == Maxi){
     salida.print( "1");
     }else {
     salida.print( "1" + ",");
     }     
 
  }else{
    
     if (i == Maxi){
     salida.print( "0");
     }else {
     salida.print( "0" + ",");
     }     
     
  }
  
}

salida.flush();
salida.close();
exit();
  
}


void draw(){
  
}

Code 1 - Image reading, writing vector in Processing

Finally its created an small code to read the vector and load the image to the TV Screen. In figure 2 its shown the resulting output. In code 2 is one mode to read the vector.

Figure 2 - Result image on TV Screen.

#include <TVout.h>

TVout TV;
unsigned char x,y;

int img [] = {1,0,0,1,1,1,1,1,1,0,1,0,1,1,0,0,1,1,1,1,0,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1};


  int i,p;
  int Maxi = 10; //Image size is 10 x 10, each 10 pixels is a new row
  int Maxt = 100; // Total size of the image

  int LEDR = 7;
  int LEDV = 6;

  
void setup() {

  Maxt = Maxt - 3;
  
  x = 0;
  y = 0;
  
  TV.start_render(_NTSC);

  pinMode (LEDR, OUTPUT);
  pinMode (LEDV, OUTPUT);

}

void loop() {

  digitalWrite (LEDR,0); //for debugging
  delay(500);
  digitalWrite (LEDR,1);  //for debugging
  delay(500);
  digitalWrite (LEDR,0);  //for debugging
  delay(500);

  
  p = Maxi;  
  x = 0;
  y = 0;
    
  TV.clear_screen();
  TV.print_str(0,0,"Testing ...");
  TV.delay_frame(60);

  TV.clear_screen();
  
//Reading an image
   
  for (i = 1; i < Maxt; i++){
      
      if (i < p){

       if (img[i] == 1){
       
        TV.set_pixel(x,y,0);
        // digitalWrite (LEDV,0); //for debugging
        // delay(10);
       
        }else{
       
          TV.set_pixel(x,y,1); 
          //digitalWrite (LEDV,1); //for debugging
          //delay(10);

        }
       
        x++;
                               
     }else {
       
       y++;
       x = 0;
       p = p + Maxi;
  
     }
      
   }

  TV.delay_frame(60);

}

Code 2 - Arduino code to read an image.

Figure 3 - Arduino Board for testing.

Now testing how to load a bit bigger images. 8)

nice work spacebird. I started to implement a method to put bitmaps onto the screen but my initial method did not work, and i decided to use my time working on fixing bugs instead. I do know what i was doing wrong now though. I was attempting to copy predefined bitmaps out of the flash space and wasn't reading the data back correctly.

Here is an idea assuming you are generating bitmaps that are ordered correctly you can directly copy whole bytes onto the screen; note that the most significant bit is the left most pixel.

I am interested in the processing program you use to generate the bitmaps though, it would be very useful. (Edit: oh hey you included that...)

I was using this for the bitmap I created:

Let me see that link...

ok... I see that is a 1 bit image or binary image. For grayscale is needed a 8 bit per pixel, this could be binarize or a pre-process call binarization.

--testing if is possible to use 8 bit images with your library .

The position of the images depends of : if you are using vector form or matrix form. At the moment due to capacity issues on Arduino Duemilanove it's hard to upload a bigger image. So now I'm planning to buy an external memory like: http://www.arduino.cc/en/Tutorial/SPIEEPROM


I forgot to upload this code to visualize the image in processing.

int [] img = {1,0,0,1,1,1,1,1,1,0,1,0,1,1,0,0,1,1,1,1,0,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1,0,0,0,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1};

void setup(){
  
  int j,k,i;
  int Maxi = 10; //Image size is m x n, eac m pixels is a new row, use m
  int Maxt = 100; // Total size of te image -3
 
  Maxt = Maxt - 3;
  int p = Maxi;  
  j = 0;
  k = 0;
    
  for (i = 1; i < Maxt; i++){
      
      if (i < p){

        j++;
               
        if (img[i] == 1){
          print (" ");   
        }else{
          print ("*");
        }
                
     }else {
       
       k++;
       j = 0;
       println();
       p = p + Maxi;
  
     }
      
   }
 
 exit();
 
}

void draw(){
  
}

That program supports 1 bit grayscale and that is what I was using. It packs the bits to use all 8 bits of a byte soo it makes the code a little more complex but uses far less memory.

To use large static images store them in flash like how the font set for the library is stored and used. See the print char function to see how to read it and put that onto the screen

The data is stored as a 1d array(vector) but a matrix is still a 1d array with fancy addressing. The screen is output byte by byte so every x number of bytes a new line occurs.

I'm loving being able to output to a tv so easily. I hope you don't mind that I've been adding to what you've given us so far with a few simple graphics functions, draw boxes, draw circles and draw progressbars.

The boxes can be filled/unfilled/inverted as can the circles and the progressbars can be white on black, black on white and sectioned bars.

Here's what I've got going so far, the eventual goal is to make on screen displays easy to set up with boxes and text etc easily:

Not terribly complex but it's a way for me to learn more C.

Nice work crook of course I don't mind if people add stuff to the library.

I working on the "back end" right now. Ive started writing separate rendering functions to do different resolutions on the, well at least make changing resolution more "arduino like". When I'm done 256pixel lines should be possible given enough memory.

I had planned to add more basic graphic functions at some point but i honestly find the rendering stuff more interesting. If you want I can add in your additions :wink:

If you think they work well you can add them in. I'm more interested in the rendering simple shapes than anything else. I've been working on rounded rectangles today, got it going apart from the filling but that's simple enough.

I don't think there's much else to do:

rectangles straight/rounded (might add in 3d boxes)
circles (might add for ellipses)
progressbars

Might do radio buttons, checkboxes, polygons perhaps. I don't think much more is needed for a simple display like this

Here's an example of box drawing at the moment, probably to have a 3d switch at the end of it too by the time I'm finished.

//draw_box(x start, y start,x length, y height, colour , fill, rounded radius);
TV.draw_box(20,20,40,30,1,3,0);

After I've got these up and running I'll have to clean up some of the code for efficiency and cleanliness, that's the part I'm keen to get done, some of it now is quite short and simple, others needs a little work. That and looking out for out of bounds errors fully to protect the user. But apart from that it's nearly there.

All of this is to have some fun with algorithms really, (Bresenham's) midpoint circle algorithm is amazing in how it works so well.

Thanks very much for this library and keep up the good work!
Changes I made for Sanguino (video=PD4/Pin12, sync=PD5/Pin13):

diff -U 3 -H -d -r -N -- TVout/TVout.cpp TVout_sanguino/TVout.cpp
--- TVout/TVout.cpp      2010-05-09 13:57:48.000000000 +0200
+++ TVout_sanguino/TVout.cpp      2010-05-17 10:55:07.597805348 +0200
@@ -77,9 +77,9 @@
       PORTB |= 0x20;

 #else

       //setup the ports

-      DDRB |= 0x03;

-      PORTB &= ~0x02;

-      PORTB |= 0x02;

+      DDRD |= 0x30;

+      PORTD &= ~0x30;

+      PORTD |= 0x10;

 #endif

       

       // inverted fast pwm mode on timer 1

diff -U 3 -H -d -r -N -- TVout/video_gen.cpp TVout_sanguino/video_gen.cpp
--- TVout/video_gen.cpp      2010-05-09 13:49:14.000000000 +0200
+++ TVout_sanguino/video_gen.cpp      2010-05-16 22:14:51.978742000 +0200
@@ -106,7 +106,7 @@
             "ADC      r27,r29\n\t"

             //save PORTB

             "IN            r16,%[port]\n\t"

-            "ANDI      r16,0xFD\n\t"

+            "ANDI      r16,0xDF\n\t"

       

             //output thingy

             "LD            __tmp_reg__,X+\n\t"            //1

@@ -194,7 +194,7 @@
             "output %[port]\n\t"

       // assembly variable IO:

       :

-      : [port] "i" (_SFR_IO_ADDR(PORTB)),

+      : [port] "i" (_SFR_IO_ADDR(PORTD)),

         "x" (g_screen),

         "y" (renderLine)

       : "r16" // try to remove this clobber later...

@@ -260,7 +260,7 @@
 #else

       // output for ATMega8,ATMega168,ATMega328

       ".macro output port\n\t"

-            "BLD      r16,0\n\t"                        //output pin of the port here pinB0

+            "BLD      r16,4\n\t"                        //output pin of the port here pinB0

             "OUT      \\port,r16\n"                  //and on the last line of asm

       ".endm\n"

 #endif

diff -U 3 -H -d -r -N -- TVout/video_properties.h TVout_sanguino/video_properties.h
--- TVout/video_properties.h      2010-05-09 14:05:06.000000000 +0200
+++ TVout_sanguino/video_properties.h      2010-05-17 10:46:00.625806000 +0200
@@ -60,7 +60,7 @@
 //Timing settings for NTSC

 #define _NTSC_TIME_SCANLINE                  63.625

 #define _NTSC_TIME_HORZ_SYNC            4.7

-#define _NTSC_TIME_OUTPUT_START            11

+#define _NTSC_TIME_OUTPUT_START            12

 #define _NTSC_TIME_VIRT_SYNC            (_NTSC_TIME_SCANLINE - _NTSC_TIME_HORZ_SYNC)

 

 #define _NTSC_LINE_FRAME                  262

@@ -82,7 +82,7 @@
 //Timing settings for PAL

 #define _PAL_TIME_SCANLINE                  64

 #define _PAL_TIME_HORZ_SYNC                  4.7

-#define _PAL_TIME_OUTPUT_START            11.75

+#define _PAL_TIME_OUTPUT_START            12.75 //11.75

 #define _PAL_TIME_VIRT_SYNC                  (_PAL_TIME_SCANLINE - _PAL_TIME_HORZ_SYNC)

 

 #define _PAL_LINE_FRAME                        308

@@ -100,4 +100,4 @@
 #define _PAL_CYCLES_OUTPUT_START      ((_PAL_TIME_OUTPUT_START * _CYCLES_PER_US) - 1)

 #define _PAL_CYCLES_VIRT_SYNC            ((_PAL_TIME_VIRT_SYNC * _CYCLES_PER_US) - 1)

 

-#endif
\ No newline at end of file
+#endif

This circuit might make an interesting base for a menu-driven multi-game hack of an arcade game PCB, using the Arduino to switch between different game EPROMs

Just for fun I've been wondering if this library and the arduino is fast enough to recreate Pong...

Just for fun I've been wondering if this library and the arduino is fast enough to recreate Pong...

I don't think you will have a problem with doing so using this library from the examples I have seen. Pong, Tetris, Breakout - plus a ton of other games could be easily created.

You have seen the specs for an Atari 2600, haven't you?

I think it's fast enough as well, basic with it being black and white but fun enough. Just a couple of pots for the pong controls, it's about the simplest game that can be made. I haven't seen any specs for old machines but those were marvels of the time with people building them from the ground up on just transistor logic and the likes.

I think Asteroids might be a step too far but maybe worth a shot at some point if I find the time (which I doubt!)

I haven't seen any specs for old machines but those were marvels of the time with people building them from the ground up on just transistor logic and the likes.

The Atari 2600 was an IC-based machine; it had some tiny cpu in it, with a very tiny amount of ram (something like 256 bytes) - and no video chip or anything, it directly controlled the TV much like this library does. The majority of game code and such were contained on the cartridges as a ROM.

Arcade machines of the day were also IC-based; more so they were essentially computers built to run the game (incompatible with each other mainly - but they each used similar 8 and 16 bit CPUs, with RAM, ROMs, video and audio circuitry, etc) - later machines started to standardize on boards and such to become more general purpose.

I don't know of any discreet component machines other than some of the analog (not digital) cheap pong boxes that existed out there (you can still find circuits for these online if you care to look).

I think Asteroids might be a step too far but maybe worth a shot at some point if I find the time (which I doubt!)

Consider Star Raiders on the Atari 2600; that was a game that really pushed that machine, and it is now considered a classic (and sought after, from what I hear - but they'll never get my copy until I am long dead!). Then there was a version of Battlezone...

I think Asteroids is well within the possibility of doing using this library.

I also think somebody needs to work on adding color capabilities...

:slight_smile:

[edit]After a token amount of research, I now see that the 2600 did have something akin to a video and sound chip; but on 128 bytes of RAM - I still think this library, though, could be used to create some advanced games...[/edit]

Honestly i feel that the limiting factor as far as games go will be the amount of memory left over. This library is a memory hog as it is.

There should be about 3.5mips( about 58333 instructions per frame). leftover running at a vertical resolution of 96. More or less time can be used based on the vertical resolution, However this is automatically scaled.

I've tried OSD modifying this library, but i've got lots of jitter in the best case. I've deleted all OCR1A in order to avoid the microcontroller making vertical and sync pulses, and substituting them for while loops controlled by the two interrups, one triggered by composite sync output on rising (from LM1881), one triggered by vertical sync output on rising (from LM1881 too).

I think the problem is where to place that while loops. May it be posible to 'insert' the rendering code inside the horizontal interrupt ISR?

I've also thought about the even/odd fields... If the input video is interlaced, jitter will be present... How can I manage that?

Can someone lead me to a solution?

Thanks a lot!

yes you can insert the render into the ISR, just delay until the output start, and make sure it is done before the next line. The library does everything from the interrupt as it is.

As far as jitter goes assuming its just horizontal jitter, you need to start outputting at exactly the same time everytime. There are two ways to accomplish this: 1) keep the processor asleep at all times it is not in the ISR. 2) synchronize the output to the timer and have it reset itself using hardware. This is what the library uses, the change needed for OSD would be resetting on an input compare.

your horizontal isr should flow something like:
count++
if output line
delay
output
else
do some work

and the loop function should just put the cpu to sleep. This is the easiest method but not necessarily the most useful.

here is an example of sleeping the cpu to generate video:
http://courses.cit.cornell.edu/ee476/video/

I hope this helps at least a little....

Updates!!
Not much functionality wise.
Added sanguino support, thanks debian (its untested at this point).
Added runtime selection of resolution:
start_render(mode,hres,vres)
the maximum resolution currently is 152pixels wide(must be a multiple of 8). lowest is 104.

I'm half done working on method that will increase the horizontal resolution up too 256 pixels, however this would need to output on the top bit of a port. It will be destructive so no other pins on that port will be usable. This will remove the ability to use the USART on th non mega/sanguino boards.

This might be a little off topic, but does anyone have a source for cheap (<$50) NTSC monitors, either LCD or tube? They don't need to have an enclosure. I'm thinking about making some Arduino terminals using this software and a PS/2 keyboard.