Source file: /~heha/mb-iwp/Bergwerk/fba-fw-230825.zip/isr.S

#define __SFR_OFFSET 0
#include "avr/io.h"

/* Wie sich avr-gcc bei einfachen ISRs anstellt kann ich nicht mit ansehen.
 Der Compiler ist instruiert, die vier Register R2..R5 freizuhalten.

 Zur Meldung von ISRs an die Hauptschleife („bottom half“) wird GPIOR0
 benutzt, in folgender Bitbelgegung:
	Bit 0	RxD: Lücke erkannt
	Bit 1	Timer1-ComparatorA (CTC oder Pulsausgabe)
	Bit 2	-
	Bit 3	sleep_cpu() in Effekt (wie PD4)
	Bit 4	Watchdog-Interrupt (derzeit ungenutzt)
	Bit 5	SR04: Input Capture aufgetreten (Wert gesichert + Interrupts gesperrt)
	Bit 6	I²C: Antwortpuffer leer oder EEPROM nicht bereit: Sende 0xFF
	Bit 7	I²C: Schreibpuffer voll
*/

#define SHOWLOAD sbi PORTD,4	// Load-Anzeige (DEBUG)
#define ee_debug (eedata+0x90)
#define l2_pwmok (l2+9)
#define savecap _ZN2us7savecapE
#define isrmax	(isrload+0)
#define isrcnt  (isrload+1)
#define isrtime (isrload+2)
#define sleepmax (sleepload+0)
#define sleepcnt (sleepload+1)
#define sleeptime (sleepload+2)
#define t1h (loadavg+18)

// Neu 230404 und noch nicht eingebaut: ISR-Zeitmessung und Interrupt-Zähler
// Mit ca. 3 µs Zeitverbrauch.
#define MEASURETIME 0

sleepEnd:	// von sleeptime wurde TCNT1 bereits beim Aufruf von sleep_cpu abgezogen
	cbi	GPIOR0,3
	lds	r2,sleeptime
	add	r2,ZL
	sts	sleeptime,r2
	lds	r2,sleeptime+1
	adc	r2,ZH
	sts	sleeptime+1,r2
	ret

.macro ISR label		// ISR-Latenz: 4 oder 8 Takte, Sprung 3 Takte
.global \label
\label:
	SHOWLOAD
	in	r3,SREG
	movw	r4,ZL		// 3
#if MEASURETIME
	lds	ZL,TCNT1L
	lds	ZH,TCNT1H
	sbic	GPIOR0,3	// Schlafmodus?
	 rcall	sleepEnd
1:	push	ZH
	push	ZL		// Mehraufwand 13 (nach sleep_cpu() mehr) Takte
#endif
.endm

isrend:	// R2, ZH:ZL sowie SREG verfügbar, bei MEASURETIME auf dem Stack die Startzeit
#if MEASURETIME
	lds	ZL,TCNT1L
	lds	ZH,TCNT1H
	pop	r2
	sub	ZL,r2		// Richtig nur wenn Timer0 nicht als CTC läuft! Daher CTC nur während Ultraschall-Impulsausgabe
	pop	r2
	sbc	ZH,r2
	lds	r2,isrtime
	add	r2,ZL
	sts	isrtime,r2	// 9
	lds	r2,isrtime+1
	add	r2,ZH
	sts	isrtime+1,r2
# if MEASURETIME == 2
	tst	ZH
	breq	1f
	ldi	ZL,0xFF
1:	lds	r2,isrmax
	cp	r2,ZL
	brcc	1f
	sts	isrmax,ZL
1:	// Mehraufwand 28..31 Takte
# endif
#endif
	movw	ZL,r4
	lds	r2,isrcnt
	inc	r2
	breq	1f		// nicht überlaufen lassen
	sts	isrcnt,r2
1:	out	SREG,r3		// 2
	reti			// 4
				// Summe 20 Takte, 3 µs (ohne MEASURETIME)

/***************************
 * Gruppe 1: PWM-Generator *
 ***************************/

