]> git.defcon.no Git - avrfbosd/blob - syncgen/main.c
Changed syncgen to allow disabling of background image. By defining DRAW_LOGO the...
[avrfbosd] / syncgen / main.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <util/delay.h>
4
5 #define ZERO PORTB &= 0xFC
6 #define BLACK PORTB |= 0x01
7
8 #define DRAW_LOGO
9
10 // -----..----- ^
11 // | ___ | |
12 // Com RST -| RST VCC |-`
13 // | |
14 // MegaClk -| CLKI ADC1 |---< VidDetect
15 // | | 470R
16 // VSwithc -| PB4 PB1 |--^v^v-.
17 // | | |----> VidOut
18 // .--| GND PB0 |--^v^v-'
19 // | | | 1kR
20 // --- ------------
21
22 #include "avrosdlogo.h"
23
24 volatile int rasterline;
25 volatile uint16_t line;
26
27 volatile uint8_t hres;
28 volatile int renderLine;
29 int stretch;
30
31 void asm_render_line( void ) {
32 __asm__ __volatile__ (
33 ".macro outbit p" "\n\t"
34 "BST __tmp_reg__,7" "\n\t" // Store bit 7 to T
35 "BLD r16,1" "\n\t" // Load bit T into r16 bit number 4
36 "OUT \\p,r16" "\n\t" // Send contents of r16 to %[port]
37 "NOP" "\n\t"
38 ".endm" "\n\t"
39
40 "ADD r26,r28" "\n\t" // Add register Y to register X, low part
41 "ADC r27,r29" "\n\t" // Add high Y to X with carry from low part
42
43 "IN r16,%[port]" "\n\t" // Save port content to register 16
44 "RJMP start_line" "\n\t"
45
46 "loop_byte:" "\n\t" // Notice that in the macro we always use bit 7 and left-shift
47 "BST __tmp_reg__,6" "\n\t" // Here we use bit 6 without left-shift to output bit 0
48 "BLD r16,1" "\n\t" // of each byte (except the last bit on the line)
49 "OUT %[port],r16" "\n\t" // We do not have time for a left-shift, teh "extra cycle" was used for "dec-branch"
50 "NOP" "\n\t"
51
52 "start_line:" "\n\t"
53 "LD __tmp_reg__,X+" "\n\t" // Load from address pointed to by X into temporary register, then increment X-pointer
54
55 "outbit %[port]" "\n\t" // Output bit 7 using Macro
56 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
57 "outbit %[port]" "\n\t" // Output bit 6 using Macro
58 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
59 "outbit %[port]" "\n\t" // Output bit 5 using Macro
60 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
61 "outbit %[port]" "\n\t" // Output bit 4 using Macro
62 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
63 "outbit %[port]" "\n\t" // Output bit 3 using Macro
64 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
65 "outbit %[port]" "\n\t" // Output bit 2 using Macro
66 "LSL __tmp_reg__" "\n\t" // Left-shift for next bit
67 "outbit %[port]" "\n\t" // Output bit 1 using Macro
68
69 "DEC %[hres]" "\n\t" // Decrement num-bytes-remaining-in-resolution
70 "BRNE loop_byte" "\n\t" // Branch to loop6 if %[hres] != zero
71
72 "LSL __tmp_reg__" "\n\t" // Left-shift for last bit on the line
73 "outbit %[port]" "\n\t" // Output bit 0 using Macro.
74
75 "CBI %[port],1" "\n\t" // Clear our "display bit" to ensure we end on black
76
77 :
78 : [port] "i" (_SFR_IO_ADDR(PORTB)),
79 "x" (testimg_data),
80 "y" (renderLine),
81 [hres] "d" (hres)
82 );
83 }
84
85 void hsync(void)
86 {
87 line++; // Bumping the line-counter as part of hsync gives just the right front-porch ;D
88 ZERO; _delay_us(4); // sync
89 BLACK; _delay_us(8); // Back porch
90 }
91
92 void vsync(uint8_t odd_even)
93 {
94 uint8_t a,b;
95 cli();
96
97 // Pre-equalization pulses
98 b = odd_even ? 6 : 5;
99 for(a=0; a<b ; a++) // short sync
100 {
101 ZERO;
102 _delay_us(2);
103 BLACK;
104 _delay_us(30);
105 }
106
107 // VSYNC
108 for(a=0; a<5 ; a++) // long sync
109 {
110 ZERO;
111 _delay_us(30);
112 BLACK;
113 _delay_us(2);
114 }
115
116 // Post-equalization pulses
117 b = odd_even ? 5 : 4;
118 for(a=0; a<b ; a++) // short sync
119 {
120 ZERO;
121 _delay_us(2);
122 BLACK;
123 _delay_us(30);
124 }
125 line = odd_even ? 5 : 317;
126 renderLine = 0;
127
128 sei();
129 }
130
131 ISR (TIMER0_COMPA_vect)
132 {
133 if(line == 310) vsync(0); // End of ODD fields, start vsync'ing
134 else if(line == 622) vsync(1); // End of EVEN, start vsync'ing
135 else // In frame. Generate a hsync pulse, then do some drawing :)
136 {
137 hsync();
138
139 int t_line = line; // Reducing line-count complexity by half ;)
140 t_line = ( line > 312 ) ? line - 312 : line;
141 #ifdef DRAW_LOGO
142 // Draw the logo-image centered vertically
143 if ( t_line > 118 && t_line < 118 + (2*testimg_height) )
144 {
145 _delay_us(12.3);
146 asm_render_line();
147
148 // Stretch the image vertically by repeating the same line twice
149 if( !stretch )
150 {
151 stretch = 1;
152 renderLine += hres;
153 // Make sure we don't draw anything from RAM after the end of the image
154 if ( renderLine > ((testimg_height*hres)-1) ) renderLine = 0;
155 } else stretch--;
156 }
157 #endif
158 }
159 return;
160 }
161
162 ISR (TIMER1_OVF_vect)
163 {
164 // If this interrupt-vector is run, Timer1 has overflowed, and that means
165 // we have not seen any signal of significant level for quite a while.
166 // This means Sync-generation is needed...
167
168 // Make sure sync-gen is running...
169 TIMSK |= (1<<OCIE0A); // Enable compare-mach-a interrupt for Timer0
170 // Open up the switch blocking attiny-generated video:
171 PORTB |= 0x10;
172 }
173
174 int main(void)
175 {
176 rasterline = 0;
177 line = 0;
178
179 hres = testimg_width / 8;
180
181 DDRB |= (1<<PB0); // Black/zero level pin/resistor
182 DDRB |= (1<<PB1); // Color-pin, adds to black level ;)
183 DDRB |= (1<<PB4); // Control of video-switching. Fuse-bits changes this pin from XTAL2 to PB4
184
185 // Timer0 is used to generate hsync (and vsync)
186 TCCR0A |= (1<<WGM01)|(0<<WGM00); // Set Clear on compare match
187 TCCR0B |= (0<<WGM02); // using WGM bits ...
188 TCCR0B |= (0<<CS02)|(1<<CS01)|(0<<CS00); // Set prescaler to CLK/8
189 TIMSK |= (1<<OCIE0A); // Enable compare-mach-a interrupt
190 OCR0A = 159; // 160 "ticks" on 20MHz/8 => 64us. Trigger one early..
191
192 // Video detection is done using ADC1 on PB2...
193 ADMUX |= (1<<ADLAR); // Left-shifted result.
194 ADMUX |= (1<<MUX0); // Select ADC for PB2
195 ADCSRA |= (1 << ADEN); // Enable ADC
196 ADCSRA |= (1 << ADPS2)|(1 << ADPS1)|(0<<ADPS0); // set prescaler to 64
197 DIDR0 |= (1<<ADC1D); // Disable logic input for PB2
198
199 TCCR1 |= (1<<CS13)|(0<<CS12)|(0<<CS11)|(1<<CS10); // Prescale Timer1 to CK/256
200 // CK/32 @ 20MHz gives 0.05us*256*256 = 3276.8us per Overflow, or 51 lines.
201 TIMSK |= (1<<TOIE1); // Enable Timer Overflow interrupts for Timer1
202
203 sei(); // We are ready to fire interrupts.
204
205 // Here, we continue to do nothing...
206 while(1)
207 {
208 ADCSRA |= (1 << ADSC); // start ADC measurement
209 while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
210 {
211 // We are detecting "video" if we have a value
212 // greater than some minimal value on ADC1/PB2.
213 // This approach requires a weak pulldown on the relevant
214 // video input to the circuit, but it beats not detecting anything.
215 // 255/50*4=20, thus 0.4V
216 if (ADCH > 20)
217 {
218 // Make sure any signal generated by us does not leak back
219 PORTB &= 0xEF; // Switch OFF VOut-switch for ATtiny
220 // The next statement stops sync-generation, thus also image-generation..
221 TIMSK &= ~(1<<OCIE0A); // Enable compare-mach-a interrupt
222 // Reset Timer1 so we don't generate a TOVF..
223 TCNT1 = 0;
224 }
225 // If we did not see a signal above the treshold, TCNT1 will continue
226 // to count up, until a TOV1 interrupt is generated.
227 }
228 };
229 }