Hallo Ich brauche eine Funktion die auf takt-genau fuktioniert, deshalb versuche ich sie in asm zu programieren es ist aber teil eines C-Programms. Die Funktion-Prototyp ist: void getdata(unsigned char *p); Die Funktion soll 600 Sampels mit AD-Wandler machen und sie in einem buffer abspeichern. Das Problem ist eben das Speichern. Ich habe mir dazu eine globale Variable deklariert: volatile unsigned char data[600]; Es ist zwar eine globale Variable, wie ich aber an sie in einer asm-Funktion heran kommen soll, habe ich überhaupt keinen Plan. Deshalb habe ich der Funktion einen Parameter gegeben, und ich dachte das wäre in 16bit Adresse, also nach dem Aufruf der Funktion befindet er sich in Registern r24 und r25. Y-Pointer (r28,r29) wird vom gcc verwendet, also habe ich versucht die Adresse von data in X-Pointer zu laden: mov 26, 24 mov 27, 25 Und dann so zu speichern(work = r20): in work, ADCH /*Hier haengt sich das Programm auf*/ st 26, work /*Wenn beide Zeilen auskommentiert sind, lauft es*/ Wenn eine dieser Zeilen in der Funktion vorhanden ist hängt sich des Programm auf. Soweit ich weiss wird darf man diesen Pointer manipulieren. Die Frage ist also wie speichere ich die Daten in diese Variable ? Ich hoffe, ich habe das Problem verständlich dargelegt. Wäre sehr dankbar für die Antwort auf diese Frage.
Klingt so schlecht ja nicht. Die globale Variable kannst du einfach mit ihrem Namen benutzen im Assemblercode. Du kannst sie zur Dokumentation mit .extern deklarieren, aber das macht effektiv überhaupt nichts: du kannst den Namen "data" auch so einfach benutzen, und der Assembler wird ihn als "undefined extern" in die Symboltabelle aufnehmen und dessen Auflösung dem Linker überlassen. Eine richtige Idee, warum das nicht gehen soll, habe ich auf Anhieb nicht. Was ich nicht ganz verstehe: was ist an dieser Funktion takt-genau, sodass du sie nicht auch in C schreiben könntest?
Danke für die Antwort Jörg. Also wenn ich es richtig verstehe ist data eine Konstante die, die Start-Adresse von dem Speicher enthält und man kann sie etwa so in den X-Pointer laden: ldi 26,lo8(data) ldi 27,hi8(data) >>Eine richtige Idee, warum... Ja, eben warum benutzt gcc doch diesen X-Pointer, in der asm Ausgabe die mit -S erzeugt wird kommt er jedenfalls nicht vor. >>was ist an dieser Funktion takt-genau... Naja, was ist an irgendeiner funktion so takt-genau das man sie in asm schreiben muss, ich will einfach an dieser Stelle der MCU genau das tut wie es im Code steht, kein Takt mehr oder weniger, C garantiert mir das nicht.
> Also wenn ich es richtig verstehe ist data eine Konstante die, die > Start-Adresse von dem Speicher enthält und man kann sie etwa so in > den X-Pointer laden: > ldi 26,lo8(data) > ldi 27,hi8(data) Ja, das müsste funktionieren. > >>Eine richtige Idee, warum... > Ja, eben warum benutzt gcc doch diesen X-Pointer, in der asm Ausgabe > die mit -S erzeugt wird kommt er jedenfalls nicht vor. Ich glaube, X nimmt GCC nur selten. Vorzugsweise nimmt er Z. > >>was ist an dieser Funktion takt-genau... > Naja, was ist an irgendeiner funktion so takt-genau das man sie in > asm schreiben muss, ich will einfach an dieser Stelle der MCU genau > das tut wie es im Code steht, kein Takt mehr oder weniger, C > garantiert mir das nicht. Nun, du hast Warteschleifen drin, bis der ADC fertig ist mit dem Sampling. Damit ist die ,Taktgenauigkeit' im rektum, wie ein Kollege neulich treffend formulierte. ;-) Da kannst du dir den Stress auch sparen und gleich alles in C schreiben, der resultierende Code (mit Optimierung) sieht sehr ähnlich aus, nur ohne deine Bugs. ;-)
1 | #include <avr/io.h> |
2 | #include <stdint.h> |
3 | |
4 | void getdata(uint8_t *d) |
5 | {
|
6 | uint16_t i; |
7 | |
8 | for (i = 0; i < 600; i++) { |
9 | ADCSR |= _BV(ADSC); |
10 | loop_until_bit_is_set(ADCSR, ADSC); |
11 | *d++ = ADCH; |
12 | ADCSR |= _BV(ADIF); |
13 | }
|
14 | }
|
erzeugt (für einen angenommenen ATmega128): getdata: /* prologue: frame size=0 */ /* prologue end (size=0) */ movw r30,r24 ldi r18,lo8(0) ldi r19,hi8(0) .L2: sbi 38-0x20,6 .L3: sbis 38-0x20,6 rjmp .L3 in r24,37-0x20 st Z,r24 sbi 38-0x20,4 subi r18,lo8(-(1)) sbci r19,hi8(-(1)) ldi r24,hi8(600) cpi r18,lo8(600) cpc r19,r24 breq .L7 adiw r30,1 rjmp .L2 .L7: ret Jetzt, wo ich drüber nachgedacht habe, fällt mir natürlich auch dein Problem auf: deine 600 Schleifendurchläufe sind sehr fraglich. Du initialisierst den Schleifenzähler nicht, du inkrementierst r19 jeweils dann, wenn r18 wieder einmal 30 erreicht hat (also effektiv nach 256 Durchläufen der inneren Schleife, mit dem Offset des Wertes, den r18 anfangs zufällig hatte), und hoffst, dass r19 irgendwann einmal den Wert 20 erreicht. Bis das erreicht ist, hat die Funktion worst case schon knapp 64 KiBytes RAM überschrieben... Wie geschrieben, ich würde das in C schreiben, falls da nicht noch wesentliche Dinge hinzu kommen sollten gegenüber dem, was du derzeit in deinem Assembler-Teil stehen hast.
p.s.: Wenn du den ADC im auto-trigger-Modus benutzt (früher free running mode) und die Ergebnisse in der Interruptroutine einsammelst, hat deine CPU massig freie Zyklen im Vordergrund.
Hallo Jörg
Die fehlende Initialisierung der Register , war das Hauptproblem.
Das war mir gänzlich entgangen, dass die Register vielleicht (höhst
warscheinlich) vorher benutzt worden sind.
Ansonsten, gabs auch noch einiger Fehler , die hatten aber nichts mit
dem Aufhängen zu zun.
Eine Sache die ich nicht verstehe ist: in C sieht die Warteschleife
etwa so:
loop_until_bit_is_set(ADCSR, ADSC);
oder so:
while (ADCSR & (1<<ADSC));
In solche Schleife im funktioniert im Assembler nicht:
sampel_loop:
sbis _SFR_IO_ADDR(ADCSRA), ADSC
rjmp sampel_loop
sondern nur die:
sampel_loop:
sbis _SFR_IO_ADDR(ADCSR),ADIF
rjmp sampel_loop
Ich verstehe nicht warum.
>>Wie geschrieben, ich würde das in C schreiben, falls da nicht noch
Die ADC-Schleife wird verschwinden, ich benutze den eingebauten ADC-
nur zu test zwecken
Danke für deine Hilfe.
> loop_until_bit_is_set(ADCSR, ADSC); > sampel_loop: > sbis _SFR_IO_ADDR(ADCSRA), ADSC > rjmp sampel_loop ADCSR != ADCSRA Der label sampel_loop wäre übrigens ein typischer Kandidat für einen local label: 1: sbis _SFR_IO_ADDR(ADCSR), ADSC rjmp 1b p.s.: "sample" schreibt man so.
>>ADCSR != ADCSRA Ich werde wohl langsam alt, diese Kleinigkeit ist mir entgangen. >>einen local label Ja, ist ne gute Idee "sampel_loop" War mir auch nicht aufgefallen. Vielen Dank.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.