#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
-#define ZERO PORTB=0b000
-#define BLACK PORTB=0b001
-
-// -----..----- ^
-// | | |
-// -| RST VCC |-`
-// | |
-// .--| X1 PB2 |-
-// [ ] | | 470R
-// '--| X2 PB1 |--^v^v-.
-// | | |----> VidOut
-// .--| GND PB0 |--^v^v-'
-// | | | 1kR
-//----- ------------
+
+#define ZERO PORTB &= 0xFC
+#define BLACK PORTB |= 0x01
+
+// -----..----- ^
+// | ___ | |
+// Com RST -| RST VCC |-`
+// | |
+// MegaClk -| CLKI ADC1 |---< VidDetect
+// | | 470R
+// VSwithc -| PB4 PB1 |--^v^v-.
+// | | |----> VidOut
+// .--| GND PB0 |--^v^v-'
+// | | | 1kR
+// --- ------------
#include "avrosdlogo.h"
void hsync(void)
{
- line++;
+ line++; // Bumping the line-counter as part of hsync gives just the right front-porch ;D
ZERO; _delay_us(4); // sync
BLACK; _delay_us(8); // Back porch
}
// Pre-equalization pulses
b = odd_even ? 6 : 5;
- for(a=0; a<b ; a++) // 6x short sync
+ for(a=0; a<b ; a++) // short sync
{
ZERO;
_delay_us(2);
-
BLACK;
_delay_us(30);
-
}
// VSYNC
- for(a=0; a<5 ; a++) // 5x long sync
+ for(a=0; a<5 ; a++) // long sync
{
-
ZERO;
_delay_us(30);
-
BLACK;
_delay_us(2);
-
}
// Post-equalization pulses
b = odd_even ? 5 : 4;
- for(a=0; a<b ; a++) // 5x short sync
+ for(a=0; a<b ; a++) // short sync
{
ZERO;
_delay_us(2);
-
BLACK;
_delay_us(30);
-
}
line = odd_even ? 5 : 317;
renderLine = 0;
ISR (TIMER0_COMPA_vect)
{
- if(line == 310) vsync(0);
- else if(line == 622) vsync(1);
- else
+ if(line == 310) vsync(0); // End of ODD fields, start vsync'ing
+ else if(line == 622) vsync(1); // End of EVEN, start vsync'ing
+ else // In frame. Generate a hsync pulse, then do some drawing :)
{
hsync();
- int t_line = line;
+
+ int t_line = line; // Reducing line-count complexity by half ;)
t_line = ( line > 312 ) ? line - 312 : line;
+ // Draw the logo-image centered vertically
if ( t_line > 118 && t_line < 118 + (2*testimg_height) )
{
_delay_us(12.3);
asm_render_line();
+ // Stretch the image vertically by repeating the same line twice
if( !stretch )
{
stretch = 1;
renderLine += hres;
+ // Make sure we don't draw anything from RAM after the end of the image
if ( renderLine > ((testimg_height*hres)-1) ) renderLine = 0;
} else stretch--;
}
return;
}
+ISR (TIMER1_OVF_vect)
+{
+ // If this interrupt-vector is run, Timer1 has overflowed, and that means
+ // we have not seen any signal of significant level for quite a while.
+ // This means Sync-generation is needed...
+
+ // Make sure sync-gen is running...
+ TIMSK |= (1<<OCIE0A); // Enable compare-mach-a interrupt for Timer0
+ // Open up the switch blocking attiny-generated video:
+ PORTB |= 0x10;
+}
+
int main(void)
{
rasterline = 0;
hres = testimg_width / 8;
- DDRB =0b111;
+ DDRB |= (1<<PB0); // Black/zero level pin/resistor
+ DDRB |= (1<<PB1); // Color-pin, adds to black level ;)
+ DDRB |= (1<<PB4); // Control of video-switching. Fuse-bits changes this pin from XTAL2 to PB4
- TCCR0A |= (1<<WGM01)|(0<<WGM00);
- TCCR0B |= (0<<WGM02)|(0<<CS02)|(1<<CS01)|(0<<CS00);
- TIMSK |= (1<<OCIE0A);
- OCR0A = 159;
+ // Timer0 is used to generate hsync (and vsync)
+ TCCR0A |= (1<<WGM01)|(0<<WGM00); // Set Clear on compare match
+ TCCR0B |= (0<<WGM02); // using WGM bits ...
+ TCCR0B |= (0<<CS02)|(1<<CS01)|(0<<CS00); // Set prescaler to CLK/8
+ TIMSK |= (1<<OCIE0A); // Enable compare-mach-a interrupt
+ OCR0A = 159; // 160 "ticks" on 20MHz/8 => 64us. Trigger one early..
- sei();
+ // Video detection is done using ADC1 on PB2...
+ ADMUX |= (1<<ADLAR); // Left-shifted result.
+ ADMUX |= (1<<MUX0); // Select ADC for PB2
+ ADCSRA |= (1 << ADEN); // Enable ADC
+ ADCSRA |= (1 << ADPS2)|(1 << ADPS1)|(0<<ADPS0); // set prescaler to 64
+ DIDR0 |= (1<<ADC1D); // Disable logic input for PB2
+
+ TCCR1 |= (1<<CS13)|(0<<CS12)|(0<<CS11)|(1<<CS10); // Prescale Timer1 to CK/256
+ // CK/32 @ 20MHz gives 0.05us*256*256 = 3276.8us per Overflow, or 51 lines.
+ TIMSK |= (1<<TOIE1); // Enable Timer Overflow interrupts for Timer1
+
+ sei(); // We are ready to fire interrupts.
// Here, we continue to do nothing...
- while(1);
+ while(1)
+ {
+ ADCSRA |= (1 << ADSC); // start ADC measurement
+ while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
+ {
+ // We are detecting "video" if we have a value
+ // greater than some minimal value on ADC1/PB2.
+ // This approach requires a weak pulldown on the relevant
+ // video input to the circuit, but it beats not detecting anything.
+ // 255/50*4=20, thus 0.4V
+ if (ADCH > 20)
+ {
+ // Make sure any signal generated by us does not leak back
+ PORTB &= 0xEF; // Switch OFF VOut-switch for ATtiny
+ // The next statement stops sync-generation, thus also image-generation..
+ TIMSK &= ~(1<<OCIE0A); // Enable compare-mach-a interrupt
+ // Reset Timer1 so we don't generate a TOVF..
+ TCNT1 = 0;
+ }
+ // If we did not see a signal above the treshold, TCNT1 will continue
+ // to count up, until a TOV1 interrupt is generated.
+ }
+ };
}