/* Impulsdiagramm zum 4017
Zählerstand 4017
0	│1	│2	│3	│4	│5	│6	│7	│8	│9	│0
PD7 = Reset 4017
──────┐										 ┌──────
      └──────────────────────────────────────────────────────────────────────────┘
PB1 = ↑Takt 4017
─────┐	┌─┐	┌─┐	┌─┐	┌─┐	┌─┐	┌─┐	┌─┐	┌─┐	┌─┐	┌───────
     └──┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
     ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑ ↑	↑
pwmidx
       0 1 2	 3 4	 5 6	 7 8	 9 10	11 12	13 14	15 16	17 18	19
Die Low-Phasen sind variabel: 1..2 ms bei den Zählerständen 1..9,
Gesamtzykluszeit 20 ms, das ergibt 2..11 ms für den Zählerstand 0, im Mittel 6,5 ms.
Alle Flanken an PB1 sind jitterfrei hardware-generiert, in einer Auflösung
von reichlich 1 µs.
*/
#define T1CTC	 (F_CPU/8/50)			// 18432 = 2^11 × 3^2
#define OCR_1msX (F_CPU/8*1025/1000000)		// 944
#define OCR_1ms5 (F_CPU/8*1501/1000000)		// 1500 µs => 1383
#define HILEN	 (F_CPU/8*501/1000000)		// 500 µs
#define SAFEDIST 4				// ca. 5 µs, 24..32 CPU-Befehle

.section .noinit,"a",@nobits
pwmidx:	.ds	1	// PWM-Index: 0..19: 20 Flanken für den 4017
t1fb:	.ds	2	// Timer1-Frame-Basis: 20 ms pro Rahmen generiereren
.text

// Aufrufrate: ≈ 1 kHz, max. ≈ 130 Takte ≈ 17 µs
// Das Setzen von OCR1A in der „Bottom Half“ in der Hauptschleife
// führte zu gelegentlichen desaströsen PWM-Ausfällen!
// Denn durch einen inzwischen behobenen Fehler beim Inline-Assembler
// dauerte der Schleifendurchlauf gelegentlich mehr als 1 ms,
// und OCR1A wurde in die Vergangenheit gesetzt.
// Da die saubere PWM-Generierung höchste Priorität hat,
// ist diese nun komplett als ISR mit gesperrten Interrupts realisiert.
// Auch der Not-Aus-Eingang wird hier abgefragt.
.global TIMER1_COMPA_vect
TIMER1_COMPA_vect:
	SHOWLOAD
	in	r3,SREG
	mov	r2,r24
	movw	r4,ZL
	lds	ZL,OCR1AL
	lds	ZH,OCR1AH
// Innere Notschleife mit Z = vorhergehendem OCR1A-Wert
0:	sbis	PORTD,7
	 rjmp	3f
	// CTC-Fall: fallende Flanke ausgegeben
	sbi	GPIOR0,1	// Signal an Hauptschleife
	rjmp	2f		// zum normalen Ablauf
.global _ZN2L26t1initEv		// `L2::t1init()`
_ZN2L26t1initEv:
	lds	r24,TCNT1L
	lds	r25,TCNT1H
	sts	t1fb+1,r25
	sts	t1fb,r24
	cli
	in	r3,SREG
2:	ldi	r24,0x80
	sbic	PINB,1		// (sollte bereits Low sein)
	 sts	TCCR1C,r24	// PB1 auf Low toggeln
	cbi	PORTD,7		// 4017 vom Reset erlösen, Trigger für Oszi
	lds	r24,l2_pwmok	// `l2.pwmok`
	tst	r24
	breq	6f		// Null: Keine PWM ausgeben
	ldi	r24,0
	sts	pwmidx,r24	// Zähler für mikrosekundengenaue Flanken = 0
	rjmp	4f
3:	// PWM-Fall: Steigende oder fallende Flanke ausgegeben
	lds	r24,pwmidx
	inc	r24
	cpi	r24,19
	brcc	6f		// Ende erreicht: CTC simulieren
	sts	pwmidx,r24
	lsr	r24
	 brcc	5f		// [10]
