This lab is to to generate a simple Luminance-only NTSC (composite) video signal using an Atmel AVR microcontroller.
NTSC stands for National Television Systems Committee, the FCC group that in 1953 approved a color television standard based on the earlier 1941 standard for black-and-white television. It is used throughout North America and Japan, and is the standard typically used for RCA video cable connections (the little push-in plugs on the back of your VCR or PS2/XBOX).
We will focus on only generating black & white video, since that was the first (and simplest) standard for TV signals. An NTSC TV has 262.5 scanlines (just think of it as 262 for now) that are written from top to bottom, and refresh the screen at 60 frames per second. The lines are interlaced to reach this refresh rate, so you actually see a total of 525 visible lines. It is transmitted simply by a formatted series of voltage levels between 0V and 1V over two conductors (a signal wire and a ground wire). The signal wire is located in the center of an RCA plug, while the ground wire is the shield around it. The signal wire transmits image data in this sequence:
Thus, we get 63.5us for a single scanline, with 242 lines drawn in sequence and another 20 or so non-visible vsync lines at the bottom, as shown below.
Color TV is similar to this, except the black-and white level (or Luminance) is supplemented by an additional subcarrier signal called Chrominance. The Chrominance is actually two signals meshed together with a technique called quadrature amplitude modulation, and synchronized using a "color burst" pulse during the Back Porch. Generating a signal of this sort is well beyond the scope of this course, but it's a remarkably useful scheme since this allows simple B&W TVs to display modern NTSC signals by just ignoring the Chrominance.
In order to create these varying voltage levels using only the 5V TTL ports of the AVR, we need a digital-to-analog (D/A) converter. The simplest kind of D/A converter is just a resistor Y-network such as that shown below:
Using two AVR pins, pin 0 connects to signal through a 900 ohm resistor, and pin 1 connects to signal through a 450 ohm resistor (430 will do as well). Signal then connects to ground through a 75-ohm resistor. In this circuit, four analog voltage levels are possible:
So to send an hsync pulse, set these pins to binary 01, then 00, then 01 again. It's easiest to see what sort of waveform you're getting on a lab oscilloscope.
To build the D/A converter, you can use a breadboard, PCB, or just stick the resistor ends into the 10-pin ribbon cable connectors used with your STK500. The resistors can be soldered or just twisted together as long as good contact is made and there is no movement in the connections. The signal wire (the center of the "Y") should be connected to the center of your RCA plug, and the ground on the STK500 should be connected to the shield around it. This is the complete parts list of what you need (available at Supremetronic on Queen St.):
First of all, to generate video and do some processing in the background the AVR could stand a speed boost. It's best to run the 8515 at it's maximum (rated) speed of 8MHz by adding an 8MHz crystal to the "crystal" port on the STK500 and shifting the "oscsel" jumper one pin toward the led-and-button end of the board.
Even though most TVs are fairly tolerant of small variations in voltage and timing, the scan lines and sync pulses must be sent consistently throughout each frame of video. Keep in mind that not all AVR instructions take the same amount of time, so different execution paths could potentially change the timing of the sync pulses. Also, the AVR is an 8-bit microcontroller, so avoid using 16-bit words as much as possible. Processing an unsigned char takes MUCH less time than an unsigned int. The easiest way to keep everything nicely timed is to use the 16-bit timer/counter 1 on the Atmel AVR. At 8MHz, if you set the timer/counter to a prescaled clock rate of CK/8, you get one timer increment (in TCNT1) every 1us (1/1MHz). Setting the prescaler to CK gives you more flexibility in adjusting the timing though, and is reccommended (giving 1us every 8 counts). If you have trouble getting a clean picture, you can "Tune" the display in single counts by using the buttons (debounced, of course) on the STK500. This allows you to compensate for adding code, which takes up extra cycles of time and can throw off horizontal synchronization. You will probably need to experiment to find exactly how many counts will give a clean picture. It doesn't have to be perfect, but reasonably good-looking and legible. There's only two interrupts available that can be generated by the STK500, but you can always poll the TCNT1 register to wait until it counts up to a certain value. Another possibility is to use the 8-bit timer/counter 0. It can only generate an overflow interrupt, but if it's prescaled to CK at 8MHz, it overflows at 256/8Mhz = 32us. Thus every two overflow interrupts, you have counted 64us. See the Spec Sheet for details.
The LCD display used in the last lab is an excellent way to display debugging information from your program. The printa() code from last lab can be pasted in to serve as a console (as long as it doesn't interfere with the timing of your video signal). If you don't have access to an oscilloscope, the easiest way to debug the timing of your circuit is to slow it down to a crawl and view the output of your D/A pins on the LEDs (setting timer prescale to CK/1024 and multiplying the time intervals by 100 will do this pretty effectively) Also, if you're using an older TV set, you probably will have to twiddle the hsync/vsync knobs to steady the frames.
Most importantly: don't get too fancy and don't try to do too much at once. First, just focus on getting a horizontal-synced scanline pattern to appear. Then, try to vertical-sync it to make it stationary. There are only two programmable-length interrupts on the AVR (timer/counter1), so much of your display will probably just be sequential writes to the A/D circuit. In order to get decent resolution, you'll have to heavily optimize your display code (remove extra instructions) while making sure that your sync pulses arrive consistently. TV's are VERY sensitive to sync pulse timing, so using a timer to directly trigger the sync pulse (and perhaps the other to end it) is recommended. If the edges are fuzzy, try to tweak the hsync pulse timing by single timer units (using the CK prescale for maximum resuluition helps here). On an oscilloscope, "ghost" traces appearing after signals are a sign that the timing is inconsistent. Also, if your image is stable but "twisted around" on the screen, try changing the number of vsync lines displayed. (I actually found the best performance using 2 vsync lines on my TV)
In the lab, you are asked to write a "video paint" program using NTSC video. This is a simple "etch-a-sketch" type of program where you have a cursor on the TV screen controllable by buttons/switches/etc. (those on the AVR are fine). You should be able to move up/down/left/right and paint trails or dots of white or grey on a canvas of at least 10*10 resolution. Also, you are to write a "pong" clone using the same techniques. Pong is pretty well known (google search if you've never seen it before). To make a good game though, you will need much higher resolution, e.g. 80x60 or so. To achieve 80 pixels per line and above, it is necessary to "unroll" code loops in your program to avoid the overhead of loop instructions. (i.e. instead of using "for" or "while", just write a big list of 80 outp() statements) The higher resolution you achieve, the better the chance for bonus marks.
These are very useful pages to look at, to help with this lab, out of interest or as alternate approaches to NTSC video generation.
|Building the resistor network.|
|Making the hsync/vsync work.|
|Getting decent or good image quality.|
|Showing a working video paint.|
|Showing a working pong game.|
|Bonus points for beating a T.A. on your video pong game.|