I wanted to try and build a cheap alternative that did the sync conversion properly, instead of just by chance. Hopefully this means that my solution will work with some video processors and digital chassis CRTs, but I need others to verify this.
Circuit
The circuit I have design uses two sync stripping circuits, two 7400 series logic chips and an atmega328 based Arduino. The LM1881 outputs an Odd/Even signal into a digital input of the Arduino and a Vsync pulse into one of the Arduino external interrupt pins. An Intersill ISL59885 sync splitter outputs a pure Hsync with no 2*H component from the serration or equalising pulses. The pure Hsync is also feed into a external interrupt pin, as well as sent into one of the mux inputs. Another mux input is feed an inverted version of the pure Hsync from the inverter. The Hsync and Inverted Hsync are selected with a digital high or low from the Arduino via the mux.
What all of this means is that A sync signal is reconstructed to look as close as possible to a standard progressive sync signal. It also means that the alignment of the two fields can be swapped. What that means is you can move the image up and down vertically by changing when the Vsync is inserted. Not only that, but you can offset the individual odd and even fields to move one up or down independent of the other. This can be useful on proper interlaced games as you can choose where the flicker will appear. It is quite hard to describe this in words though.
IC Component List:
- LM1881 sync stripper (for ODD/EVEN field detect)
- ISL59885 sync stripper (for pure Hsync)
- ATmega328p (Arduino)
- 74HC04 Hex Inverters (Or 74HC14 Schmitt Trigger Inverters)
- 74HC151 8-to-1 Multiplexor (Or any 3+ to 1 Mux)
Edit: Basic circuit diagram below.
Here is a basic diagram of how this is connected. The Atmega328p pins are the same as Arduino Uno digital pins 2-8.
(A single TI LMH1980 Sync Chip could replace both the LM1881 and ISL59885 as it has both Odd/Even and Hsync out)
Arduino Code
My code is design to be used with two push switches. This could (and should) be improved to use multiple selection switches instead. The code selects between a few options:
- Turn processing on/off (bypass processing)
- Swap odd/even field alignment
- Shift image up
- Shift image down
EDIT: I've update this to solve the Vsync jitter issue by disabling timer0. Also, the code now auto switches to remove the offset when a signal swaps between interlaced and progresive. Note that 576i usally requires no offset, while 480i usally requires the offset turned on, so you still need to manually change this when switching between NTSC and PAL timings. I have no solution for this at the moment.
Spoiler
Code: Select all
// Sync Processor Arduino Code
//
// 480i to 240p sync processor for use with LM1881 and ISL59885.
// Or any sync cleaners/strippers with hsync, vsync and odd/even field outputs.
// Signal remuxed with 74HC151 logic mux or similar from hsync and inverted hsync.
//
// Author: Duane (Dooklink) Cook
// Date: 2014.12.07
// Power reduction / Hardware disable code from Nathan Seidle, Spark Fun Electronics 2011
// Delays assume 8MHz clock, but timing is not critical here for button polling
#include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
volatile unsigned char vCount = 0;
volatile char vFlag = false;
volatile unsigned int delayCountA = 0;
volatile unsigned int delayCountB = 0;
volatile char field = true;
char offsetSetting = true;
unsigned int delaySetting = 0;
char buttonMode = 0;
char buttonDelta = true;
char forceProgressive = true;
char interlacedSignal = true;
char previousField = false;
char fieldCheckCount = 0;
char fieldSwapCount = 0;
/***************************************************
Arduino setup function
****************************************************/
void setup()
{
// To reduce power, setup all pins as inputs with no pullups
for(int x = 1 ; x < 18 ; x++){
pinMode(x, INPUT);
digitalWrite(x, LOW);
}
pinMode(2, INPUT); // Vsync interrupt pin
pinMode(3, INPUT); // Hsync interrupt pin
pinMode(4, INPUT); // Odd/Even field input pin
pinMode(5, OUTPUT); // Mux control pin bit 2 - HIGH -> bypass, LOW -> Hsync/not(Hsync)
pinMode(6, OUTPUT); // Mux control pin bit 3 - HIGH -> Hsync, LOW -> not(Hsync)
pinMode(7, INPUT_PULLUP); // Switch 1 input - select button 2 mode
pinMode(8, INPUT_PULLUP); // Switch 2 input - bypass/forceprogressive, line offset on/off, vshift up, vshift down
attachInterrupt(0, vSync, FALLING ); // Pin 2 - vSync in
attachInterrupt(1, hSync, FALLING ); // Pin 3 - hSyncn in
digitalWrite(5, LOW); // Force progressive
digitalWrite(6, HIGH); // Select Hsync to begin with
ADCSRA &= ~(1<<ADEN); //Disable ADC
ACSR = (1<<ACD); //Disable the analog comparator
DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins
// Buffers are still required for external interupt pins for now
//DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0
power_twi_disable();
power_spi_disable();
power_usart0_disable();
power_timer0_disable(); // Normally for delay_ms(), removes competing interrupt
power_timer1_disable();
power_timer2_disable();
sei(); //Enable global interrupts
}
/***************************************************
Arduino loop function
****************************************************/
void loop()
{
fake_msdelay(25); // Replaces timer driven delay()
field = !digitalRead(4);
if ( field != previousField )
{
fieldSwapCount++;
}
previousField = field;
fieldCheckCount++;
if (fieldSwapCount > 1)
{
interlacedSignal = true;
fieldSwapCount = 0;
fieldCheckCount = 0;
}
else if (fieldCheckCount > 5)
{
interlacedSignal = false;
fieldSwapCount = 0;
fieldCheckCount = 0;
}
if (!digitalRead(7) || !digitalRead(8) )
{
if (!digitalRead(8) && forceProgressive)
{
if (!buttonDelta)
{
buttonMode++;
buttonMode = buttonMode % 4;
}
buttonDelta = true;
}
if (!digitalRead(7))
{
if (!buttonDelta)
{
switch (buttonMode)
{
case 0:
if (forceProgressive)
{
detachInterrupt(0);
detachInterrupt(1);
digitalWrite(5, HIGH);
forceProgressive = false;
}
else
{
attachInterrupt(0, vSync, FALLING ); // Pin 2 - vSync
attachInterrupt(1, hSync, FALLING ); // Pin 3 - hSync
digitalWrite(5, LOW); // Force progressive
forceProgressive = true;
}
break;
case 1:
offsetSetting = !offsetSetting;
break;
case 2:
delaySetting++;
if (delaySetting > 349)
delaySetting = 0;
break;
case 3:
if (delaySetting == 0)
delaySetting = 350;
delaySetting--;
break;
default:
break;
}
}
buttonDelta = true;
}
}
else
{
buttonDelta = false;
}
}
/***************************************************
Interrupt Service Routines
****************************************************/
void vSync()
{
if (field == LOW)
{
if (offsetSetting && interlacedSignal)
{
delayCountA = delaySetting +1;
}
else
{
delayCountA = delaySetting;
}
field = HIGH;
}
else
{
delayCountB = delaySetting;
field = LOW;
}
}
void hSync()
{
if (delayCountA == 0 || delayCountB == 0)
vFlag = true;
if (vFlag)
{
if (vCount < 3)
{
digitalWrite(6 ,LOW);
vCount++;
}
else
{
digitalWrite(6 ,HIGH);
vCount = 0;
vFlag = false;
}
}
delayCountA--;
delayCountB--;
}
/***************************************************
Replacement Delay Routines
****************************************************/
// This is a not-so-accurate delay routine
// Calling fake_msdelay(100) will delay for about 100ms
// Assumes 8MHz clock
void fake_msdelay(int x){
for( ; x > 0 ; x--)
fake_usdelay(1000);
}
// This is a not-so-accurate delay routine
// Calling fake_usdelay(100) will delay for about 100us
// Assumes 8MHz clock
void fake_usdelay(int x){
for( ; x > 0 ; x--) {
__asm__("nop\n\t");
__asm__("nop\n\t");
__asm__("nop\n\t");
__asm__("nop\n\t");
__asm__("nop\n\t");
__asm__("nop\n\t");
__asm__("nop\n\t");
}
}
The LM1881 datasheet shows a good example of what an interlaced sync signal should look like:
I have been using an analogue CRO scope to analyse the sync signals. On analog scopes the signal is displayed differently when using one or two inputs. When using one input, an interlaced sync signal is displayed starting at the Vsync point. This is the start of the serration pulses, just after the pre equalising pulses.
It is quite hard to see the transition from the post equalising pulses into the standard Hsync periods. This is due to both odd and even fields being displayed on top of one another. When most analogue scopes are switched to display two inputs, each input displays only one field, either odd or even. In the example below section 1 shows the serration pulses, section 2 the post-equalising pulses and section 3 the start of the video field. Note that the start of the video field has no active video as it is the start of the 525(625 in PAL) lines not the active the 480(576 in PAL) lines.
The vertical red lines show how the odd and even fields are offset by half of the Hsync period. What can also be seen is that the vertical blanking interval (VBI) contains 2*Hsync pulses. The next image shows one field of the same interlaced sync, and then one field of my processed sync signal. The red line still shows that there is an offset between the odd and even fields, however, the 2*Hsync VBI is gone. Now we have progressive sync!
It is also worth noting that the offset between odd and even fields MUST remain. Since the RGB video signals already have the half Hsync offset between fields this offset must reamain in the Hsync area of our new Composite sync signal. This means that the new progressive Csync is not strictly progressive, but it is as close as you can get.
Here is what the new Csync looks like on the scope as a single input. Here it is still being triggered from the interlaced sync, but you don't need to worry about that. What is important is the half Hsync offset is obvious as the signal looks twice the frequency. Again the odd and even fields are displayed on top of each other now.
I'll get a circuit diagram uploaded as soon as I can. Hopefully this post makes sense so that others can try this out where Extron units haven't worked.
Cheers.
| My games - 
