Source file: /~heha/basteln/Haus/Telefon/CLIP-Anzeige/clip.zip/dtmf.S

/*
 vorzeichenbehaftet** bedeutet:
 - Vorzeichen-Betrags-Darstellung wenn kein __AVR_HAVE_MUL__
 - Zweierkomplement wenn __AVR_HAVE_MUL__
 Dieses Vorgehen spart erheblich Rechenzeit und Kodegröße
 bei der Multiplikation „zu Fuß“.
*/
#include <avr/io.h>

.extern wintab		// Fenstertabelle im Flash, direkt vor der Sinustabelle
.extern sintab		// Sinustabelle im Flash, 256 Byte, an 0x100-Grenze ausgerichtet
.extern kdata		// Arbeitsspeicher im RAM, < 256 Byte
.extern kresult		// Ergebnisdaten im RAM, 20 Byte
//.extern dcval		// Ergebnis der Messung am alternativen Pin, 1 Byte

#define SLOTS 8		// Suchfrequenzen

// I/O zu Speicher- oder Registervariablen
.macro inb r,a
.ifge \a-0x60
	lds	\r,\a
.else
	in	\r,_SFR_IO_ADDR(\a)
.endif
.endm
.macro outb a,r
.ifge \a-0x60
	sts	\a,\r
.else
	out	_SFR_IO_ADDR(\a),\r
.endif
.endm

// Gemischt-vorzeichenbehaftete Multiplikation für Fensterfunktion
// PE:	\f1 = Faktor 1 vorzeichenbehaftet** (A/D-Wert)
//	\f2 = Faktor 2 vorzeichenlos 0..255
// PA:	R1  = Ergebnis vorzeichenbehaftet** (High-Teil der Multiplikation)
// VR:	\f2 (ohne MUL)
// Ta:	32, mit MUL 2
.macro mulvu f1,f2
#ifdef __AVR_HAVE_MUL__
	mulsu	\f1,\f2
#else
	clr	r1
.set bit,0
.rept 7				// 7 Bits (nicht 8)
	sbrc	\f1,bit
	 add	r1,\f2		// kann überlaufen
	ror	r1		// Überlaufbit einschieben
	clc			// Carry für nächste Runde löschen
.set bit,bit+1
.endr
	sbrc	\f1,7
	 sec
	ror	r1		// Vorzeichenbit hinzufügen
#endif
.endm

//16-bit-Negation (einer Zweierkomplementzahl)
// PE:	\h\l = 16-Bit-Zahl
// PA:	\h\l = negierte 16-Bit-Zahl
// VR:	-
// Ta:	4
.macro neg16 h,l
	com	\h
	neg	\l
	brcs	.+2
	inc	\h		// Nur wenn \l Null war und ist
.endm

// Betrag einer 16-Bit-Zahl
// Ta:	3 oder 6
.macro abs16 h,l
	sbrs	\h,7
	 rjmp	.+8
	neg16	\h,\l
.endm

// Vorzeichenbehaftete Multiplikation
// PE:	\f1 = Faktor 1 vorzeichenbehaftet** (gefensterter A/D-Wert)
// 	\f2 = Faktor 2 vorzeichenbehaftet** (Sinus- oder Kosinuswert)
// PA:	R1:R0 = Produkt in Zweierkomplement
// VR:	\f2 (ohne MUL)
// Ta:	2/39
.macro mulvv f1,f2
#ifdef __AVR_HAVE_MUL__
	muls	\f1,\f2		// 2 Takte
#else
	clr	r1
	mov	r0,\f1
	eor	r0,\f2		// Endgültiges Vorzeichen in Bit 7
	cbr	\f2,1<<7	// In \f2 Vorzeichen löschen
.set bit,0
.rept 7				// 7 Bits (nicht 8)
	sbrc	\f1,bit
	 add	r1,\f2		// kann nicht überlaufen wegen gelöschtem Bit 7
	lsr	r1
	ror	r0
.set bit,bit+1
.endr
	lsr	r1		// (Achtes Bit ist Null)
	ror	r0		// Zurechtschieben UND Vorzeichen gewinnen
	brcc	.+8
	neg16	r1,r0
#endif
.endm

// Quadrieren („vorzeichenbehaftet“)
// PE:	\r = Zahl vorzeichenbehaftet**
// PA:	R1:R0 = 14-Bit nichtnegative Zahl
// VR:	R23
// Ta:	32, mit MUL 2
.macro square r
#ifdef __AVR_HAVE_MUL__
	muls	\r,\r
#else
	clr	r1
	ldi	r23,127
	and	r23,\r		// Vorzeichen löschen
.set bit,0
.rept 7				// 7 Bits (nicht 8)
	sbrc	\r,bit
	 add	r1,r23		// kann nicht überlaufen wegen gelöschtem Bit 7
	lsr	r1
	ror	r0
.set bit,bit+1
.endr
	lsr	r1
	ror	r0		// Zurechtschieben
#endif
.endm

