Die Routine (nicht selbst geschrieben) generiert an einem Port (hier D)
ein Signal mit einem R/2R Netzwerk als D/A-Wandler, *Signal ist ein
Pointer auf die Tabelle (z.B. Sinustabelle), ad0-2 ist die gewünschte
Frequenz in 3 Bytes.
So wie es ist bricht der Loop da drin nie ab, ich brauche es aber so,
dass die Schleife abbricht, wenn sich der Status an einem
Eingangs-Portbit von 0 auf 1 ändert (signalOUT wird aufgerufen, wenn der
Eingang auf 0 geht, und soll wieder abbrechen, wenn er wieder hoch
geht).
Da ich mit Assembler sehr ungeübt bin möchte ich euch bitten mir zu
helfen, dies sauber einzubauen. Tut sowas das Gewünschte:
oder sollte/muss ein anderer Befehl verwendet werden? Die Anzahl Zyklen
sollte minimiert werden. Woher weiss man überhaupt, welcher Befehl
wieviele Zyklen braucht?
Vielen Dank
Martin
0x19 ist das PINA-Register des Mega128.
der Abbruch funktioniert jedoch nicht. Sieht jemand warum? Wird
vielleicht nicht dorthin zurückgesprungen, von wo signalOUT aufgerufen
wurde? Denke da komme ich allein nicht dahinter, wär wirklich gut wenn
mir da jemand den Tip geben könnte.
Gruss
Martin
Martin Geissmann schrieb:
> "sbic 0x19,1 ;2 cycles if skipping takes
place" "\n\t"
> "ret ;0 cycles if skipped" "\n\t">
Autsch.
Mach das nicht.
Nur weil nichts dort steht, bedeutet es nicht, dass nicht der Compiler
zwischen Betreten der Funktion und deinem Code nicht noch Sachen
erledigt. Dito für das Ende der Funktion. Stichwort: Register sichern
bzw. nach Ablauf der Funktion die gesicherten Register wiederherstellen
und aufräumen.
Hier lese ich das PINA-Register, maskiere das PINA1-Bit, teste das
Resultat Gleichheit mit der Maske und wenn es gleich ist springe ich zum
ret. Funktioniert aber auch nicht. Das kann doch nicht so falsch sein!
Wo ist der sprichwörtliche Hund begraben?
Gruss
Martin
Martin Geissmann schrieb:
> Ok, ist denn sowas besser:
Nein.
Der 'ret' hat da drinn nichts verloren.
Lass du Kontrolle einfach unten aus deinem Assemblerabschnitt (von dem
ich sowieso noch nicht überzeugt bin, dass der in Assembler sein muss)
rausfallen.
Der Compiler weiß schon, was es noch alles zu tun gibt, ehe er den
tatsächlichen Return auf Assemblerebene durchführen darf.
Sieh es so:
Der Compiler baut dir das Framework zusammen
1
Funktion
2
Alles was es beim Betreten der Funktion zu tun gibt
3
4
Benutzercode
5
6
Alles was es beim Verlassen der Funktion zu tun gibt
7
ret
Du klemmst dich an die Stelle 'Benutzercode' rein. Aus allem anderen
hältst du dich raus. Insbesondere hältst du dich aus allem was mit dem
Stack zu tun hat (und dazu gehört auch der ret) raus. Dein Code, wenn er
nichts mehr zu tun hat, fällt einfach durch sodass der vom Compiler
generierte Aufräumcode weitermachen kann.
Ok danke, das habe ich korrigiert, indem ich die Zeile mit dem ret
rausgelöscht habe, läuft aber trotzdem noch nicht.
Scheinbar muss das in Assembler geschrieben sein, damit man die absolute
Kontrolle über die benötigten Clockcycles hat - diese müssen bekannt
sein für die Frequenzberechnung bzw. den Wert, durch den diese geteilt
werden muss.
Das hier
"in r30, 0x19
schmeckt mir überhaupt nicht.
Hast du kontrolliert, ob das auch wirklich den richtigen Port abfrägt
(das Pin-Register von diesem Port)? Der Port ist auf Eingang geschaltet?
Dann lass den Inlineassembler weg und pack das Ding in eine eigene
Assembler-Quelldatei. Eine komplette Funktion in inline asm zu
hacken ist unsinnig, du hast keinen Gewinn von der Integration in
das C-Umfeld, dafür musst du dich mit dem ganzen constraining
befassen, das eigentlich nur dafür da ist, die Integration mit dem
Compiler anpassbar zu halten.
Der Inline-Assembler-Code enthält übrigens keine initiale Zuweisung
an das Registerpaar r30/r31, das die Quelladresse für den LPM-Befehl
bildet. Damit steht da irgendwas drin. Zu allem Übel überbügelst
du dieses irgendwas hernach noch mit deiner Eingabe aus PIND. Die
Auswertung der Eingabe ist auch reichlich umständlich: nach dem AND
ist bereits das Z-Flag gesetzt, du kannst also sofort ohne weiteren
Vergleich einen bedingten Sprung benutzen.
Martin Geissmann schrieb:
> Ok, ist denn sowas besser:>>
1
> ...
2
>
>> Hier lese ich das PINA-Register, maskiere das PINA1-Bit, teste das> Resultat Gleichheit mit der Maske und wenn es gleich ist springe ich zum> ret.
Statt
in r30, 0x19
and r30, 0b00000010
cpi r30, 0b00000010
wäre ein sbi{r,c} gefolgt von einem rjmp einfacher und schneller. Bei
meinem digitalen Sinusgenerator habe ich folgenden Code verwendet:
1
; 16 cycles total at 16 MHz -> 1MS/s D/A conversion rate
2
1: add r6, r2 ; 1 cycle
3
adc r7, r3 ; 1 cycle
4
adc r8, r4 ; 1 cycle
5
adc r30, r5 ; 1 cycle
6
brcc 2f ; 1 cycle if branch not
7
; taken, 2 cycles if branch taken
8
eor r31, r0 ; 1 cycle
9
2: lpm r16, Z ; 3 cycles
10
out _SFR_IO_ADDR(PORTD), r16 ; 1 cycle
11
12
nop ; 3 cycles
13
nop
14
nop
15
16
sbic _SFR_IO_ADDR(PINC), 3 ; 1 cycle (if rjmp is not skipped)
17
rjmp 1b ; 2 cycles
18
19
; Hier landet man, wenn PC3 Low ist
Die beiden letzten Instruktionen sind genau dazu da, den Status eines
I/O-Pins abzufragen und die Schleife zu beenden, wenn der Pin Low ist.
Ohne die drei NOPs wäre das ganze schneller, aber ich wollte eine
Samplerate von 1M/sec.
Der Trick mit dem brcc und dem eor ermöglicht 512 Byte große LUTs. Das
kann man weglassen und sich nochmal 2 Zyklen sparen.
Um aus der ganzen Sache eine Funktion zu machen, die man von C aus
aufrufen kann ist nicht mehr allzu viel Arbeit nötig: Register sichern,
benötigte Werte von Hand laden oder in die "richtigen" Register
verschieben, Register wieder herstellen, fertich.
Stephan