Forum: Compiler & IDEs String Übergabe an asm-funktion


von Martin #. (martin-)


Lesenswert?

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.

von Martin #. (martin-)


Angehängte Dateien:

Lesenswert?

Und hier ist die ganze Funktion.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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?

von Martin #. (martin-)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Martin #. (martin-)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> 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.

von Martin #. (martin-)


Lesenswert?

>>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
Noch kein Account? Hier anmelden.