// Addiert R1:R0 auf *Y++
// VR:	R23
// Ta:	10/10
.macro add16
	ld	r23,Y
	add	r23,r0
	st	Y+,r23
	ld	r23,Y
	adc	r23,r1
	st	Y+,r23
.endm

.macro add16v r
// 8-Bit-Wert** \r auf 16-Bit-Akkumulator *Y++ addieren,
// dazu Summand vorzeichenrichtig erweitern, Register sparend (gut??)
// VR:	R23; R1:R0 = vzb. Summand
// Ta:	14/18
#ifdef __AVR_HAVE_MUL__
	mov	r0,\r
	clr	r1
	sbrc	r0,7
	 dec	r1
#else
	mov	r0,\r
	clr	r1
	sbrs	r0,7
	 rjmp	.+8
	ldi	r23,0x7F
	eor	r0,r23
	inc	r0
	dec	r1
#endif
	add16
.endm

// Ta:	8/8
.macro addcy r
	clr	r0	// CY nicht ändern
	sbrc	\r,7
	 dec	r0	// r0 = Vorzeichenerweiterung
	ld	r23,Y
	adc	r23,r0
	st	Y+,r23
.endm

// 16-Bit-Wert R1:R0 auf 24-Bit-Akkumulator *Y++ addieren,
// dazu Summand vorzeichenrichtig erweitern, Register sparend (gut??)
// VR:	R23, R0
// Ta:	18
.macro add24
	add16
	addcy	r1
.endm

// Multiplizieren und addieren
// PE:	R22 = Gefensterter A/D-Wert
//	R21 = (optional) A/D-Wert im Zweitfenster
// 	Z   = Zeiger auf Sinustabelle im Flash
//	Y   = 24-Bit-Zahl-Zeiger im RAM
// PA:	Y[3] = addiertes Produkt; Y vorgerückt
// VR:	R25,R1,R0
// Ta:	11(22)/57
.macro mulvvadd
	lpm	r23,Z	// Sinuswert laden
	mulvv	r22,r23	// 1. gefensterter A/D-Wert
	add24
#if WINDOWS==2
	lpm	r23,Z	// (zu langsam!?) restaurieren
	mulvv	r21,r23	// 2. gefensterter A/D-Wert
	add24
#endif
.endm

// Eine Frequenz suchen und komplex aufaddieren
// PE:	Y = Strukturzeiger: 2 Byte Frequenz (fix), 2 Byte Phase, 3 Byte Sinus-Akku, 3 Byte Kosinus-Akku
//	ZH = Sinustabellen-Zeiger
//	R22 = gefensterter A/D-Wert
//	R21 = (optional) A/D-Wert mit Zweitfenster
// Ta:	133, mit MUL 59
.macro addfreq
	ld	r0,Y+		// .add
	ld	r1,Y+
	ld	ZL,Y		// .pha
	add	ZL,r0
	st	Y+,ZL
	ld	ZL,Y
	adc	ZL,r1
	st	Y+,ZL
	mulvvadd
	subi	ZL,64
	mulvvadd
.endm

.macro zk2vb r
#ifndef __AVR_HAVE_MUL__
// \r in Vorzeichen-Betrags-Darstellung konvertieren (nur für MUL-lose ATtiny)
// sowie auf ±127 begrenzen (d.h. -128 wird zu -127)
	sbrs	\r,7
	 rjmp	.+8
	neg	\r		// Vorzeichen-Betrags-Darstellung
	brpl	.+2		// Normalfall
	dec	\r		// 0x80 -> 0x7F (begrenzen)
	ori	\r,0x80		// Bit 7 = Vorzeichen
#endif
.endm

// Auswertung
// PE:	Y = kdata (Fenster 1) oder kdata+6 (Fenster 2)
// VR:	R0,R1,R21..R23,Y,Z
.macro check
	sbi	_SFR_IO_ADDR(PORTB),3
	clr	r1
	ldi	ZH,hi8(kresult)
	ldi	ZL,lo8(kresult)
#if 0
// Summe kopieren und nullsetzen
	ld	r22,Y
	st	Y+,r1
	ld	r23,Y
	st	Y+,r1		// immer nullsetzen
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r22		// Daten nur kopieren wenn erlaubt
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r23
// Summe kopieren fertig
// Summe der Quadrate kopieren (obere 16 Bit)
	st	Y+,r1
	ld	r22,Y
	st	Y+,r1
	ld	r23,Y
	st	Y+,r1
// Wurzel ziehen? Siehe meine Webseite!
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r22
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r23
// Summe der Quadrate kopieren fertig
#else
	adiw	YL,5
#endif
// Für jede Suchfrequenz Betrag aus komplexer Zahl bilden
8:	ldd	r22,Y+5		// Realteil
	ldd	r23,Y+6
	abs16	r23,r22
	ldd	r0,Y+8		// Imaginärteil
	ldd	r1,Y+9
	abs16	r1,r0
	cp	r22,r0		// Näherung Betrag=|re|+|im|/2 je nachdem was größer ist
	cpc	r23,r1
	brcs	6f		// Springe wenn R23:R22 < R1:R0
	lsr	r1		// R1:R0 ≤ R23:R22 → R1:R0 halbieren
	ror	r0
	rjmp	7f
