Interfacing with an NTSC TV

Hey guys, this is a follow up on this topic. It started in French, but I figured since NTSC is used mainly in America and in Japan, English might be better suited for the topic. The basic setup is the same as the Arduino pong and the other topic. So this is just my "port" of what these guys did. I did build mine with a "structure" to allow adding pal support to be able to switch modes easily in the code.

Disabling the interrupt timer

I haven't gotten my code to work with cli() and serial so you'll have to modify lib/target/arduino/wiring.c (MAKE A BACKUP) like such:

// enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
    sbi(TIMSK0, TOIE0);
#else
    sbi(TIMSK, TOIE0);
#endif

To:

/*
    // enable timer 0 overflow interrupt
#if defined(__AVR_ATmega168__)
    sbi(TIMSK0, TOIE0);
#else
    sbi(TIMSK, TOIE0);
#endif
*/

The circuit

That is... Sync to port 8 on the arduino and video to port 9. I use 900 and 300ohm resistors but it should change much anyway; these can be a little off.

Grab the latest version of the code.

Once you've uploaded the code, connect via serial (I use screen in the terminal) and wait for the uC to finish loading (you'll see "Mode:"). If everything went well there should be a dot in the middle of the TV. Now hit enter for usage. If you read through the code you will see there is a way of enabling "signal voltage mode" which allows you to debug your circuit.

There are a few things that are acting weird... I could really use some feedback.

  • If you put the resolution higher the uC seems to crash (not enough ram?).
  • Sometimes the uC acts as if the mode (output) had not been set properly. Re-uploading seemed to have fixed the problem when I've seen it.
  • There may be a timing issue while writing the first few lines. (EDIT: The issue has been fixed, see posts bellow)
  • Going into signal mode sometimes seems to crash the uC (same behavior as the first issue)

I've tested these this with both a mega8 and mega168 (thanks to the USBTiny that appeared in my mailbox this morning and a little magic from its fairy) on the NG and under 0007 and 0009. Optimally this code would need to be ported to a version with interrupt support.

P.S. If that pastie ever goes down, here is a plain text mirror.

And the obligatory picture ^-^


(The flickering at the top seems to have been fixed – at least as best as I could) FIXED

The cli() command seems to work for me, though I have a brand new diecimilla i think with the 0009 (?) firmware. I haven't seen any of the odd failures you mention.

From my experience the top lines are curved because of the length of your vsync too short and it curves one way and too long it curves the other way- I made a little video of the effect- Arduino Video - NTSC on Vimeo

Thanks for the video! that was great. Fixed the issue for real this time.

line 61 is now:
#define _ntscDelayBackPorch 5.9
and line 64:
#define _ntscDelayVSync 50

Now why can't I rev up the resolution?... basically anything over 21x16 seems to crash (I don't use the sprite with it, obviously!)

From a couple of experiments I don't think I can display much beyond 900 pixels- I'm pretty sure this is because I'm using a byte of memory per pixel and there's only a kilobyte of ram- maybe you have less?

But for the two shades of gray each byte of memory could be 4 different pixels, so if memory is the problem resolution should be able to go 4 times as high like this:
PORTB = (fb[x][y]) & 0x3;
PORTB = (fb[x][y] >> 2) & 0x03;
PORTB = (fb[x][y] >> 4) & 0x03;
PORTB = (fb[x][y] >> 6) & 0x03;

A for loop to draw the screen didn't work for me but I didn't play with timing with it much.

delayMicroseconds of less than one microsecond also didn't work but I also didn't experiment much.
Assuming you can get more horizontal pixels (or you could repeat the pattern horizontally) I think the best thing to do is just get rid of the delays, and the delay of setting PORTB is the limiting time for horizontal resolution- though I'm seeing some strange things doing that while trying to repeat horizontally- the second set of pixels comes out thinner.

I have a Mega168, so yeah 1k worth of RAM. that should be plenty. All I use except the frame buffer are a few int counters for looping (all in functions so they are all released fast). To my calculations, 21x16 would be 336 bytes, which is way under. Now boosting the resolution to just 24x18 (432 bytes... still plenty of room left) and the uC crashes. AS for your idea of packing more pixels per byte, yeah that's a great idea... definitely something I'll look into today.

I might have found the bug that makes the uC crash. The loadSprite function. The weird part is it's not even being called. But if I comment it out, the uC runs fine and if I don't it never boots. I've replicated this over many upload cycles. The weird part though is that it still crashes when I compile/upload from the Makefile (0007 and the recently posted 0009)

Hi, first let me compliment you on the code. It's very clear and well written. I had been tracking down similar video examples and this is way better written than anything else I've seen.

Now, I can't get it to work and I'm wondering if you can help. I am trying it with a tv that shows a blank blue screen if there's no other video and when I fire this up I just get a very faint pattern of darker horizontal bars on the same blue. The TV superimposes a "video 1" banner in the top right for a few seconds and this seems to be getting pulled up off the screen (maybe 5-10 scanlines) and it wobbles.

