#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-8 | 0
|