Source file: /~heha/enas/vabl.zip/vabl.S

/* Programm für Vertikalsynchronisation und Klasse-D-Endstufe
 * für ATtiny13 @ 16 MHz
 * „h#s“ Henrik Haftmann, TU Chemnitz, 22. März 2016
 * tabsize = 8, encoding = utf-8 (ohne Windows-Header BOM)
Hardware:
1	PB5	ADC0	Einstellung der Bildhöhe (Sollamplitude) 3,75 .. 5 V
2	PB3	PCINT3	Kombinations-Sync-Eingang, negativ, H: 15..16 kHz, V: 48..62 Hz
3	PB4	ADC2	Spulenstrommessung (Istwert)
4		GND	0 V
5	PB0	OC0A	Horizontalausgang (70% Tastverhältnis, nicht springend)
6	PB1	OC0B	Push-Pull-Ausgangstreiber, invertierend
7	PB2		Booster-Transistor, H = Boost
8		Ucc	5 V
Da der Reset-Eingang mit einer „hohen“ Analogspannung benutzt wird,
ist das Programmieren der Reset-Disable-Fuse nicht unbedingt notwendig.
PWM-Trägerfrequenz: 38 kHz (8-Bit-PWM) zeilensynchron
Zum Vergleich: STV9380A: Modulationsfrequenz 140 kHz asynchron;
			 Rückschlag 700 µs ≈ 11 Zeilen
Was noch reinpasst:
* Automatische Erkennung der Sync-Polarität
* Schnelle HSync-Anpassung nach Vsync für Signal aus VHS-Videorekorder
* Koindizenzerkennungs-Ausgang (am Bildhöhen-Poti)
* OneWire-Digitalsteuerung (an Stelle des Bildhöhen-Potis);
  für's TV-übliche I²C wäre ein größerer µC angebracht.

Unklar: Regelzeitkonstante für HSync (sollte für verrauschtes TV-Signal lang sein)

Daten PAL-Fernsehsignal:
H:	1,5 µs vordere Schwarzschulter
	4,7 µs Synchronimpuls
	5,8 µs hintere Schwarzschulter: Σ 12 µs
V:	625 Zeilen, 575 sichtbar
	5 Vortrabanten: Zeilensynchronimpulse doppelter Frequenz und halber Dauer
	5 Synchronimpulse (2,5 Zeilen lang) 32 µs - 4,7 µs = 27,3 µs
	— erster Impuls im Raster zum Zeilensynchronimpuls = Beginn Zeile 0,
	— erster Impuls in 2. Hälfte = inmitten Zeile 312
	5 Nachtrabanten
 */

#define __SFR_OFFSET 0
#include <avr/io.h>

.section .fuse,"a",@progbits	// --set-section-flags=.fuse="alloc,load" ersparen
	.byte	0b11111010	// kein Taktteiler, kein SPI
	.byte	0b11111110	// kein Reset

.section .signature,"a",@progbits
	.byte	SIGNATURE_0,SIGNATURE_1,SIGNATURE_2

.text