4:	// Nach CTC oder steigender Flanke Puls konstanter Länge ausgeben
	subi	ZL,lo8(-HILEN)
	sbci	ZH,hi8(-HILEN)
	rjmp	7f
5:	// Nach fallender Flanke berechnete Pulslänge ausgeben
	push	ZH	// Altes (ggf. Rechen-)OCR retten
	push	ZL
	 mov	ZL,r24	// Adresse in l2.pwms ermitteln, pwms = 1. Strukturmember(!)
	 clr	ZH	// (r24 = 1..9)
	 subi	ZL,lo8(-(l2-1))
	 sbci	ZH,hi8(-(l2-1))	// für Nullbasierung 1 abziehen
	 ld	r24,Z	// PWM-Wert holen (darf -128..+127 sein)
	 subi	r24,0x80	// byte Zeitfaktor (regulär 3..253) vorzeichenlos
	 push	r1
	 push	r0
	  ldi	ZH,lo8(OCR_1msX)
	  mul	r24,ZH
	  mov	ZL,r1		// der Low-Teil (r0) interessiert nicht
	  ldi	ZH,hi8(OCR_1msX)
	  mul	r24,ZH
	  clr	ZH
	  add	ZL,r0
	  adc	ZH,r1		// Produkt 8×16 fertig, High-Teil wird verwendet
	 pop	r0
	 pop	r1
	 subi	ZL,lo8(-(OCR_1ms5-OCR_1msX*125/256-HILEN))
	 sbci	ZH,hi8(-(OCR_1ms5-OCR_1msX*125/256-HILEN))
52:	pop	r24		// Pulsdauer zum alten OCR1A-Wert addieren
	add	ZL,r24
	pop	r24
	adc	ZH,r24
	rjmp	7f		// [38]
6:	// Nach letzter steigender Flanke CTC-Fall vorbereiten
	sbi	PORTD,7		// 4017 in Reset halten
	lds	ZL,t1fb		// Altes OCR1A ignorieren und auf Frame-Basis beziehen
	lds	ZH,t1fb+1
	subi	ZL,lo8(-T1CTC)	// Frame-Länge (20 ms) addieren
	sbci	ZH,hi8(-T1CTC)
	sts	t1fb+1,ZH	// Nächste Frame-Basis
	sts	t1fb,ZL
	
7:	// Zur Sicherheit Zählerstand einlesen und achten,
	// dass OCR hinreichend in der Zukunft liegt
	// if (TCNT1+SAFEDIST < Z) okay => if (TCNT1-Z-(-SAFEDIST) <0)
	lds	r24,TCNT1L
	sub	r24,ZL
	subi	r24,lo8(-SAFEDIST)
	lds	r24,TCNT1H
	sbc	r24,ZH
	sbci	r24,hi8(-SAFEDIST)
	brmi	8f		// okay
	ldi	r24,0x80
	sts	TCCR1C,r24	// „Falschen“ Compare-Impuls sofort auslösen
	lds	r24,ee_debug+9	// Zeitproblem: Zu setzendes OCR1A zu nah an oder hinter TCNT1
	inc	r24		// Ursache: Andere ISR sperrt Interrupts zu lange
	sts	ee_debug+9,r24	// eedata.debug[9] protokollieren
	lds	r24,pwmidx
	sts	ee_debug+10,r24	// eedata.debug[10]
	rjmp	0b		// in ISR bleiben
	
8:	sts	OCR1AH,ZH
	sts	OCR1AL,ZL
9:	movw	ZL,r4
	mov	r24,r2
	out	SREG,r3
	reti			// [21]
// Maximale Ausführungszeit (regulär): 6+11+10+38+21 = 86 Takte, ca. 12 µs
// Bei „Vorfall“ eher das Doppelte.
	
// Aufrufrate: 14 Hz, 71 ms
// „Verlängert“ Timer1 auf 32 Bit, Überlauf dann nach über 1 h (erst mal nur Gimmick)
.global TIMER1_OVF_vect
TIMER1_OVF_vect:
	SHOWLOAD
	in	r3,SREG
	lds	r2,t1h
	inc	r2		// High-Byte Timer1: Überlauf nach 18,2 s
	sts	t1h,r2
	brne	9f
	lds	r2,t1h+1
	inc	r2
	sts	t1h+1,r2
