Hallo zusammen,
ich hab mal eine Frage zu einem Assembler-Code für den ATtiny2313. Und
zwar geht es um die Firmware für diesen AVR-basierten Signalgenerator:
http://www.myplace.nu/avr/minidds/index.htm
Firmware: http://www.myplace.nu/avr/minidds/minidds.asm
Verstanden habe ich folgendes:
Ab 0x100 steht eine 256 Byte lange vorberechnete Tabelle mit den
Ausgabecodes für ein Sinusignal. Weiter hinten kommen die Tabellen für
Dreieck, Rechteck usw.
Bleiben wir beim Sinus. Diverse Register werden initialisiert, die
Serielle Schnittstelle aktiviert und dann läuft irgendwann diese
Mainloop und gibt die Daten aus:
LOOP1:
add r28,r24 ; 1
adc r29,r25 ; 1
adc r30,r26 ; 1
lpm ; 3
out PORTB,r0 ; 1
rjmp LOOP1 ; 2 => 9 Taktzyklen
Der Befehl lpm lädt die über r30/r31 adressierten Daten aus dem Speicher
in r0, danach wird r0 auf dem Port ausgegeben. R30 wird durch den lpm um
eins erhöht, also wird beim nächsten Durchlauf das nächste Byte der
Tabelle gelesen.
So lange r26=0x00 ist wird die Tabelle komplett ausgegeben, was bei
einem CPU-Takt von 20MHz eine Ausgabefrequenz des Sinussignals von 20MHz
: 9 : 256 = 8680Hz geben müsste. Wenn in r26 eine 0x01 steht, dann wird
nur jeder zweite Wert der Tabelle ausgegeben und wir erhalten die
doppelte Ausgabefrequenz. Höhere Frequenzen werden also durch eine
unvollständige Ausgabe der Sinustabelle erreicht, es gibt keine
zeitbestimmenden Befehle in der Loop.
Ich verstehe jetzt nicht wie der Inhalt von r24 und r25 auf die Ausgabe
wirkt. Laut Beschreibung soll man die Ausgangsfrequenz ja in sehr
kleinen Schritten um ca. 1Hz einstellen können.
Was genau machen die ersten beiden Befehle der Loop? Wie werden
Frequenzen unter 8kHz erzeugt und wie entstehen die Frequenzen
dazwischen?
mfg
Harri
Hallo,
Harri schrieb:
> Bleiben wir beim Sinus. Diverse Register werden initialisiert, die> Serielle Schnittstelle aktiviert und dann läuft irgendwann diese> Mainloop und gibt die Daten aus:>> LOOP1:> add r28,r24 ; 1> adc r29,r25 ; 1> adc r30,r26 ; 1> lpm ; 3> out PORTB,r0 ; 1> rjmp LOOP1 ; 2 => 9 Taktzyklen>> Der Befehl lpm lädt die über r30/r31 adressierten Daten aus dem Speicher> in r0, danach wird r0 auf dem Port ausgegeben. R30 wird durch den lpm um> eins erhöht, also wird beim nächsten Durchlauf das nächste Byte der> Tabelle gelesen.
lpm erhöht Z nicht.
> So lange r26=0x00 ist wird die Tabelle komplett ausgegeben, was bei> einem CPU-Takt von 20MHz eine Ausgabefrequenz des Sinussignals von 20MHz> : 9 : 256 = 8680Hz geben müsste. Wenn in r26 eine 0x01 steht, dann wird> nur jeder zweite Wert der Tabelle ausgegeben und wir erhalten die> doppelte Ausgabefrequenz. Höhere Frequenzen werden also durch eine> unvollständige Ausgabe der Sinustabelle erreicht, es gibt keine> zeitbestimmenden Befehle in der Loop.>> Ich verstehe jetzt nicht wie der Inhalt von r24 und r25 auf die Ausgabe> wirkt. Laut Beschreibung soll man die Ausgangsfrequenz ja in sehr> kleinen Schritten um ca. 1Hz einstellen können.
Naja, offenbar enthält ja r28-30 einen 24Bit Wert, der den Low-Teil der
Adresse für LPM bestimmt. Dazu wird dann ein 24Bit Offset in r24 - r26
befindet.
Genutzt wird nur das Ergebnis in r30 als Low-Byte der Adresse, der
H-Teil in r31 wird zumindest hier ja nicht gesetzt.
Wenn r26 > 0 ist und ein Übertrag der Rechnung aus r24/r25 stattfindet,
wird dann natürlich 2 addiert.
Das Problem ist also dann schon vorher der Wertebereich von r24-r26, der
ist offenbar zu groß.
Gruß aus Berlin
Michael
Harri schrieb:
> Ich verstehe jetzt nicht wie der Inhalt von r24 und r25 auf die Ausgabe> wirkt.
Das realisiert offenbar so etwas wie 'Kommastellen'
Wenn du in eine Tabelle indizierst und den Index nur in Einerschritten
erhöhen kannst, dann kannst du natürlich nicht sehr fein abstufen.
Nimmst du aber künstlich noch 2 'Nachkommastellen' dazu, dann kannst du
den Inkrement viel feiner erhöhen. Du kannst zwar immer noch nur auf die
in der Tabelle vorhandenen Werte zugreifen, aber die Indexsteuerung wird
feiner. Genau das was du brauchst, damit zumindest die Länge einer
Einzelschwingung stimmt auch wenn die Form der Kurve nicht ganz exakt
ist.
Stell es dir so vor:
Deine Tabelle hat 100 Einträge.
Anstatt einem Index der von 0 bis 99 zählen kann, machst du einen Index
der Werte von 0 bis 9900 annehmen kann, wobei du nur den Teil der
Hunderter aufwärts als Index in deine Tabelle nimmst. Steh dein Indedx
beispielsweise auf 5623, dann holst du damit aus der Tabelle den Wert an
Position 56. Aber: Du kannst den Index in 'Hunderstelschritten' erhöhen
Alles klar. Das mit dem 24 Bit Wert und den Kommastellen hab ich
verstanden. Passt auch zum Rest des Quellcodes.
Mann also die gewünschte Frequenz mit einer 24 Bit Genauigkeit und 1Hz
Schritten einstellen aber in Wirklichkeit können wohl nur Frequenzen in
ca. 8kHz Schritten erzeugt werden, weil die niedrigeren Bits gar nicht
für den Index genutzt werden.
Am besten steck ich es mal zusammen und mess nach :-)
mfg
Harri
Harri schrieb:
> Mann also die gewünschte Frequenz mit einer 24 Bit Genauigkeit und 1Hz> Schritten einstellen aber in Wirklichkeit können wohl nur Frequenzen in> ca. 8kHz Schritten erzeugt werden, weil die niedrigeren Bits gar nicht> für den Index genutzt werden.
Die Frequenz kannst du schon einigermassen genau einstellen. Alles was
du brauchst ist ein Incrementwert, der ein paar Nachkommastellen
aufweist. Und natürlich auch ein Index, der mit diesen Nachkommastellen
incrementiert werden kann.
Aber die Kurvenform wird dann kein 'Sinus' mehr sein, sondern etwas
Sinusähnliches, welches aber gut genug ist
Annahme, wieder die 100 Werte in der Tabelle.
Ein Index mit 2 künstlichen Nachkommastellen (d.h. der eigentliche Index
beginnt an der Hunderterstelle). Aus der Frequenzberechnung hast du
erhalten, dass du bei 100 Tabellenwerten du den Index um jeweils 0.6
erhöhren musst, damit die 100 Werte bei bekannter Schleifenzeit in dem
Tempo ausgegeben werden, damit sich die gewünschte Frequenz einstellt
Also läuft die Schleife
Index Increment neuer Index Index in die Tabelle
0 60 60 0
60 60 120 1
120 60 180 1
180 60 240 2
240 60 300 3
300 60 360 3
360 60 420 4
420 60 480 4
480 60 540 5
540 60 600 6
.....
Siehst du, wie sich das Zugriffsmuster in die Tabelle an die '0.6
anpasst. Manchmal werden Indizes doppelt generiert, manchmal nicht. Je
nachdem wie sich das mit den 'Nachkommastellen' ausgeht.
Bei einer anderen Frequenz hast du eventuell errechnet, dass du den
Index mit dem Wert 8.76 erhöhen müsstest, damit sich das Timing für die
gewünschte Frequenz wieder ausgeht. In deiner 'Fixed Point Welt' (denn
genau darum handelt es sich letzten Endes: Fixed Point Arithmetik) sind
die 8.76 876 und du hast
Index Increment neuer Index Index in die Tabelle
0 876 876 8
876 876 1752 17
1752 876 2628 26
1628 876 3504 35
3504 876 4380 43
....
Harri schrieb:
> Danke für das tabellarische Beispiel.> Jetzt ist der Groschen (so eine Art früher Cent
Du meinst: Den kleinsten Wert eines 'Fixed Point Arithmetik' - Euro mit
2 künstlichen Nachkommastellen :-)