/*	H:
Quadrant[----- 00 -----][----- 01 -----][----- 10 -----][----- 11 -----][-
Timer0	0	128	256	384	512	640	768	896	1024
µs	0	8	16	24	32	40	48	56	64
	     ___________________________________________________________
Sync(1)	\___/								\_	Horizontalsynchronimpuls
								    ____
Sync(2)	\__________________________________________________________/	\_	Komb. Vertikalsynchronimpuls (Vollbildmodus)
	   _____________________________   _____________________________
Sync(3)	\_/				\_/				\_	Vor- oder Nachtrabanten {(3)-(5) = Halbbildmodus}
				    ____			    ____
Sync(4)	\__________________________/	\__________________________/	\_	Komb. Vertikalsynchronimpuls
	   _____________________________			    ____
Sync(5)	\_/				\__________________________/	\_	Halbzeilig einsetzener Vertikalsynchronimpuls
	_____________
Sync(6)		     \____________________________________________________	Nur Vertikalsynchronimpuls (unkorreliert)

PinChg	PpPpPppppp		    PpppPpPpppp			    Ppp	Pp	Interrupts (Großbuchstaben) und Rechenzeiten
T0Ovl			O───────o			O─────────o
T0CmpA		      Aaaaaaaaaa↓	A   A──aaaaaaaaaaaaaaaaaaa↓		(O verzögern ist unkritisch, P nicht!)
OCR0A		      var	40	     00			↓ E0		Wert und Zeitpunkt, wirkt nach Zählerüberlauf
OCR0B								var		Wert und Zeitpunkt, wirkt nach Zählerüberlauf
		       _________________________________________________
PB0	\_____________/ 						\_	zum Zeilentransistor
	 _		 _		 _		 _		 _
PB1	/ \\\\\\\\\\\\\_/ \\\\\\\\\\\\\_/ \\\\\\\\\\\\\_/ \\\\\\\\\\\\\_/	PWM-Vertikalendstufensteuerung
	     ___________
ADC	____/fertig	\Start____________________________________________	synchron laufender A/D-Wandler
	12  13		0   1	2   3	4   5	6   7	8   9	10  11

½Zeile	n	      n+1			      n+2			alle 32 µs

Der Quadrant 01 wird per CTC ein wenig verkürzt,
um hinreichend streng zeilensynchron mit wenig Jitter (< 1 ‰!)
den Zeilensynchronimpuls auszugeben.
(Die 16-bit-Uhr aus TCNT0 und R9 springt dann bspw. von 509 auf 512.)
Das beeinträchtigt die PWM-Vertikalendstufensteuerung ein wenig.
Daher wird OSCCAL so groß wie möglich gehalten.
Über ungleichmäßige Hsync in der Nähe der Vertikalsynchronisation
aus Videorecordern mache ich mir hier keine Gedanken.
Ohne HSync (6) läuft die Rattermaschine einfach mit der maximalen OSCCAL-Frequenz durch.

	V:

½Zeile	0	64	128	192	256	320	384	448	512	576	640
hex:		040	080	0C0	100	140	180	1C0	200	240	280
	 										 
PB2	/\______________________________________________________________________________/\

PB1	__........ooooooooooooooooOOOOOOOOOOOOOOOOOOOOOOOOOOOOOººººººººººººººººººººººººº__

≈ADC	 +100	+80	+60	+40	+20	0	-20	-40	-60	-80   -100	negativ gehender Ablenkstrom

≈OCR1B	0 28	48	68	88	108	128	148	168	188	208	228	steigender Wert nach unten

PWM	LL	 mehr Low	...	...	50%	...	...	 mehr High	LL	„nichtinvertierende“ PWM

Registerbelegung:
R0	temporär
R1	Null
R3:R2	A/D-Wert für Strom-Null, um 10 0000 0000 (512) herum bei 0,55 V
R5:R4	Bildhöhe = Amplitude des Sägezahnstroms, immer 01 xxxx xxxx (256..511)
R7:R6	Rechenfaktor aus Bildhöhe/Zeilenzahl, für konstante Bildhöhe bei PAL↔NTSC-Umschaltung
R8	Bresenham-Akku für jitterarme Zeilensynchronisation auf CTC-Basis (OSCCAL wäre viel zu grob)
R9	High-Teil des Zählers, Quadrantenzähler (Bit 1 und 0)
R11:R10	tic = Zeitstempel der fallenden Flanke
R13:R12	e' = vzb. integrierter Fehler (für den PI-Regler)
R15:R14	A/D-Wert, y (PI-Regler) wenn Halbzeilenzahl >= 6, Zähler für Hauptprogramm
R17:R16	temporär
R19:R18	temporär
R23:R22	Halbzeilenzahl
R25:R24	Aktuelle Halbzeile (Halbzeilen erforderlich wegen Zeilensprungverfahren)
R27:R26	Feinabgleich der Zeilenfrequenz (Soft-PLL), normal 0xFF00
R29:R28	frei
R31:R30	frei

RAM und EEPROM sind ungenutzt, ebenso der Analogvergleicher.
*/

