This final post in the NTSC Demystified series demonstrates color NTSC signal generation using a microcontroller. We're going to get Atmega16, an 8-bit microcontroller, to generate color NTSC video signals without using an NTSC color encoder chip like AD725!
CRT TVs display pictures by drawing horizontal lines as shown here. Each horizontal line is drawn from left to right and each frame is completed by drawing horizontal lines from the top to the bottom. To transmit a picture to the TV on a wire, it is necessary to inform the TV when each line starts and when a new frame/field starts. This is done by sync pulses. I've written more about how the NTSC sync works is here and NTSC color encoding is described here in more detail. The standard NTSC sync signal is unnecessarily complicated for a modern TV. Examining a "modern" (post 1980s) sync detector helps in coming up with a simplified sync signal. The datasheet for LM1881, a sync separator chip, has a functional circuit diagram along with a helpful description of how V-Sync is detected and how even fields are distinguished from odd fields. These are my observations:
1. 70 mV is the threshold for sync. If the video signal is 70 mV more than its minimum value, the region is marked non-sync; otherwise, it's marked as a sync pulse. Although the NTSC standard states that sync should be 0.3 V below the black level, the precise value of the sync pulse amplitude is not important (0.2 V to 0.4 V is OK).
2. Short sync pulses are horizontal syncs. If the sync pulse width exceeds some threshold, which I'm guessing is about 1/4 of the width of a full scanline, it is marked as a v-sync pulse. The precise duration of the sync pulse is unimportant. Short sync pulses should be about 4 uS, and long sync pulses should be about 30 uS. In NTSC jargon, the first serration pulse triggers vertical sync. The pre-equalization, post-equalization, and the other serration pulses are ignored by modern sync detectors.
3. Even/Odd fields are identified by when the long sync (v-sync) pulse occurs. If it occurs at the beginning of a scanline, the field is marked as an odd field; if it occurs in the second half of a scanline, the field is declared an even field.
The NTSC standard requires interlace scanning. But, progressive scanning is easier to implement and gives twice the frame rate. Based on the above insights, the following is a simplified NTSC sync signal for "fake-progressive" scan. In this mode, only the odd field is drawn in every frame, and the even field is left blank (every other scanline on the TV is left blank). This kind of progressive scan is done by nearly all the old 8-bit game consoles (NES, SNES, Sega master etc.).
The schematic below includes an Atmega16 microcontroller and a 7495 shift register. We'll see in a moment how the shift register aids color generation. The crystal frequency is 14.31818 MHz, and this was chosen specifically to be 4 times the NTSC color carrier frequency (3.579545 MHz). The Timer/PWM peripheral provides the sync signal on pin PD5 (OC1A) and saves us from having to cycle count assembly code for getting a consistent sync signal. All eight bits of PORTA are used to generate "pixels". The upper four bits select the color (chroma), and the lower four bits set the intensity (luma).
To generate color in NTSC, different phases of the 3.579545 MHz carrier are needed. One way of accomplishing this is by running the processor at a frequency that is a large multiple of 3.579545 MHz and generating different phases of the color carrier using the GPIO or the SPI peripheral. This is how the rbox generates color. Unfortunately, the maximum frequency at which Atmega16 can run is 20 MHz, which restricts the number of colors that can be generated this way. This is where an external shift register can help. Initially, '1100' is loaded to the register. The figure below depicts how the shift register, which is configured as a ring counter, generates four phases (0, 90, 180, and 270 degrees) of the color carrier from a 14.31818 MHz clock. By adding different phases, we can get intermediate phases (ex: adding 0 and 90 degrees gives 45 degrees because sin(x) + cos(x) = 0.707 * sin(x+45)), and each phase produces a distinct hue. The four 560 Ohm resistors in the schematic need not be identical. Using unequal values will give different colors. Also, additional resistors, can be added with extra chroma bits to obtain more colors. Another non-standard NTSC signalling we can get away with is to keep the sub-carrier always turned "ON" rather than only during the color burst (yes, this includes adding the sub-carrier during the sync pulse too). This works because the sub-carrier is filtered out any way.
The upper bits (7:4) of PORTA are kept at '0', and the lower bits are kept at '1' always. Voltages to represent pixels are generated by changing the "direction" of PORTA, i.e., by switching PORTA between hi-z and output mode. The 1N4148 diodes serve to covert the output of the shift register from '0' / '1' to hi-z / '1'. When an Atmega16 chroma pin (one of PA7-PA4) connected to the shift register is hi-z, the corresponding phase gets added to the output; if the Atmega16 outputs '0' on a chroma pin, the corresponding phase is absent in the output. So, different combinations of the 4 upper bits of PORTA result in different phases being added, thereby producing different colors. The following picture shows the color palette generated by this circuit on a CRT.
Note that each line has to have a duration of precisely 227.5 sub-carrier cycles to reduce luma-chroma interference spatially. The reason for this is detailed here. If the line duration is rounded to 228 cycles per line, the interference becomes visible, as shown below, in the form of bands around color transitions.
Another crucial aspect is the number of lines per frame. With 262 lines per frame and 227.5 sub-carrier cycles per line, the alternating luma-chroma interference pattern will be static, which is not pleasant. But, with 263 lines per frame, the interference pattern alternates both spatially and temporally, thereby considerably reducing the visibility of luma-chroma interference artifacts. This is explained in more detail here. Chris Covell has documented this with slow motion in old game consoles, which I am reproducing below (the alternating interference pattern becomes less visible at 60 FPS, and we perceive only the average color). The NES console, even though it outputs 262 lines per frame, achieves this temporal artifact cancelling by increasing the length of one of the scanlines every other frame.
Although this looks fine on a CRT, the digital filter (a comb filter I expect) in modern flat screen TVs, which expects an interlaced NTSC signal, interacts poorly with "fake-progressive" scanning and produces alternating black and white dots around color transitions.
The only solution that occurred to me was to use interlace scanning (please leave a comment if you think of a way to get the filter in flat screen TVs to work properly with "fake-progressive" scans. And no, my TV doesn't have an option to disable the filter). The following is a simplified sync signal for interlace scanning.
With interlace scanning, the digital flat screen TV is happy and produces, as shown below, a decent image. But, interference is slightly more visible in the old CRT TV because the frame rate drops from 60 FPS to 30 FPS (it doesn't have a comb filter).
The firmware and the schematic for the Atmega16 NTSC color palette generator are available under an open-source license here. Other projects, such as the Uzebox, demonstrate how you can use this to make a full-fledged game console.
Finally, I would like to acknowledge Thejasvi, with whom I worked on NTSC during our undergrad days. The following is my collection of links about NTSC. Have fun building retro game consoles!
[1] NTSC demystified - B&W Video and Sync - Part 1
[2] NTSC demystified - Color Encoding - Part 2
[3] NTSC demystified - Nuances and Numbers - Part 3
[4] NTSC demystified - Math - Part 4
[5] NTSC demystified - Implementation - Part 5
[6] NTSC demystified - Cheats - Part 6
[7] PIC PONG by Rickard Gunee
[8] Text on TV by Batsocks
[9] Video Basics - Maxim
[10] PAL TV Timings and Voltages - Retroleum
[11] AVR Video Generator with Mega163 - by Bruce Land
[12] PAL and NTSC timing information - ePanorama
[13] NTSC V-Sync Confusion - AVRFreaks Forum post
[14] Scanning, Timing, Sync Recovery, Numbers
[15] "TV Paint", Embedded Systems Design Laboratory EE281, Handout 7 - Stanford University
[16] Video Generation with Mega644, EE476 - Cornell university
[17] Video Primer - Uzebox
[18] SX PONG - Rickard Gunee
[19] AVR PAL Generation
[20] AVR PAL Color Bar
[21] Lazarus-64 Project
[22] AVRFreaks Forum Post by AtomicZombie
[23] PAL Encoding using FPGA
[24] NTSC Color Decoding
[25] Dot Crawl - Wikipedia
[26] AD725 Datasheet
[27] XGS AVR
[28] RBox
[29] Apple II graphics
[30] "I want my RGB" by Chris Covell
[31] LM1881 datasheet
[32] "RGB vs. Composite" by Chris Covell
CRT TVs display pictures by drawing horizontal lines as shown here. Each horizontal line is drawn from left to right and each frame is completed by drawing horizontal lines from the top to the bottom. To transmit a picture to the TV on a wire, it is necessary to inform the TV when each line starts and when a new frame/field starts. This is done by sync pulses. I've written more about how the NTSC sync works is here and NTSC color encoding is described here in more detail. The standard NTSC sync signal is unnecessarily complicated for a modern TV. Examining a "modern" (post 1980s) sync detector helps in coming up with a simplified sync signal. The datasheet for LM1881, a sync separator chip, has a functional circuit diagram along with a helpful description of how V-Sync is detected and how even fields are distinguished from odd fields. These are my observations:
1. 70 mV is the threshold for sync. If the video signal is 70 mV more than its minimum value, the region is marked non-sync; otherwise, it's marked as a sync pulse. Although the NTSC standard states that sync should be 0.3 V below the black level, the precise value of the sync pulse amplitude is not important (0.2 V to 0.4 V is OK).
2. Short sync pulses are horizontal syncs. If the sync pulse width exceeds some threshold, which I'm guessing is about 1/4 of the width of a full scanline, it is marked as a v-sync pulse. The precise duration of the sync pulse is unimportant. Short sync pulses should be about 4 uS, and long sync pulses should be about 30 uS. In NTSC jargon, the first serration pulse triggers vertical sync. The pre-equalization, post-equalization, and the other serration pulses are ignored by modern sync detectors.
3. Even/Odd fields are identified by when the long sync (v-sync) pulse occurs. If it occurs at the beginning of a scanline, the field is marked as an odd field; if it occurs in the second half of a scanline, the field is declared an even field.
The NTSC standard requires interlace scanning. But, progressive scanning is easier to implement and gives twice the frame rate. Based on the above insights, the following is a simplified NTSC sync signal for "fake-progressive" scan. In this mode, only the odd field is drawn in every frame, and the even field is left blank (every other scanline on the TV is left blank). This kind of progressive scan is done by nearly all the old 8-bit game consoles (NES, SNES, Sega master etc.).
The schematic below includes an Atmega16 microcontroller and a 7495 shift register. We'll see in a moment how the shift register aids color generation. The crystal frequency is 14.31818 MHz, and this was chosen specifically to be 4 times the NTSC color carrier frequency (3.579545 MHz). The Timer/PWM peripheral provides the sync signal on pin PD5 (OC1A) and saves us from having to cycle count assembly code for getting a consistent sync signal. All eight bits of PORTA are used to generate "pixels". The upper four bits select the color (chroma), and the lower four bits set the intensity (luma).
To generate color in NTSC, different phases of the 3.579545 MHz carrier are needed. One way of accomplishing this is by running the processor at a frequency that is a large multiple of 3.579545 MHz and generating different phases of the color carrier using the GPIO or the SPI peripheral. This is how the rbox generates color. Unfortunately, the maximum frequency at which Atmega16 can run is 20 MHz, which restricts the number of colors that can be generated this way. This is where an external shift register can help. Initially, '1100' is loaded to the register. The figure below depicts how the shift register, which is configured as a ring counter, generates four phases (0, 90, 180, and 270 degrees) of the color carrier from a 14.31818 MHz clock. By adding different phases, we can get intermediate phases (ex: adding 0 and 90 degrees gives 45 degrees because sin(x) + cos(x) = 0.707 * sin(x+45)), and each phase produces a distinct hue. The four 560 Ohm resistors in the schematic need not be identical. Using unequal values will give different colors. Also, additional resistors, can be added with extra chroma bits to obtain more colors. Another non-standard NTSC signalling we can get away with is to keep the sub-carrier always turned "ON" rather than only during the color burst (yes, this includes adding the sub-carrier during the sync pulse too). This works because the sub-carrier is filtered out any way.
Four phases of the sub-carrier |
Color palette from "fake-progressive" scan on CRT TV |
Note that each line has to have a duration of precisely 227.5 sub-carrier cycles to reduce luma-chroma interference spatially. The reason for this is detailed here. If the line duration is rounded to 228 cycles per line, the interference becomes visible, as shown below, in the form of bands around color transitions.
227.5 cycles per line |
228 cycles per line |
Another crucial aspect is the number of lines per frame. With 262 lines per frame and 227.5 sub-carrier cycles per line, the alternating luma-chroma interference pattern will be static, which is not pleasant. But, with 263 lines per frame, the interference pattern alternates both spatially and temporally, thereby considerably reducing the visibility of luma-chroma interference artifacts. This is explained in more detail here. Chris Covell has documented this with slow motion in old game consoles, which I am reproducing below (the alternating interference pattern becomes less visible at 60 FPS, and we perceive only the average color). The NES console, even though it outputs 262 lines per frame, achieves this temporal artifact cancelling by increasing the length of one of the scanlines every other frame.
Static artifacts |
Temporally changing artifacts |
Although this looks fine on a CRT, the digital filter (a comb filter I expect) in modern flat screen TVs, which expects an interlaced NTSC signal, interacts poorly with "fake-progressive" scanning and produces alternating black and white dots around color transitions.
"fake-progressive" scan artifact in LCD TV zoomed in |
"fake-progressive" scan on LCD TV |
The only solution that occurred to me was to use interlace scanning (please leave a comment if you think of a way to get the filter in flat screen TVs to work properly with "fake-progressive" scans. And no, my TV doesn't have an option to disable the filter). The following is a simplified sync signal for interlace scanning.
With interlace scanning, the digital flat screen TV is happy and produces, as shown below, a decent image. But, interference is slightly more visible in the old CRT TV because the frame rate drops from 60 FPS to 30 FPS (it doesn't have a comb filter).
Interlace scan in LCD TV zoomed in |
Interlace scan on LCD TV |
The firmware and the schematic for the Atmega16 NTSC color palette generator are available under an open-source license here. Other projects, such as the Uzebox, demonstrate how you can use this to make a full-fledged game console.
Finally, I would like to acknowledge Thejasvi, with whom I worked on NTSC during our undergrad days. The following is my collection of links about NTSC. Have fun building retro game consoles!
[1] NTSC demystified - B&W Video and Sync - Part 1
[2] NTSC demystified - Color Encoding - Part 2
[3] NTSC demystified - Nuances and Numbers - Part 3
[4] NTSC demystified - Math - Part 4
[5] NTSC demystified - Implementation - Part 5
[6] NTSC demystified - Cheats - Part 6
[7] PIC PONG by Rickard Gunee
[8] Text on TV by Batsocks
[9] Video Basics - Maxim
[10] PAL TV Timings and Voltages - Retroleum
[11] AVR Video Generator with Mega163 - by Bruce Land
[12] PAL and NTSC timing information - ePanorama
[13] NTSC V-Sync Confusion - AVRFreaks Forum post
[14] Scanning, Timing, Sync Recovery, Numbers
[15] "TV Paint", Embedded Systems Design Laboratory EE281, Handout 7 - Stanford University
[16] Video Generation with Mega644, EE476 - Cornell university
[17] Video Primer - Uzebox
[18] SX PONG - Rickard Gunee
[19] AVR PAL Generation
[20] AVR PAL Color Bar
[21] Lazarus-64 Project
[22] AVRFreaks Forum Post by AtomicZombie
[23] PAL Encoding using FPGA
[24] NTSC Color Decoding
[25] Dot Crawl - Wikipedia
[26] AD725 Datasheet
[27] XGS AVR
[28] RBox
[29] Apple II graphics
[30] "I want my RGB" by Chris Covell
[31] LM1881 datasheet
[32] "RGB vs. Composite" by Chris Covell
Awesome. Will definitely revisit. In the meantime, you might be able to ditch the shiftreg by using an avr's pwm dead-time or power stage controller and pll-driven timer... attiny861/85, at90pwm-series... search avr-lvds-lcd. Seriously awesome job here... been wondering if color was possible for quite some time. Reading the ntsc specs just made my head hurt.
ReplyDeleteVery interesting project... I have been working on composite NTSC video for a week or so now and this has helped me get close...
ReplyDeleteIn the process of debugging my code I tried yours. Ported it from the Atmega16 to and Atmega234P at 14.318MHz and I am running into something weird...
It appears that I get a full field of video taking 17.5ms approx then 3.5 fields of sync approx 69ms. I should only bee getting 3 lines of sync correct totaling around 190us.
Any suggestions as to what might be the issue? It has me stumped.
I found the issue in my ported code after the timer/pwm config portion i had to add the following line of code
ReplyDeleteMCUCR = (1 << BODS);
This is a cool project, but I can't find the CLK pin for the 74LS195, or what it should be connected to. I realize this has been around for over 4 years, but it would still be useful to make the CLK more obvious, assuming this isn't an error of omission.
ReplyDeleteI, as well, was looking for how the 74LS195 is driven from the clock. The datasheet says pin 10 is clock pulse, but the above schematic does not show this pin. Careful reading of the ATmega16 datasheet says XTAL2 is the oscillator drive output, so you may be able to get away with connecting that directly to the CP(10) pin of the 74LS195. However, if this setup loads the oscillator too much a buffer will need to be placed between the oscillator and the 74LS195. Another thing to consider is that some AVRs have a clock output pin than can provide a prescaled and buffered clock source (CLKO) at the same frequency as the system clock. The ATmega48/88/168/328 parts have this option.
ReplyDelete