/*
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-8 | 0
|