#define Kp	100
#define Ki	10	// y=Ki*(e-e')+Kp*e; e'=e;
#define MAXLINE	640

#define MIN_CTC 0xFC00
#define MAX_CTC 0xFF00

.macro outi port,val
	ldi	r16,\val
	out	\port,r16
.endm

// Interrupttabelle mit Kode
	rjmp	main
	reti			// INT0
	rjmp	pinchg
	rjmp	ovl
	reti			// EEPROM
	reti			// Analogvergleicher
// ISR: Vergleich an OCR0A
// Damit sich die beiden ISRs nicht bekriegen,
// wird zwar Timerwert TCNT0 bei der Vorderflanke des Synchronimpulses
// phasenstarr auf Null geregelt,
// aber diese ISR verzögert aufgerufen.
// Pro Bildzeile wird diese ISR 3x aufgerufen
	in	r0,TCCR0B
	outi	TCCR0B,0x01	// CTC-Modus beenden
	sbrc	r0,3		// War im CTC-Modus?
9:	 reti			// diesen Interrupt schnell beenden: Nachtrabanten erkennen; nächster Interrupt kommt in 4 µs
	inc	r9
	outi	TIFR0,0x02	// Überlauf-Interrupt löschen (ist hier stets gesetzt)
	outi	TIMSK0,0x06	// Überlauf-Interrupt freigeben
	adiw	r24,1		// Halbzeile erhöhen
	sbrs	r9,1
	 rjmp	q2
// Im Quadrant 00 ist zeitkritische Arbeit; Auslösung erfolgte weit (14 µs = 224 Takte) nach Überlauf
	mov	r0,r27		// High-Teil der Feinjustierung (Soft-PLL)
	add	r8,r26		// Bresenham-Akku += Low-Teil der Feinjustierung
	adc	r0,r1		// Übertrag addieren
	out	OCR0A,r0	// (R0 ist nahe oder gleich 0xFF)
	outi	ADCSRB,0x04	// A/D-Wandler mit nächstem Überlauf starten lassen
// Erst ab hier (24 Takte nach ISR) darf der Timer0 einen Überlauf haben
// Da keine Pegelwechsel-Interrupts erwartet werden,
// dürfen Interrupts weiter gesperrt bleiben.
	outi	TCCR0B,0x09	// CTC-Modus (unklar ob das auch gepuffert wird — lt. Datenblatt nicht)
	outi	TCCR0A,0x23	// OC0A vom Pin abkoppeln, Pin bleibt High
	ldi	r16,hi8(MAXLINE)
	cpi	r24,lo8(MAXLINE)
	cpc	r25,r16
	brcs	9b
	rjmp	vretr1		// Zwangs-Vertikalrückschlag
// Quadrant 10, Auslösung kurz (4µs = 64 Takte) nach Überlauf
q2:	outi	ADCSRB,0x01	// A/D-Wandler nicht mehr von diesem Interrupt starten
	out	OCR0A,r1	// 1 Takt nach Überlauf und niederpriorisiert: Wird von Überlauf-ISR geschluckt
	movw	r16,r24		// ab hier sind noch rund 450 Takte Zeit, abzüglich möglicher PinChange-ISR-Zeiten
	lsr	r17
	ror	r16		// Bit 0 ausschieben
// 5. Einlesen der Bildhöhe
	cpi	r16,2
	cpc	r17,r1
	brcs	1f		// wenn 1
	brne	2f
	in	r4,ADCL		// Sollbildhöhe alle 20 ms einlesen (nach 128 µs)
	in	r0,ADCH		// High-Teil weg wegen Reset-Multiplex, Stellbereich ¾Ucc..Ucc
	rcall	div10
	movw	r6,r16
	rcall	mul10
	movw	r6,r0
	clr	r21		// Fehler-Akku löschen
	clr	r20
	lsr	r23
	ror	r22		// ab jetzt halbe doppelte Zeilenzahl
	reti
1:	outi	ADMUX,0x42	// wieder Spulenstrom messen
	reti
