Source file: /~heha/basteln/Haus/Telefon/Impulswahl→DTMF/mfv.zip/mfv3c/clip.S

/* CLIP-Dekoder, ATtiny25, extremes Energiesparen */
#define __SFR_OFFSET 0
#include <avr/io.h>

#define TAU_SH 4	// 0, 4, 6 oder 8: Ausprobieren!
#define SUBSAMP 0	// 1 oder 0: Ausprobieren!
#define IRQMODE 1	// Beides funktioniert.
// Theoretisch müsste IRQMODE etwas Strom sparen
// Der Energiespeicher-Elko muss im IRQMODE 2200 µF haben,
// ohne 4700 µF. Aber das kann ein Beobachtungsfehler sein …
// Sicher ist sicher, ich muss wohl 4700 µF empfehlen.
// … Nee, alles Quatsch!

#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

// Erstinitialisierung von AKKU mittels ADLAR
// CPU-Taktfrequenz: 1 MHz
// Zeit zwischen 2 Abtastwerten: 52 Takte
.global acInit,acMean,acOnes,acByte
acInit:
#if IRQMODE
	ldi	r24,0x21	// 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
1:
#if IRQMODE
	sei			// ADC = einzige Interruptquelle
	sleep			// Die ISR sperrt die Interrupts, sonst nichts
#else
	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
0:
#if IRQMODE
	sei
	sleep
#else
	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
#endif
	movw	r22,DIFFL
	eor	r23,r21
	brpl	1b		// weitersuchen wenn keine Flanke
#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
	ret

#if F_CPU >= 8000000
# define F_DIV (F_CPU/8)
#elif F_CPU >= 4000000
# define F_DIV (F_CPU/4)
#else
# define F_DIV (F_CPU/2)
#endif
#define FADC (F_DIV/4/13)

// 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
acMean:
1:	clr	TLEVEL
	clr	r0
	clr	r25
2:	rcall	acEdge
	cpi	r24,FADC/870	// 22: 520 µs => 1 kHz
	brcc	1b		// Zu groß: Neustart
	cpi	r24,FADC/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 6 raus
	ret

#ifdef __AVR_ATtiny85__
.global acHisto
acHisto:
// R25:R24 = Zeiger auf <r22> Byte
	movw	XL,r24
	mov	r25,r22
	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)
acOnes:
1:	ldi	r25,100
2:	rcall	acEdge
	cp	r24,TLEVEL
	brcs	1b
	dec	r25
	brne	2b
	ret

#define FBIT (1200)
#define NBIT ((FADC*2+FBIT/2)/FBIT)	// 32 @ 4 MHz, *2 wegen Subsampling
#define NSTA (NBIT*38/32)		// 38 @ 4 MHz, erprobt
acByte:
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

// Gemeinsamer Anfang für PutNib und GetNib
27:	movw	ZL,r24		// Basiszeiger
	mov	r0,r22		// Index
	lsr	r0
	add	ZL,r0
	adc	ZH,r1
	ld	r24,Z
	sbrs	r22,0
	 swap	r24
	ret


.global PutNib,GetNib,x10
PutNib:	rcall	27b
	andi	r24,0xF0
	or	r24,r20		// Nibble (muss 0..F sein)
	sbrs	r22,0
	 swap	r24
	st	Z,r24
	ret

GetNib:	rcall	27b
	andi	r24,0x0F
	ret

x10:	lsl	r24
	mov	r0,r24
	lsl	r24
	lsl	r24
	add	r24,r0
	ret

.global clock_set
clock_set:
	cli
	ldi	r25,0x80
	out	CLKPR,r25
	out	CLKPR,r24
	sei
	ret
Detected encoding: UTF-80