6:	lsr	r23		// R23:R22 < R1:R0 → R23:R22 halbieren
	ror	r22
7:	add	r22,r0
	adc	r23,r1
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r22
	sbis	_SFR_IO_ADDR(GPIOR0),7
	 st	Z+,r23
	clr	r1
	std	Y+4,r1
	std	Y+5,r1
	std	Y+6,r1
	std	Y+7,r1
	std	Y+8,r1
	std	Y+9,r1
	adiw	YL,4+6*WINDOWS
//	ldi	r23,hi8(kdata+6*WINDOWS+SLOTS*(4+6*WINDOWS))
	cpi	YL,lo8(kdata+6*WINDOWS+SLOTS*(4+6*WINDOWS))
//	cpc	YH,r23
	brcs	8b
	sbi	_SFR_IO_ADDR(GPIOR0),7	// Gültige Daten markieren
	cbi	_SFR_IO_ADDR(PORTB),3
.endm

.global ADC_vect
// Verfügbare Zeit: 128 × 14 = 1792 Takte
// (Die 14 ist der AVR-spezifische Taktteiler für den A/D-Wandler bei differenzieller Erfassung)
// Ta:	11 + 8 × (133+3) + 32 + 23 = 1154, mit MUL 11 + 8 × (59+3) + 2 + 23 = 532
//	(ohne ISR+RETI und ohne Auswertung alle 256 Aufrufe)
ADC_vect:
	sbi	_SFR_IO_ADDR(PORTB),2
	push	r0
	push	r1
#if WINDOWS==2
	push	r21
#endif
	push	r22
	push	r23
	push	YL
	push	YH
	push	ZL
	push	ZH
	inb	r0,SREG
	push	r0
#if WINDOWS==2
	inb	r21,ADCH
	zk2vb	r21
#else
	inb	r22,ADCH
	zk2vb	r22
#endif
	ldi	YH,hi8(kdata)
	ldi	YL,lo8(kdata)
	ldi	ZH,hi8(wintab)
	ld	ZL,Y		// Index in wintab
	inc	ZL
	brne	.+2
	 ldi	ZL,lo8(wintab)	// Anfang der Fenstertabelle setzen
	st	Y+,ZL
	lpm	r23,Z		// Mit Cos²-Fensterung versehen
#if WINDOWS==2
	mulvu	r21,r23
#else
	mulvu	r22,r23
#endif
	mov	r22,r1
	add16v	r22
	square	r22
	add24
#if WINDOWS==2
	ld	ZL,Y		// Zweiter Index
	inc	ZL
	brne	.+2
	 ldi	ZL,lo8(wintab)	// Anfang der Fenstertabelle setzen
	st	Y+,ZL
	lpm	r23,Z		// Mit Cos²-Fensterung versehen
	mulvu	r21,r23		// Zweiter gefensterter A/D-Wert
	mov	r21,r1
	add16v	r21
	square	r21
	add24
#endif
#if 1
	ldi	ZH,hi8(sintab)
9:	addfreq
	ldi	r23,lo8(kdata+6*WINDOWS+SLOTS*(4+6*WINDOWS))
	cpse	YL,r23
	 rjmp	9b		// Zu große Sprungdistanz für brne
#endif
	ldi	YH,hi8(kdata)
	ldi	YL,lo8(kdata)
	ld	r23,Y+
	cpi	r23,0xFF
// Akkumuierte Ergebnisse auswerten!!
// Bei Abtastrate 16 MHz / 128 / 14 = 8,9 kHz
// geht das mit 8,9 kHz / 256 = 35 Hz ≙ 29 ms.
// Günstig erscheint ein 20-ms-Erkennungsfenster:
// 16 MHz / 128 /14 = 8,9 kHz; 8,9 kHz / 178 ≈ 50 Hz ≙ 20 ms
// Von 697 Hz werden dann 7 Schwingungen erfasst, von 1633 Hz 16 Schwingungen.
// Das bedeutet, dass der DTMF-Ton nicht mehr als ±25 Hz abweichen darf.
// Für die Hilfsträger-Frequenzsynthese werden auf jeden Fall 2 Bytes benötigt.
// Sonst wäre die Frequenzabweichung bspw. 34 Hz.
#if WINDOWS==2
	breq	2f
	ldd	r23,Y+5
	cpi	r23,0xFF
	breq	.+2
	 rjmp	isrend
	adiw	YL,6		// Ergebnisse des zweiten Fensters adressieren
2:
#else
	breq	.+2		// Niemals gleichzeitig
	 rjmp	isrend
#endif
	check
isrend:	pop	r0
	outb	SREG,r0
	pop	ZH
	pop	ZL
	pop	YH
	pop	YL
	pop	r23
	pop	r22
#if WINDOWS==2
	pop	r21
#endif
	pop	r1
	pop	r0
	cbi	_SFR_IO_ADDR(PORTB),2
	reti
Detected encoding: UTF-80