2:
// 6. Ende des Vertikalrückschlags mittels Spulenstrom oder Maximaldauer
	in	r14,ADCL
	in	r15,ADCH
	sub	r14,r2		// jetzt vzb.
	sbc	r15,r3
	cp	r14,r4
	cpc	r15,r5
	brge	3f
	cpi	r16,lo8(10)
	cpc	r17,r1
	brcs	4f
3:	cbi	PORTB,2
// 7. Sollstrom aus Zeilennummer berechnen
4:	movw	r18,r24		// lfd. Zeilennummer
	sub	r18,r22
	sbc	r19,r23		// vzb. Zeilennummer (-320..+319)
	movw	r16,r6		// vorberechneter Faktor aus Bildhöhe und Zeilenzahl
	rcall	mul10
	sub	r0,r14
	sbc	r1,r15
	movw	r18,r0		// e
	ldi	r16,lo8(Kp)
	ldi	r17,hi8(Kp)
	rcall	mul10
	movw	r14,r0
	sub	r18,r12		// e-=e'
	sbc	r19,r13
	add	r12,r18		// e'+=e
	adc	r13,r19
	ldi	r16,lo8(Ki)
	ldi	r17,hi8(Ki)
	rcall	mul10
	add	r14,r0
	adc	r15,r1
	clr	r1
	ldi	r16,0x80
	add	r14,r16
	adc	r15,r1
1:	brmi	1f
	cp	r14,r27		// maximal erlaubter PWM-Wert
	cpc	r15,r1
	brcs	2f
	out	OCR0B,r14	// der berechnete Wert
	reti
1:	out	OCR0B,r1	// der Minimalwert (0)
	reti
2:	out	OCR0B,r27	// der Maximalwert (zumeist 0xFF)
	reti

ovl:	inc	r9		// im Quadrant 01 oder 11
	outi	TIMSK0,0x04	// Mit Compare-Interrupt abwechseln
	sbrc	r9,1
	 rjmp	1f
// Im Quadrant 11 (r9 ist jetzt xxxx xx00)
	out	TIFR0,r16	// Output-Compare-Interruptflag (wegen OCR0A=0) löschen
	outi	TCCR0A,0xE3	// inverse PWM für OC0A im Quadrant 0 zum Zeilentransistor
	outi	OCR0A,0xF0	// nächste Auslösung weit nach Überlauf
	reti
// Im Quadrant 01 (r9 ist jetzt xxxx xx10)
1:	outi	OCR0A,0x40	// nächste Auslösung kurz (> 2,4 µs) nach Überlauf
	reti

//Ausgerollte vzl. Multiplikation
//R17:R16	Faktor 1 (10 bit)
//R19:R18	Faktor 2 (16 bit vzb.)
//R1:R0		Produkt/1024 (10 bit vzb.)
//Takte: 6*10+6 = 66
mul10:
	clr	r1
	clr	r0

	sbrc	r16,0
	 add	r0,r18
	sbrc	r16,0
	 adc	r1,r19
	asr	r1
	ror	r0
	
	sbrc	r16,1
	 add	r0,r18
	sbrc	r16,1
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,2
	 add	r0,r18
	sbrc	r16,2
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,3
	 add	r0,r18
	sbrc	r16,3
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,4
	 add	r0,r18
	sbrc	r16,4
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,5
	 add	r0,r18
	sbrc	r16,5
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,6
	 add	r0,r18
	sbrc	r16,6
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r16,7
	 add	r0,r18
	sbrc	r16,7
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r17,0
	 add	r0,r18
	sbrc	r17,0
	 adc	r1,r19
	asr	r1
	ror	r0

	sbrc	r17,1
	 add	r0,r18
	sbrc	r17,1
	 adc	r1,r19
	asr	r1
	ror	r0

	ret

