// CLIP-Dekoder auf ATtiny45 mittels A/D-Wandler
// Grundlage: Messung der Abstände der Nulldurchgänge
// CPU-Taktfrequenz: 1..1,99 MHz
#define __SFR_OFFSET 0
#include <avr/io.h>
#define IRQMODE 1 // Beides funktioniert.
// Theoretisch müsste IRQMODE etwas Strom sparen
// Ich hatte mal für IRQMODE 2200 µF ausgerechnet, ohne 4700 µF.
// Aber tatsächlich reichen sogar 220 µF, mit 470 µF ist man schon sicher.
// Selbst diesen gibt es mit schlanken 6 mm Durchmesser, als 6,3-V-Typ.
// Hochpass-Funktion in Software
#define TAU_SH 4 // 0, 4, 6 oder 8: Ausprobieren!
// Genauere Nulldurchgangsbestimmung
#define SUBSAMP 0 // 1 oder 0: Ausprobieren!
// Die Register R8..R15 sind für den DDS-Tongenerator (Frequenzsynthese)
// reserviert und hier frei, weil dieser nicht gleichzeitig mit der
// CLIP-Erkennung läuft.
#define MEANL r8
#define MEANH r9
#if TAU_SH > 6
# define MEANE r10 // 18 Bit
#endif
#define DIFFL r12
#define DIFFH r13
#define TLEVEL r14 // Gefundener Schwellwert
#if SUBSAMP
# define TBEFORE r15 // 0..1
#endif
// F_CPU ist historisch die Resonatorfrequenz, nicht die CPU-Taktfrequenz
#if F_CPU>=16000000
# error F_CPU zu groß!
#elif F_CPU>=8000000
# define CLOG2 3
#elif F_CPU>=4000000
# define CLOG2 2
#elif F_CPU>=2000000
# define CLOG2 1
#elif F_CPU>=1000000
# define CLOG2 0
#else
# error F_CPU zu klein!
#endif
# define F_ADC (F_CPU/(1<<CLOG2)/4/13)
// Erstinitialisierung von AKKU mittels ADLAR
// Zeit zwischen 2 Abtastwerten: 52 Takte
.global _Z6acInitv,_Z6acMeanv,_Z6acOnesv,_Z6acBytev,_Z6acDonev
_Z6acInitv:
#if PB5DEBUG&3
sbi DDRB,5
cbi GPIOR0,5
#endif
#if IRQMODE
ldi r24,0x28 // ADC-Störunterdrückung
out MCUCR,r24
ldi r24,0xFA
#else
ldi r24,0xF2 // Start; ADC-Takt = 250 kHz (4 µs), ADC-Rate = 19,23 kHz (52 µs)
#endif
out ADCSRA,r24
#if IRQMODE
sei // ADC = einzige Interruptquelle
sleep // Die ISR sperrt die Interrupts, sonst nichts
#else
1: sbis ADCSRA,4
rjmp 1b
sbi ADCSRA,4 // Interruptflag löschen
#endif
#if TAU_SH == 6
in MEANL,ADCL
in MEANH,ADCH
cbi ADMUX,ADLAR // ab jetzt rechtsbündig
#elif TAU_SH == 4
cbi ADMUX,ADLAR // rechtsbündig auslesen
in r24,ADCL
in r25,ADCH
swap r24
swap r25
eor r25,r24
andi r24,0xF0
eor r25,r24
movw MEANL,r24 // x 16
#elif TAU_SH == 8
cbi ADMUX,ADLAR // rechtsbündig
clr MEANL
in MEANH,ADCL
in MEANE,ADCH
#elif TAU_SH == 0 // nur differenzieren (1. Ableitung)
cbi ADMUX,ADLAR // immer rechtsbündig
in MEANL,ADCL
in MEANH,ADCH
#else
# error TAU_SH fehlt oder falsch!
#endif
#if SUBSAMP
clr TBEFORE
#endif
ret
// kehrt bei Flanke zurück und liefert die Zeit in 26-µs-Schritten seit der letzten Flanke
// VR: W20-R24
acEdge: clr r24
1: movw r20,DIFFL // vorherigen Messwert retten
subi r24,-2
// Nächsten A/D-Wert holen und Flanke feststellen
// NZ = Flanke, Takte: 19 + (CallRet)7 = 24
#if IRQMODE
sei
sleep // wartet bis A/D-Wandler fertig
#else
0: sbis ADCSRA,4
rjmp 0b
sbi ADCSRA,4 // Interruptflag löschen
#endif
in DIFFL,ADCL
in DIFFH,ADCH
#if TAU_SH == 6 // R23 R22
movw r22,MEANL // AAAA BBBB CCCC DDDD
lsl r22 // CCCD DDD0
rol r23 // AAAB BBBC
rol r22 // CCDD DD0A
rol r23 // AABB BBCC
rol r22 // CDDD D0AA
andi r22,3 // 0000 00AA
sub DIFFL,r23 // τ = 64
sbc DIFFH,r22 // Differenz ermittelt
add MEANL,DIFFL
adc MEANH,DIFFH
#elif TAU_SH == 4 // R23 R22
movw r22,MEANL // 00AA BBBB CCCC DDDD
swap r22 // DDDD CCCC
swap r23 // BBBB 00AA
andi r22,0x0F // 0000 CCCC
eor r22,r23 // BBBB CCčč č = C^A
andi r23,3 // 0000 00AA
eor r22,r23 // BBBB CCCC
sub DIFFL,r22 // τ = 16 (mehr Hochpasswirkung)
sbc DIFFH,r23 // Differenz ermittelt
add MEANL,DIFFL
adc MEANH,DIFFH
#elif TAU_SH == 8
sub DIFFL,MEANH // 8 bit, τ = 256
sbc DIFFH,MEANE
sbc r22,r22
add MEANL,DIFFL
adc MEANH,DIFFH
adc MEANE,r22
#elif TAU_SH == 0
sub DIFFL,MEANL
sbc DIFFH,MEANH
add MEANL,DIFFL
adc MEANH,DIFFH
#else
# error unsupported TAU_SH
#endif
movw r22,DIFFL
eor r23,r21
brpl 1b // weitersuchen wenn keine Flanke
#if PB5DEBUG&1
sbi PINB,5 // PB5 toggeln bei Flanke
#endif
#if SUBSAMP
// W22 = neuer Messwert, W20 = alter Messwert
eor r23,r21
tst r21
brpl 5f
// W22 positiv, W20 negativ
com r20
com r21 // positiv machen
rjmp 20f
5:// W22 negativ, W20 positiv
com r22
com r23
20: cp r22,r20
cpc r23,r21 // Neuer Wert betragsmäßig kleiner dem alten?
adc r24,r1 // Dann Nulldurchgang in „zweier Hälfte“
mov r22,r24
andi r22,1
sub r24,TBEFORE // Vom vorherigen Nulldurchgang Zeit fürs Ergebnis abziehen
mov TBEFORE,r22 // Jetzigen Bruchteil merken
#endif
#if PB5DEBUG&2
sbis GPIOR0,5 // TLEVEL gültig?
rjmp 0f // nein, nichts tun
cp r24,TLEVEL
sbi PORTB,5
brcc 0f // Länger als TLEVEL, lange Zeit, tiefe Frequenz = 0-Bit
cbi PORTB,5
0:
#endif
ret
// Ermitteln der durchschnittlichen Abstände der Nulldurchgänge
// 256 gültige Flanken ~ 200 ms
// R24 = durchschnittlicher Pegelwechsel-Abstand in 26 µs
// 1300 Hz => 385 µs => 14,8
// 2100 Hz => 238 µs => 9,2
_Z6acMeanv:
1: clr TLEVEL
clr r0
clr r25
2: rcall acEdge
cpi r24,F_ADC/800 // 22: 520 µs => 1 kHz
brcc 1b // Zu groß: Neustart
cpi r24,F_ADC/4800 // 4: 104 µs => 4 kHz
brcs 1b // Zu klein: Neustart
add r0,r24
adc TLEVEL,r1
dec r25
brne 2b // 256 Flanken ausmitteln
;inc TLEVEL // runden (gemessen: +1)
mov r24,TLEVEL // Hier kommt 11 raus
#if PB5DEBUG&3
sbi GPIOR0,5
#endif
ret
//Beim ATtiny85 gibt es Debug-Funktionalität
#ifdef __AVR_ATtiny85__
.global _Z7acHistoPhh
_Z7acHistoPhh: // R25:R24 = Zeiger auf <r22> Byte RAM
movw XL,r24 // RAM-Zeiger -> X
mov r25,r22 // Länge -> R25
clr r0
1: rcall acEdge
cp r24,r25
brcs 2f
mov r24,r25
dec r24
2: add XL,r24
adc XH,r1
ld r1,X
inc r1
st X,r1
clr r1
sub XL,r24
sbc XH,r1
dec r0
brne 1b
ret
#endif
// Sucht eine lange Kette von Einsen (tiefe Frequenz)
_Z6acOnesv:
1: ldi r25,100
2: rcall acEdge
cp r24,TLEVEL
brcs 1b
dec r25
brne 2b
ret
#define FBIT 1200
#define NBIT ((F_ADC*2+FBIT/2)/FBIT) // 32 @ 4 MHz, *2 wegen Subsampling
#define NSTA (NBIT*38/32) // 38 @ 4 MHz, erprobt
_Z6acBytev:
0: rcall acEdge // Warten auf lange Abstände (1-Bits)
cp r24,TLEVEL
brcs 0b
1: rcall acEdge // Warten auf kurzen Abstand (0-Bit = Startbit)
cp r24,TLEVEL
brcc 1b
// Startbit erkannt; jedes Bit ist 32 x 26 µs lang
ldi r25,8 // Bitzähler
ldi r18,-NSTA // Bitzeit (hier 1. Abtastzeitpunkt einstellen! Lange habe ich probiert.)
2: rcall acEdge
add r18,r24
brcc 2b // 32 × 26 µs = 832 µs (1 Bitzeit) abwarten
subi r18,NBIT
cp r24,TLEVEL // Länge des letzten Abstands
ror r19 // Bit einschieben (noch invertiert)
dec r25
brne 2b // 8 Runden
com r19
mov r24,r19
ret
_Z6acDonev:
sbi ADMUX,5 // Ergebnis links ausrichten um nur 8 Bit auswerten zu müssen
ldi r24,0xD1
out ADCSRA,r24 // Start mit Ergebnis in 50 µs ohne Interrupt
#if PB5DEBUG&3
cbi DDRB,5; // DEBUG
#endif
ret
Detected encoding: UTF-8 | 0
|