I've tried the debugSignal(nice feature by the way) and the levels look fine. Any suggestions?

This is work you did a couple of years ago - Is it possible that arduino timing has changed?

Bill,
I got a variation on the pong code to work out of the box last year so I dont think the timing has changed.
FYI - my code is posted here - it doubles the number of pixels in the original pong code. Also it uses a hardware timer for vertical refresh which lets you run other code during retrace (and doesnt require disabling interrupts)
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231475982/0

I am not positive what the code you are starting with, but Phizone posted a couple of stripped down versions of the pong code here:
http://dailyduino.com/archives/368
This was the easiest example I found, and one of those examples should run out of the box.

All the code I have looked at had some delay code (NOPS or duplicated OUT commands) at the end of the frame write. You may need to add or remove the delay to get the sync to work.

Secondly, your TV may be picky about the video level, try an old portable TV, my Sony will go blue if the signal is too low, but the junky Phillips I use for Arduino doesnt care. Some people say that TV cards for your PC are more tolerant of wierd timing also.

Finally, double check your resistor circuit, the TV may be seeing enough signal to try to sync, but not enough to go white.

Hello!
I am impressed. So what about having the Arduino take input from external hardware, such as digital logic, and then translate that input to the TV screen?

Gregg (doctorwho8)
Time moves in spirals because it can't move straight across.[/left]

Take a look at the new TellyMate shield also, his site has a ton of good info on talking to NTSC and PAL TVs

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1240539968

DrWho, both the medium res version I posted to the forum and the Pong mod I posted to the dailyduino can VERY easily be modified to take inputs etc. Just put whatever you want in the loop routine (the loop routine is just an example). Because the vertical refresh is interrupt driven you dont have to worry about timing in any code you add. The only limitation is that loop doesnt get very many CPU cycles and the bitmap uses a bunch of RAM.

FYI I havent gotten around to coding the "High Res" version, but I have it worked out how to double the pixels again. It shouldnt take any more RAM than the existing code. If anyone wants to try it I will outline the changes that need to be made.

I'm the chap behind the Batsocks TellyMate Shield.

@bill2009 - you might find that the circuit shown at the top of this thread isn't quite giving you the voltage range you expect because the TV has an (equivalent of) a 75ohm resistor within it. This forms a voltage divider with the other 75ohm resistor (shown in the circuit at the top of the thread) giving a 0.5v p2p signal, rather than the required 1v p2p.

I've found much better results without the 75ohm resistor in the circuit. Whites are white, rather than grey.

Having said that, on the more recent design of the TellyMate Shield, I've added a jumper (J7) to optionally connect this 75ohm resistor back in, because I found a small portable telly that wouldn't work otherwise.

(See this thread over at AVRFreaks)

@doctorwho8 - There shouldn't be any real problems in reading input to display, but you'll have to bear in mind that you won't be able to use interrupt driven methods because the interrupt routines will interfere with the timing of the sync and display signals - e.g. you don't want a 'USART_RXC' (serial received) interrupt called part-way through outputting your pattern!

@drspectro - I'm interested in the 'High Res' version!

Rough outline of hi-res

The original Pong video used the low 2 bits of a 38 wide by 14 high array.

medium res demo used the low 4 bits (2 at a time) of a 37 by 16 arrray. Uses two copies of the 2 resistor D2A converter but only ONE D2A converter is really active at any time. By bit masking the for loop index, we step through the array twice. The first time we enable the low two bits (0-1) of PORTC using the DDRC command. The second time we enable the bits (2-3 ) using DDRC. The DDRC command is a single and runs after each scan line so you dont get jitter halfway down the screen.

High Res (proposal)
Eliminate the grey tone. Black and white only. Instead of two copies of a 2 bit D2A, we use 4 (or8) one bit D2A converters (330 ohm resistor). If you use all 8 this ties up all 8 bits of PORTB. We use one bit from another port to drive a 1K resistor to get "black level" when all 9 bits go low that is blacker than black.

Finally we make the frame buffer roughly 60 bytes wide by 8 bytes high and loop through it 8 times (4 for square pixels) enable a different bit each time with DDRC. The loop logic is REALLY funky, but you can see it work in the medium res demo.

The big problems are

  1. If you use all 8 bits of PORTB you loose hardware serial (could reduce vertical res and use a 6 bit port - needs thought here)
  2. the DDRC mask logic has to take a constant amount of time. Currently its an if/else but 4 or 8 options is hard to make constant time.
  3. The logic for looping through the array multiple times is a bit confusing (well REALLY confusing).
  4. it might be better to use only the low 4 bits and keep the high 4 for a working copy of the bitmap (thats how medium res works). 4 line pixels vs 8 line pixels depending on how you count it.
  5. Sync line ties up another digital out line.
  6. All the Pong type video programs depend let horizontal sync drift during the vertical sync period, it works but causes that top of the screen jitter. Looks like you keep sync tighter in Batsocks????