9:	out	SREG,r3
	reti

/******************
 * Gruppe 2: UART *
 ******************/

.global TIMER2_COMPA_vect
TIMER2_COMPA_vect:
// Intercharacter-Timeout: Index vermerken, Freigabebit setzen
// PROBLEM: Dieser Interruptvektor ist höher priorisiert als USART_RX_vect.
// Deshalb bei gleichzeitigem Interrupt den USART_RX-Interrupthandler anspringen.
	SHOWLOAD
	lds	r2,UCSR0A
	sbrc	r2,7
	 rjmp	USART_RX_vect	// RxC0 „drückt auf die Klingel“? Dort weiter!
	lds	r2,rxWrIdx
	sts	rxItIdx,r2	// Lücke vermerken
	sbi	GPIOR0,0	// (Mehrfachauslösungen bei Ruhe auf dem seriellen Port stören nicht)
	reti

// Aufrufrate 115200 Baud ÷ 11 ≈ 10 kHz (es kommen 2 Stoppbits)
// Byte-Lücken (Inter-Character-Timeouts) werden via OCR2A detektiert.
// Wenn Puffer voll -> eedata.debug[6]++
	ISR USART_RX_vect	// Byte in Empfangs-FIFO abholbereit
	lds	r2,UDR0		// Byte von serieller Schnittstelle
	lds	ZL,rxWrIdx
	ldi	ZH,0		// Index auf 16 Bit vzl. erweitern
	subi	ZL,lo8(-(rxRingBuf))// Den Trick mit den inneren Klammern muss man kennen!!
	sbci	ZH,hi8(-(rxRingBuf))// Sonst hagelt es Fehlermeldungen.
	st	Z+,r2		// etwa: rcBuf[rxIdx++]=UDR0
	sbis	GPIOR0,0
	 rjmp	2f		// Solange kein (erstes) Timeout eintrat, Byte ignorieren
	subi	ZL,lo8(rxRingBuf)
	cpi	ZL,40		// 40 == sizeof rxRingBuf
	brcs	0f
	subi	ZL,40
// Prüfen auf Pufferüberlauf: Wenn ja Schreibindex nicht vorrücken.
// Geschriebenes Byte ist dann flüchtig.
0:	lds	r2,rxRdIdx
	cp	ZL,r2
	brne	1f		// ungleich: kein Überlauf
	lds	r2,ee_debug+6
	inc	r2		// Überlauf melden
	sts	ee_debug+6,r2
	rjmp	2f
1:	sts	rxWrIdx,ZL
// Intercharacter-Timeout feststellen lassen: Zeitgeber starten
2:	lds	ZH,TCNT2	// Momentaner Zählerstand
	subi	ZH,-(F_CPU/1024/1000)	// Timeout = 1 ms in der Zukunft (-7)
	sts	OCR2A,ZH	// Bei jenem Zählerstand Interruptflag setzen
	sbi	TIFR2,1		// OCF2A löschen
	rjmp	isrend

// Aufrufrate 115200 Baud ÷ 10 = 11 kHz
// Zeichen aus Antwortpuffer ausgeben oder, wenn ausgelesen,
// auf Interrupt-bei-Sendeende umschalten
	ISR USART_UDRE_vect	// Sendehalteregister frei zum Nachfüttern
	lds	ZL,txIdx
	lds	ZH,txBuf	// Die Länge der Sendedaten steht da drin (!=0)
	cpse	ZH,ZL
	 rjmp	1f
	ldi	ZH,0b01001000;	// Auf „Interrupt bei Sende-Ende“ wechseln
	sts	UCSR0B,ZH
	ldi	ZL,0
	rjmp	2f
1:
	ldi	ZH,0
	subi	ZL,lo8(-(txBuf))
	sbci	ZH,hi8(-(txBuf))
	ld	r2,Z+
	sts	UDR0,r2		// Hunger stillen
	subi	ZL,lo8(txBuf)
