#define __SFR_OFFSET 0
#include "avr/io.h"
.global TIMER0_COMPB_vect,makeAverage,phi,Cplx24AbsLog,addphasedreh
// Für die Signalverarbeitung
// Prinzip Hoffnung: Keine Aussteuerung die zum Überlauf führt, d.h. 16-Bit-Akku reicht
TIMER0_COMPB_vect:
in r3,SREG
in r2,ADCL
sbic GPIOR2,1
rjmp 2f
sbic GPIOR2,0
rjmp 1f
add r4,r2 // I += ADC
in r2,ADCH
adc r5,r2
rjmp 4f
1: add r6,r2 // Q += ADC
in r2,ADCH
adc r7,r2
rjmp 4f
2: sbic GPIOR2,0
rjmp 3f
sub r4,r2 // I -= ADC
in r2,ADCH
sbc r5,r2
rjmp 4f
3: sub r6,r2 // Q -= ADC
in r2,ADCH
sbc r7,r2
4: in r2,OCR0B // Int24Rec z = {r8,r9,OCR0B}
add r8,r12 // z += {r12,r13,r14}
adc r9,r13
adc r2,r14
out OCR0B,r2 // {r8,r9,OCR0B} = z — C++ hat kein destrukturierendes Assignment
in r2,GPIOR2
inc r2
5: out GPIOR2,r2
out SREG,r3
cpse r2,r15 // 22 Takte
reti // dazu Aufruf + rjmp + reti + 1 = 33 Takte
sts ISR_Buf+0,r4 // Nötig: 62 kHz = 16 µs
sts ISR_Buf+1,r5 // Bei der absolut tiefstmöglichen Quarzfrequenz von 1,8432 MHz
sts ISR_Buf+2,r6 // sind 29 Takte pro Interrupt da; das geht _nur_ mit Tail-Chaining.
sts ISR_Buf+3,r7 // Der nächstbeste Baudratenquarz mit 3,6864 bietet 59 Takte pro Interrupt.
clr r4 // Das reicht bereits.
clr r5 // Komfortabler sind glatte 4 MHz mit 64 Takten pro Interrupt.
clr r6
clr r7
sbi GPIOR0,0 // Alle 4 ms: Idle-Routine kümmert sich um die Weiterverarbeitung
clr r2
rjmp 5b
// lade R23:21 von Z
24: ldd r21,Z+0
ldd r22,Z+1
ldd r23,Z+2
ret
makeAverage:
// PE: X = const int*buf
// Z = int24*avg
// VR: W20, L22, X vorgerückt, Z vorgerückt
ld r24,X+
ld r25,X+
mov r0,r25
lsl r0
sbc r0,r0 // Vorzeichen auf 24 Bit erweitern
rcall 24b
ldi r20,4 // T = 16 als erprobte Zeitkonstante
2: asr r23 // schiebe R23:21 um R20 nach rechts, vzb.
ror r22
ror r21
dec r20
brne 2b
sub r24,r21
sbc r25,r22
sbc r0,r23
rcall 24b // Der Trick: 2× laden spart Register
add r21,r24
adc r22,r25
adc r23,r0
st Z+,r21
st Z+,r22
st Z+,r23
ret
phi:
// PE: Z = const int24 avg[2]
// PA: R24 = Phase 0, 0x40, 0xC0, 0x80 je nach Vorzeichen der beiden Mittelwerte
clr r24
clr r25
ldd r0,Z+2
sbrc r0,7
ldi r24,0x40
ldd r0,Z+5
sbrc r0,7
ldi r25,0xC0
eor r24,r25
ret
// 24-Bit-Wert laden und Betrag bilden
23: rcall 24b
sbrs r23,7
ret
com r23
com r22
neg r21
sbci r22,-1
sbci r23,-1
ret
// Quadrieren / Multiplizieren vzl. mit konstanter Rundenzahl, 16×16 => 32 Bit
// (Die avr-gcc-laufzeitoptimierte Version nützt hier nichts da die Faktoren eher groß sind!)
// PE: R25:24 = W = 16-Bit-Zahl
// PA: R23:20 = L20 = Quadrat der Zahl
// VR: R1=0
usqr: movw r20,r24
// PE: R21:R20 = W20 = zweiter Faktor
umul: clr r22
clr r23
0: sbrc r20,0
add r22,r24
sbrc r20,0
adc r23,r25
ror r23
ror r22
ror r21
ror r20
inc r1
sbrc r1,4
rjmp 0b
clr r1
ret
// Binärer Logarithmus
// PE: L20 = 32-Bit-Zahl vzl.
// PA: W = Binärlogarithmus Q8.8, maximal „31.99“, negativ für Argument=0
// VR: Z
// Benötigt externe (von C++ zur Compilezeit generierte) Logarithmentabelle
lb8: ldi r25,31
1: tst r23
brmi 3f
2: dec r25
brmi 9f // Notbremse für Argument 0: Liefert negativen Wert
lsl r20
rol r21
rol r22
rol r23
brpl 2b
// An dieser Stelle ist das Bit 7 von R23 stets gesetzt, und R25 enthält den ganzzahligen Binärlogarithmus
3: lsr r23 // 6-Bit-Rest für Tabellenzugriff zurechtrücken
andi r23,63
mov r30,r23
clr ZH
subi r30,lo8(-(logtab))
sbci r31,hi8(-(logtab))
lpm r24,Z // Den Nachkommateil laden
9: ret
Cplx24AbsLog:
// Logarithmus des Betrags einer komplexen Zahl = log. Amplitude eines I/Q-Signals
// PE: Z = const-Zeiger auf komplexe Zahl: 2× 24 Bit vzb.
// PA: W24 = Zweierlogarithmus Q8.8, 1=> 0.0, 2=> 1.0, 4=> 2.0 usw. max. <31.5
// VR: Nach avr-gcc-Konvention alle: R20..R27, R30..R31. W18 wird NICHT eingesaut!
rcall 23b // I laden und Betrag => R22:R20
movw r26,r22
mov r25,r21 // => L25
adiw ZL,3
rcall 23b // Q laden und Betrag => L21
// ab hier Z frei für Arithmetik
ldi r20,9 // Mehr als 9 0-Bits einschieben ist Quatsch da Multiplikation 16-bittrig
rjmp 3f
// Einzelbitweise linksschieben bis eine der beiden Zahlen >=0x400000 ist
2: lsl r21 // Alle 24 Bit
rol r22
rol r23 // Q<<=1
lsl r25
rol r26
rol r27 // I<<=1
dec r20 // Schiebeoperationen zählen bis -1
brmi 5f // Schluss bei -1, um den Rest kümmert sich der Logarithmus
3: mov r24,r27
or r24,r23
andi r24,0xC0
breq 2b
5: mov 0,r20
// Ab hier |Q| in W22 und |I| in W26, jeweils 16 Bit. R20 ist hier maximal 9.
movw r24,r22 // |Q| positionieren
rcall usqr // Q² in L20, max. 0x3FFF'0001
movw r24,r26 // |I| positionieren
movw XL,r20
movw ZL,r22 // Q² in Z:X
rcall usqr // I² in L20, max. 0x3FFF'0001
add r20,XL
adc r21,XH
adc r22,ZL
adc r23,ZH // I² + Q², max. 0x7FFE'0002
rcall lb8 // Logarithmus daraus machen => W24, max. <32.0
asr r25
ror r24 // halbieren entspricht Wurzel ziehen, max. <16.5
add r25,r0 // Skalierung hinzufügen, max. <31.5
ret
// „Oben“ sind noch 3 Bit frei, wenn man das Ergebnis „unsigned“ interpretiert, sonst 2.
// Diese Lösung mit 16-MHz-Quarz frisst mit sleep_cpu() und ohne Verwendung von Timer1
// nur noch 6 mA. Das ist für viele Anwendungen zu viel, da eine Akkustütze vorgesehen ist.
// Daher ist angedacht, die Quarzfrequenz radikal zu senken.
// Bei seriellem Interface (Blinkbit, UART-TxD oder I²C) ist das möglich.
// Bereits die bisherige ISR erlaubt theoretisch 3,579545 (NTSC-Quarz),
// 3,6864 MHz (Baudratenquarz) oder 4 MHz (glatter Quarz).
// Mit der hier angegebenen ISR komme ich auf 2 MHz — nur I²C oder gaanz langsame UART sinnvoll.
// Die ISR-Last liegt dann bei extrem sportlichen 96 % — durch Tail Chaining komme ich auf
// 80 %, was einen Baudratenquarz von 1,8432 MHz zulässig erscheinen lässt, dann 87 % ISR-Last
// und „Dauer-Tail-Chaining“ von vielleicht 5~10 Runden!
// Damit sollte die Stromaufnahme auf 2 mA sinken, und die Stromaufnahme des Vorverstärkers von
// (gemessen) 1,3 mA fällt ins Gewicht. Nicht vergessen die LFuse an den langsamen Quarz anzupassen!
// (Mit FET-Vorstufe 2 mA für den Vorverstärker. Der bringt gar nichts an Empfangsqualität.)
// Bei einer V-USB-Lösung sind ohnehin 15 MHz für den Quarz nötig, und der gefräßige Timer1
// muss laufen um unbediente Timer0-CompareB-Interrupts zu detektieren.
// I²C-Slave erfordert deaktiviertes Reset und damit ein Hochvolt-Programmiergerät zum Entwickeln.
Detected encoding: UTF-8 | 0
|