Hello!
One of the things I forgot to mention below is that I am running on a
Arduino Duemilanove http://arduino.cc/en/Main/ArduinoBoardDuemilanove

So I suspect that something will need to be changed to accomodate this board as opposed to the ones you are using for this great idea. As for what that is, I am not sure. I am getting great smears of black and white color patterns on this set.

/me

Time only stands still for an appointment.

Drwho8 - I am running on a Duemilanove also.

I agree with Condemmed, try removing (or increasing) the 75Ohm resistor. There are a couple of versions of the 3 resistor D2A converter around, you might try a different one.

I guess the fact that you are getting some grey and colors indicates you are in the ballpark for voltage levels. Unlike XSmurf who isnt really getting anything. Yours sounds more like timing problems.

If you load the stripped down Pong example from the DailyDuino link I gave earlier I can tell you where you can tweak the timing. I couldnt tell which code you had downloaded earlier. Basicly there are a couple of dummy instructions at the end of line , the "PORTB=PORTB" statement. Add or remove these ONE AT A TIME.

The vertical refresh is set by the "FrequencyTimer2Init", but I think it is as close to the correct value as the hardware will support. You can change the vertical refresh by changing the timer2 value. NOTE - the timer2 code rounds your selected time to a value the timer can actually do. You can ask for a more accureate 58.x hz time, but the timer probably will round it for you...

Condemmed -
I think it would be possible to make the vertical more accurate by halting the timer for a couple of NOP instructions then restarting it each frame. (possible, not easy..)

Hello!
About that code from the blog. It seems to be incomplete in places. Which code blob should I be using? Do you have a pastie for it someplace?

And for the code that I know works, I am getting either a squashed spiral of lines at first, or a big spiral of lines, instead of the pattern that you're displaying on your demonstration screens.

/me

Both pieces of code on the DailyDuino blog are complete. However they were created by stripping down the original Pong program. So all they do is a demo screen. There are a few comments and variables left from pong but they both work. I used the original code "as is" and the "improved version" I posted there is complete. I do recomend the second example I posted.

Actually it looks like Phizones original code has an emoticon embeded in it and my improved code the "include" statement didnt display right. There is a note from me right below the code with the correct include statement. Other than that they are "complete"

Note that the "improved" code requires the FrequencyTimer2 library from the libraries page on this site. Maybe thats what you are missing? The first example on the DailyDuino page uses software delays. However, the first example doesnt need the FrequencyTimer2 library.

The "medium res" code post is sort of messy in three chunks, but Phizone did download and test it from here. I will see if I can post it somewhere as a complete chunk.

Regarding port numbers this is confusing and my example didnt make it any easier. The picture in thread you started with showed the D2A convertor on PORTD. The "grab code here" code used PORTB. Phizones original Pong mod used PORTB pins 8-9.

However for "Medium Res" I switched to PORTC pin 14-17. I thought I had a problem with PORTB (I didnt) but by the time I got it working I was too lazy to switch it back (big sorry for that). PORTB should work just fine, but would take a bit of editing, both port name and pin numbers.

Finally the comments for "Medium Res" have a diagram of the D2A circuit it needs. For any of the others, make sure you are using the diagram that goes with the code.

Once you are sure you have the right D2A (and pins) for the code you are running, try tweaking the delay statements I mentioned earlier. Be sure and let me know which code you are working with too....

doctorwho8
If you were wondering about "digital 14-17" the analog pins can also be used as digital pins see this thread (and its in the docs somewhere).

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1243531186/3#3

"Then you use the following from the Arduion reference section:

"The analog input pins can be used as digital pins w/ numbers 14 (analog input 0) to 19 (analog input 5). "
"

I had a hard time getting this code to work. It wasn't sync'ing, and I had to drop the delays down really far to get it to be stable. Something like:
ntscDelayHSyncStart 3 us
ntscDelayBackPorch 1 us
ntscDelayFrontPorch 1 us
ntscDelayPerLine 2*21 us
ntscDelayVSync 25 us

Is it common to have this problem (not sync'ing -- screen is a rapidly moving mess of short horizontal lines) with the original values? Does this mean my board is slower than standard? Or that something else is slowing it?

Is it possible that arduino timing has changed?

Perhaps delayMicroseconds was significantly changed over the past couple years? I just compared 0007 and 0017... The only difference I see is the addition of a local variable and saving/restoring the interrupts SREG. Just a few clock cycles I would think.

My specs:

  • Duemilanove with ATmega328
  • arduino version 0017

Also... i modified the code to allow interactive changing of the delay values, and then to allow 4x the precision in delay values. I could post this messy code if anyone else has trouble with the "standard" delay values; let me know.

Thanks for the code and for any advice about why I'd need much smaller delays!