2:	sts	txIdx,ZL
	rjmp	isrend
	
// Aufrufrate ca. 2 kHz
// Sender abschalten (PD1 wird Eingang) und Empfänger reaktivieren
// Keine gesonderte Info an Hauptschleife
.global USART_TX_vect
USART_TX_vect:			// Sendeschieberegister leer (PD.1 = hi)
	SHOWLOAD
	mov	r2,r24		// (Flags werden nicht geändert)
	 in	r3,SREG
	 clr	r24		// DEBUG: ISR-Marker
1:	 dec	r24
	 brne	1b
	 out	SREG,r3
	 ldi	r24,0b10010000	// Sender abschalten, Empfänger aktivieren
	 sts	UCSR0B,r24	// (Flush des Empfängers unnötig)
	mov	r24,r2
	reti

/***************************************************************
 * Gruppe 3: I²C-Slave (Raspberry) und SPI-Master (Grapefruit) *
 ***************************************************************/
.global WDT_vect
WDT_vect:
	SHOWLOAD
	sbi	GPIOR0,4
	reti
 
.global _Z9twiOnReadv
_Z9twiOnReadv:	// Byte ausgeben (SFR, RAM, EEPROM, Flash), VR: R24, Z, Flags
	lds	r24,twiLen
	tst	r24
	breq	6f		// 0xFF liefern
	lds	ZL,twiPtr
	lds	ZH,twiPtr+1
	cpi	r24,0xFF
	breq	1f		// Länge nicht dekrementieren
	dec	r24
	sts	twiLen,r24
1:	sbrc	ZH,7
	 rjmp	2f		// Flash lesen
	sbrs	ZH,5		// EEPROM? (Xmega-kompatibel)
	 rjmp	3f		// RAM lesen
	sbic	EECR,1
	 rjmp	6f		// EEPROM beschäftigt: 0xFF liefern
#ifdef EEARH
	out	EEARH,ZH
#endif
	out	EEARL,ZL
	sbi	EECR,0
	adiw	ZL,1
	in	r24,EEDR	// EEPROM lesen
	rjmp	4f
2:	lpm	r24,Z+
	rjmp	4f
3:	ld	r24,Z+
4:	sts	TWDR,r24
5:	sts	twiPtr+1,ZH
	sts	twiPtr,ZL
	rjmp	8f		// 12 (weitere) Takte bis zum Schreiben von TWCR: 1,6 µs
6:	sbi	GPIOR0,6
	sts	ee_debug+29,r24	// debug[29]
	ldi	r24,0xFF
	sts	TWDR,r24
7:	ldi	r24,0x85	// I²C vom Clock-Stretch erlösen mit NAK
	rjmp	9f
8:	lds	r24,twiLen
	tst	r24
	ldi	r24,0xC5	// I²C vom Clock-Stretch erlösen mit ACK
	brne	9f
88:	ldi	r24,0x85	// … mit NAK beim letzten Byte
9:	sts	TWCR,r24
	ret
// PROBLEM 230217: Beim Lenkwinkel zeigen sich gelegentliche -13° bei sonst 0,7°.
// I²C oder ADC?
// Temperaturanzeige falsch, beim Digitalwert ähnliche Sprünge.
// Seltsam: debug[27] mit 62 überschrieben!

_Z10twiOnWritev:// Byte einlesen (nur in Empfangspuffer), VR: R24, Z, Flags
	lds	r24,twiLen
	tst	r24
	brne	1f		// Okay; NAK beim letzten Byte
	sbi	GPIOR0,7	// Fehler: Längenüberschreitung, mehr als 66 Byte
	lds	r24,TWDR	// Lesen und verwerfen
	sts	ee_debug+31,r24	// debug[31]
	rjmp	88b		// NAK als Anwort
1:	dec	r24
	sts	twiLen,r24
	lds	ZL,twiPtr
	lds	ZH,twiPtr+1
	lds	r24,TWDR
	st	Z+,r24		// Nur SFR, RAM (nicht EEPROM oder Flash) schreiben