// Ausgerollte vzl. Division
// R1:R0 = Dividend 10 bit
// R23:R22 = Divisor (Halbzeilenzahl) 10 bit; Divisor>Dividend
// R17:R16 = Quotient*1024
// R1:R0 = Rest
// Takte: max. 3+8*10+4 = 87, min. 3+6*10+4 = 67
div10:	clr	r16
	clr	r17
	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r17,1<<1
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r17,1<<0
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<7
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<6
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<5
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<4
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<3
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<2
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<1
1:	lsl	r0
	rol	r1
	cp	r0,r22
	cpc	r1,r23
	brcc	1f
	sub	r0,r22
	sbc	r1,r23
	sbr	r16,1<<0
1:	ret

pinchg:	in	r0,TIFR0	// Überlauf-Flag (= 9. Bit des 8-Bit-Zählers) fangen
	in	r16,TCNT0	// Capture Zählerwert im nächsten CPU-Takt
	mov	r17,r9		// High-Teil
	tst	r0		// Wenn Null hatte gerade Überlauf stattgefunden
	breq	1f		// … dann High-Teil erhöhen
	sbrc	r0,1		// Zählerüberlauf anhängig?
1:	 inc	r17		// High-Teil der Zeit inkrementieren
	sbic	PINB,3
	 rjmp	9f
// Vorderflanke
	sub	r16,r10
	sbc	r17,r11		// Differenz bilden
	add	r10,r16
	adc	r11,r17		// tic speichern
// Synchronisation der Rattermaschine
// Vorderflanken sind entweder Horizontalsynchronimpulse
// oder Trabanten mit doppelter Zeilenfrequenz.
// Da der Zähler mit vierfacher Zeilenfrequenz laufen soll,
// könnte OSCCAL direkt synchronisiert werden.
// Da jedoch der Horizontalausgang nicht springen sollte,
// (würde eine daran angeschlossene Horizontalendstufe killen)
// werden Trabanten — wie im Fernseher — ignoriert
// 1. FLL: Frequenzsynchronisation
	subi	r16,lo8(0x380)
	sbci	r17,hi8(0x380)	// Ergebnis sollte 0x0000 bis 0x00FF sein, optimal 0x80
	brne	1f		// Trabanten und Fehlimpulse aussieben
	cpi	r16,0x80-8
	brcs	1f
	cpi	r16,0x80+8
	brcc	1f
// 2. PLL: Phasensynchronisation bei kleiner Frequenzabweichung
	movw	r16,r10		// tic holen
	andi	r16,0x03
	sbrc	r16,1
	 ori	r16,0xFC	// Vorzeichenerweiterung
1:	subi	r16,0x80
	sbci	r17,0		// jetzt ±128 (PLL: ±512)
	asr	r17
	lsr	r16		// halbieren für dämpfende Wirkung, jetzt ±64 (PLL: ±256)
	add	r26,r16
	adc	r27,r17		// größerer CTC-Wert macht die ISRs langsamer
// auf Überläufe prüfen und OSCCAL verstellen
// Bei 15,625 kHz Zeilenfrequenz (PAL) = 64 µs
// ergibt sich eine Frequenz ≤ 16 MHz (statt 9,6 MHz).
// Bei 15,734.. kHz (NTSC) entsprechend ≤ 16,112 MHz
// Der Zähler startet schließlich bei Null bei jedem Zeilensynchronimpuls.
// Damit werden evtl. sichtbare Interferenzen der digitalen PWM-Ausgangsstufe
// auf das Fernsehbild wirksam unterdrückt.
// Denn die PWM-Trägerfrequenz ist mit 62,5 kHz vergleichsweise niedrig.
	in	r17,OSCCAL
	ldi	r16,hi8(MIN_CTC)	
	cpi	r26,lo8(MIN_CTC)
	cpc	r27,r16
	brcs	3f		// grob: mehr OSCCAL
	ldi	r16,hi8(MAX_CTC+1)
	cpi	r26,lo8(MAX_CTC+1)
	cpc	r27,r16
	brcc	4f		// grob: weniger OSCCAL
	reti
3:	subi	r27,-2		// in den gültigen Bereich hinauf
	inc	r17
	brmi	3f		// OSCCAL zu groß: Fehler!!
	out	OSCCAL,r17