// Hier optional:
// if (twiPtr==twiBuf+2 && !(twiBuf[0]&0xE0)) {
//  twiPtr=twiBuf[0]<<8|twiBuf[1];	// auf RAM/SFR schreiben umbiegen
//  twiLen=0xFF;	// unendlich erlaubt
// }
// Erst mal nicht, auch sicherheitshalber!
	rjmp	5b		// Geänderten Pointer schreiben und ACK (NAK beim letzten Byte)

// Aufrufrate ca. 44 kHz! Mit Software-PWM nur 200 kHz I²C-Takt, Rate 22 kHz.
// Nur „Datenbyte empfangen“ und „Datenbyte senden“.
// Alles andere muss die Hauptschleife machen,
// mit dem Clock-Stretch-Feature (was der Raspberry auswerten MUSS)
// hat die Hauptschleife genügend Zeit, den Puffer zu füllen
// bzw. die Daten aus dem Puffer zu übernehmen
// Weil dann der Interrupt bestehen bleibt
// muss I²C-Interrupt-Enable gelöscht werden.
	ISR TWI_vect
	mov	r2,r24
	lds	r24,TWSR
	cpi	r24,0x80	// Byte empfangen, vorher ACK
	breq	1f
	cpi	r24,0x88	// Byte empfangen, vorher NAK
	breq	1f
	cpi	r24,0xB8	// Byte gesendet, ACK empfangen
	breq	3f
//TEST: Irrtümliches STOP beim Lesen?
//	cpi	r24,0xC0	// Byte gesendet, NAK empfangen (auf langer Leitung?)
//	breq	3f		// So tun als wäre das NAK irrtümlich: weitersenden (hilft nicht)
//	cpi	r24,0xC8
//	breq	3f
//TEST ENDE
	cpi	r24,0x60	// Schreibadresse empfangen: Puffer vorbereiten
	breq	6f
	cpi	r24,0xA0	// STOP nach Schreiben: An I2C::poll() delegieren!
	breq	2f		// (Muss eingegangene Daten beackern)
	cpi	r24,0xA8	// Leseadresse empfangen: An I2C::poll() delegieren!
	breq	2f		// (Muss Puffer mit Daten sowie Zeiger bereitstellen)
	ldi	r24,0xC5	// Bei allen anderen Kodes Interruptflag löschen, ACK, nicht die Hauptschleife belasten
	rjmp	4f
2:	lds	r24,TWCR
	andi	r24,0x7E	// Clock-Stretch behalten und Interrupts sperren
4:	sts	TWCR,r24
	rjmp	9f		// raus: I2C::poll() kümmert sich (bei gesperrtem Interrupt)
1:	// Byte entgegennehmen (nur in Empfangspuffer)
	rcall	_Z10twiOnWritev
	rjmp	9f
6:	// Empfangspuffer (66 Byte = 2 Byte Header + 64 Byte Nutzdaten) präparieren
	ldi	ZL,lo8(twiBuf)
	ldi	ZH,hi8(twiBuf)
	ldi	r24,0x4F
	st	Z,r24		// twiBuf.lvl = 0x4F (ungültig, muss überschrieben werden)
	ldi	r24,0
	std	Z+1,r24		// twiBuf.ofs = 0
	ldi	r24,66
	sts	twiLen,r24	// twiLen = sizeof twiBuf
	call	5b		// twiPtr=&twiBuf.lvl; ACK
	rjmp	9f
3:	// Byte ausgeben (SFR, RAM, EEPROM, Flash)
	rcall	_Z9twiOnReadv
9:	mov	r24,r2
	rjmp	isrend

// Aufrufrate ca. 10 kHz?
// Nur „Byte empfangen“ und „Byte senden“
	ISR SPI_STC_vect
	mov	r2,r24
	lds	ZL,spiIdx	// Index im Sende/Empfangspuffer
	lds	ZH,spiLen	// Maximalanzahl erwarteter Bytes
	cp	ZL,ZH
	brcc	7f		// Problem bei ZL≥ZH, Index ≥ Maximalzahl
	clr	ZH
	subi	ZL,lo8(-(spiBuf))
	sbci	ZH,hi8(-(spiBuf))
	push	r25
	 ld	r25,Z
	 in	r24,SPDR
	 out	SPDR,r25
	 st	Z+,r24
	pop	r25
	subi	ZL,lo8(spiBuf)
	sts	spiIdx,ZL
	rjmp	9b
7:	in	r24,SPCR
	cbr	r24,1<<7
	out	SPCR,r24	// Interrupt sperren, weiter in Hauptschleife
	rjmp	9b

/********************************
 * Gruppe 4: Tacho, Meterzähler *
 ********************************/
// Die Filterung der Hochfrequenzanteile übernimmt die Hardware
// aus 74LVC2G14DCK (2 Schmitt-Trigger-Inverter) und RCD-Beschaltung.
// Aufrufrate max. ≈ 400 Hz, Ausführungszeit 24 Takte ≈ 3,3 µs
// Da die Interruptpins (INT0, INT1) maximal hoch priorisiert sind,
// kommt bei Gedränge am Interruptcontroller diese ISR mit maximaler Latenz
// der längsten ISR (hier: Timer1_CompA: 130 Takte, 17 µs) zum Zug.
// Das ergibt einen Capture-„Fehler“ gegenüber Hardware-Capture von 16 Timer1-Ticks.
// Macht bei 20 ms Messrate (18432 Timer1-Ticks) einen Fehler von < 1 ‰,
// im Unglücksfall (2 einzelne Pulse in 2 verschiedenen 50-Hz-Frames, sonst nichts)
// deutlich mehr, sollte aber eh' nicht vorkommen und ist allenfalls ein Randproblem.
// Der Timer1-Hardware-Capture ist für den Ultraschallsensor reserviert.
.global INT1_vect
INT1_vect:
	SHOWLOAD
	lds	r2,TCNT1L	// High-Teil des Zählerstandes im TEMP-Register
	sts	i1ts,r2
	lds	r2,TCNT1H	// Liest das TEMP-Register
	sts	i1ts+1,r2
	in	r3,SREG
	in	r2,GPIOR2
	inc	r2		// Software-Zähler für Geschwindigkeit und Tacho
	out	GPIOR2,r2
	out	SREG,r3
	reti

/*************************
 * Gruppe 5: A/D-Wandler *
 *************************/
// Aufrufrate: 8,8 kHz, Ausführungszeit ≈ 64 Takte ≈ 9 µs.
// Das Umladen des Kondensators an AREF dauert viel zu lange,
// daher Temperaturmessung mit 3,3 V Referenz, sollte genau genug sein.
// Es wird ADC0..ADC3 und ADC8 abgetastet und jeweils 32× summiert.
// Danach wird der A/D-Wandler abgeschaltet.
// Siliziumfehler? Das Abschalten von Auto-Trigger (ADATE)
// beim _vorletzten_ Sample funktioniert NICHT erwartungsgemäß!
// Daher beim letzten Sample A/D-Wandler abwürgen.
	ISR ADC_vect
// A/D-Wert aufsummieren
// adcChan = Kanalnummer 0..4
// VR: Z (adcAccu+(ZL+1)*2) wenn ZH>=0
	lds	ZL,adcCollect+0
	lsl	ZL		// ×2
	clr	ZH
	subi	ZL,lo8(-(adcCollect+2))
	sbci	ZH,hi8(-(adcCollect+2))
	push	r0
	 lds	r0,ADCL
	 ld	r2,Z
	 add	r0,r2
	 st	Z+,r0
	 lds	r0,ADCH
	 ld	r2,Z
	 adc	r0,r2
	 st	Z+,r0
	pop	r0
// Kanal-Weiterschaltung (0..4)
	lds	ZL,adcCollect+0	// nochmal laden
	inc	ZL		// Nächster Kanal
	cpi	ZL,5
	brne	1f
	clr	ZL