3:	reti
4:	subi	r27,2		// in den gültigen Bereich hinab
	dec	r17
	cpi	r17,112		// OSCCAL zu klein: Fehler!!
	brcc	4f
	out	OSCCAL,r17
4:	reti
// Rückflanke
9:	sub	r16,r10
	sbc	r17,r11		// Impulsdauer
// Vertikalsynchronimpuls detektieren
// Ohne VSync nur wenige µs.
// Bei identischen Halbbildern (keine Trabanten) fast so lang wie eine Zeile.
// Bei versetzten Halbbildern eine halbe Zeile lang.
// Bei fehlenden Horizontalimpulsen „elend“ lang — da wird das Ende erfasst.
	breq	9f		// < 32 µs
	tst	r25
	breq	9f		// noch weniger als 256 Halbzeilen geschrieben
	ldi	r16,hi8(500)
	cpi	r24,lo8(500)	// Bei plausibler Zeilenzahl …
	cpc	r25,r16
	brcs	1f
vretr1:	movw	r22,r24		// … Zeilenzahl merken
1:	sbi	PORTB,2		// Booster aktiveren
	out	OCR0A,r1	// High ausgeben
	ldi	r25,0
	ldi	r24,0		// Start von oben
	out	ADMUX,r1	// Bildhöhen-Poti lesen lassen
9:	reti

main:	clr	r1		// SPL ist bereits initialisiert
	ldi	r22,lo8(MAXLINE)
	ldi	r23,hi8(MAXLINE)
	outi	OSCCAL,127	// auf > 16 MHz hochziehen (typ. laut Datenblatt)
	outi	MCUCR,0x20	// Sleep (Idle) aktivieren
	outi	PORTB,0x09	// Pullup am Sync-Eingang, Halbbrückenausgang 0V ohne Booster
	outi	DDRB, 0x26	// Ausgänge aktivieren
	outi	PCMSK,0x08	// Pegelwechsel-Interrupt
	outi	TCCR0B,0x01	// 62,5 kHz Trägerfrequenz
	outi	OCR0A,0xE0	// Timer-Behandlung „am Ende“
	outi	TIMSK0,0x08	// Interrupt alle 32 µs (Halb-Zeile)
	outi	ACSR,0x80	// Analogvergleicher totlegen
	outi	ADMUX,0x42	// Referenzspannung 1,1 V benutzen
//	outi	ADCSRB,0x04	// Trigger bei Timerüberlauf
	outi	ADCSRA,0xE6	// Synchrone A/D-Wandlung, Vorteiler 64: Zeit 52µs
	outi	DIDR0,0x37	// Analogeingänge ohne Digitalfunktion, Ausgänge nicht rücklesen
	ldi	r26,lo8(MAX_CTC)
	ldi	r27,hi8(MAX_CTC)	// Feinabgleich-Register für Timer im CTC-Modus
	ldi	r16,lo8(1000)
	ldi	r17,hi8(1000)
	movw	r14,r16
1:	ldi	r24,0		// ISRs sollen nur synchronisieren, keinen Vertikalrückschlag auslösen
	ldi	r25,0		// und nicht OCR0B berechnen / verändern
	sei
	sleep			// nur genau eine ISR kommt hier dran
	cli
	sub	r16,r24		// line==1 im Falle dass Timer0-Überlauf aufgerufen wurde
	sbc	r17,r25
	brne	1b		// ergibt Wartezeit von 64 ms,
		// das sollte den Koppelkondensator zur Vertikalablenkspule
		// hinreichend entladen haben, und der Strom durch den Messshunt ist Null
	in	r2,ADCL		// den entsprechenden A/D-Wert als Kalibrierwert einfangen
	in	r3,ADCH
	outi	OCR0B,0xFF	// „unten“ anfangen: Halbbrückenausgang 0V
	outi	TCCR0A,0xE3	// Schnelle PWM, OC0A invers, OC0B nichtinvers
	sbi	PORTB,1		// Zeilentransistor ansteuern
	sei
1:	sleep			// die Rattermaschine freilassen,
	rjmp	1b		// die ISRs verändern munter alle Register: egal!
Detected encoding: UTF-80