// Sample-Weiterschaltung (0..31; 32 = Fertigmeldung)
	lds	ZH,adcCollect+1
	inc	ZH		// Nächste Samplenummer
	sbrc	ZH,5		// vollständig?
	 sts	ADCSRA,ZL	// ADC abwürgen
3:	sts	adcCollect+1,ZH
1:	sts	adcCollect+0,ZL
// Multiplexer-Weiterschaltung (1 voreilend)
	lds	ZL,ADMUX
	inc	ZL		// 0->1, 1->2, 2->3, 3->4, 8->9
	sbrc	ZL,3
	 subi	ZL,9		// 9->0
	sbrc	ZL,2
	 subi	ZL,-4		// 4->8
	sts	ADMUX,ZL	// Für nächste A/D-Umsetzung einstellen
9:	rjmp	isrend

// EEPROM-Interrupt: Tut sich nur abschalten, zum CPU aufwecken
.global EE_READY_vect
EE_READY_vect:
	SHOWLOAD
	cbi	EECR,3		// Interruptfreigabe löschen
	reti

/*******************************
 * Gruppe 6: Ultraschallsender *
 *******************************/
// Aufrufrate: 48 kHz, alle 154 CPU-Takte
.global TIMER0_COMPA_vect
TIMER0_COMPA_vect:
	SHOWLOAD
	in	r3,SREG
	in	r2,GPIOR1
	dec	r2
	out	GPIOR1,r2
	brne	9f
	out	TCCR0B,r2	// Timer0 anhalten, keine weiteren Interrupts
	sbi	TIFR1,5	// ICF löschen (compiliert zu SBI und funktioniert richtig)
	cbi	DDRD,6	// Hochohmig: Komparatorschwelle hochlaufen lassen
	mov	r2,r24
	ldi	r24,0b00100011
	sts	TIMSK1,r24	// Capture-Interrupt freigeben
	mov	r24,r2
9:	out	SREG,r3
	reti

.global TIMER1_CAPT_vect
TIMER1_CAPT_vect:
	SHOWLOAD
	lds	r2,ICR1L
	sts	savecap,r2
	lds	r2,ICR1H
	sts	savecap+1,r2
	sbi	GPIOR0,5
	sbi	DDRB,0		// Am Oszilloskop als Dauer-Low anzeigen
	mov	r2,r24
	ldi	r24,0b00000011	// ICIE1 aus (OC1B bleibt ohne Interrupt)
	sts	TIMSK1,r24
	mov	r24,r2
	reti

/*************************
 * Statt Inline-Assembly *
 *************************/

.global _ZN3UhrppEv	// `Uhr::operator++()`
_ZN3UhrppEv:		// R24=this
	movw	ZL,r24
	ld	r24,Z
	subi	r24,-2		// 20 ms
	ldi	r25,-100
	add	r25,r24		// r24≥100 ? CF=1
	brcc	0f
	mov	r24,r25		// Modulo 100 zählen (nicht BCD)
0:	st	Z+,r24
	ldi	r24,5		// Anzahl Folgebytes (Ganze Sekunden)
1:	brcc	2f		// vorzeitig raus wenn CF == 0
	ld	r0,Z
	adc	r0,r1		// Jedes Byte mit Übertrag addieren
	st	Z+,r0
	dec	r24
	brne	1b
2:	ret

.global _ZN7OneWire4crc8EPKhh	// `OneWire::crc8(const byte*,byte)`
_ZN7OneWire4crc8EPKhh:		// R25:R24 = Datenzeiger, R22 = Länge (0 = 256!)
	movw	ZL,r24
	ldi	r24,0		// Startwert
	ldi	r23,0x8C	// Xor-Wert
0:	ld	r0,Z+
	eor	r24,r0
	lsr	r24		// Ausgerollte Schleife: 24 Takte
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	lsr	r24
	brcc	2f
	eor	r24,r23
2:	dec	r22
	brne	0b
	ret
// Ein Jammer dass man dem Compiler nicht verklickern kann
// welche Register versaut werden und welche nicht.
Detected encoding: UTF-80