Forum: Mikrocontroller und Digitale Elektronik Assembler Grundlagen (einbinden) (ATxmega128A1 + AVR Studio 5)


von Ronald (Gast)


Lesenswert?

Guten Tag,

für mich ist die Mikrocontrollerprogrammierung noch relativ neu, also 
vergebt mir bitte falls einige "dumme Aussagen" von mir gebe.^^

Ich muss einen ATxmega128A1 programmieren, damit er ein bestimmtes 
Signal verarbeitet.
Dazu verwende ich AVR Studio 5.

Ich hatte den Code bis jetzt in C programmiert, und bis jetzt lief alles 
einwandfrei.

Ich habe einige Funktionen geschrieben die einzeln auch alle 
funktionieren.
Wenn ich allerdings alle zusammenfüge habe ich das Problem, dass der 
Compiler den Code plötzlich anders "optimiert". (Die Optimierung habe 
ich nicht verändert. Allerdings werden später (in anderen Funktionen) 
teilweise größere Datentypen verwendet.)

Wenn an Pin A0 eine bestimmte Präambel erkannt wird dann soll er das 
anschließende Signal in ein char Array einlesen.
Dieser Teil in C funktioniert auch.
Allerdings kommen später dann noch größere Datentypen hinzu.
Anscheinend erkennt der Compiler dass, und "optimiert" den kompletten 
Code daraufhin für diese größeren Datentypen.

Ich habe unter anderem aber eine sehr zeitkritische Funktion.
Durch die neue "Optimierung" ist diese Funktion leider zu langsam 
geworden.


Deshalb "muss" ich diesen Teil jetzt in Assembler programmieren, und 
irgendwie in meinen jetzigen C Code einbinden.


Ich habe also ein Eingangssignal an PA0 (laut Datenblatt müsste das der 
Pin 95 sein) bei dem zuerst eine Präambel erkannt werden muss, und 
danach das dort anliegende Signal in eine Variable geschrieben werden 
muss.

Als einzelner C Code funktioniert das. Dadurch kann ich mir auch 
anschauen was für ein Assemblercode daraus wird.

Ich dachte mir nun da ich den Assemblercode habe, kopiere ich den 
einfach und füge ihn als asm volatile () in meinen C Code ein.

Dabei habe ich zur Zeit jedoch folgende Probleme:


1. Wie lege ich den Eingangspin (also den PA0) fest damit ich da im 
Assembler darauf zugreifen kann?

Aus dem C-Code If(PA0)
wird im Assembler
LDD R24, Z+8
SBRC R24,0
RJMP PC+0x02AA

Aber ich kann ja wahrscheinlich nicht einfach
asm volatile (
"LDD R24, Z+8"
"SBRC R24,0"
"RJMP PC+0x02AA"
)
anstatt If(PA0) schreiben.

Wie kann ich denn im Assembler den entsprechenden PIN direkt "auslesen"?


2. Ich habe einige If und while Schleifen in meinem C-Code.
Die werden im Assembler mit RJMP realisiert.
Wenn ich jetzt direkt im Assembler schreibe, woher soll ich denn dann 
wissen wohin er springen soll?


3. Ich habe eine globale Variable (volatile char daten[255])
Dort soll dann nach Erkennung der Präambel das an PA0 anliegende Signal 
in bestimmten Abständen gespeichert werden.
Der Assemblercode dafür ist für mich noch unverständlich, aber wie kann 
ich auf dieses Array denn im Assembler zugreifen?


4. Wenn ich einen C-Code habe und mir diesen als Assembler anschaue.
Wie kann ich dann einen Teil von diesem Assembler-Code direkt in den 
C-Code einfügen um den entsprechenden C-Codeteil zu ersetzen?

Die Frage 4 ist eigentlich mein Hauptproblem.

Ich hoffe dass ich mein Problem verständlich erklären konnte. Wenn 
nicht, fragt einfach nach.

Vielen Dank dass ihr das hier durchgelesen habt.

Ich hoffe ihr könnt mir irgendwie helfen.

Wie gesagt, ich habe mit Mikrocontrollern, bzw. mit dem AVR Studio noch 
sehr wenig Erfahrung.
Also bräuchte ich möglichst einfache Erklärungen.

Vielen Dank für eure Mühe.

Ich kann euch auch die entsprechenden Codeteile per E-Mail zukommen 
lassen, falls das benötigt wird.

von Peter II (Gast)


Lesenswert?

Ronald schrieb:
> Deshalb "muss" ich diesen Teil jetzt in Assembler programmieren, und
> irgendwie in meinen jetzigen C Code einbinden.

das ist schon das erste Problem.

Es wird nichts optimiert was du nicht willst oder was ungünstig ist. 
Zeige mal bitte ein Beispiel von dem du denkst das es nicht so übersetzt 
wird wie du es willst.
Klar kann man es mit ASM machen, aber ASM und C mischen ist nichts für 
"Anfänger" und es gibt auch überhaupt keinen Grund dafür.

von Krapao (Gast)


Lesenswert?

> für mich ist die Mikrocontrollerprogrammierung noch relativ neu...
> Ich muss einen ATxmega128A1 programmieren...

Eine sehr unglückliche Kombination.

> Deshalb "muss" ich diesen Teil jetzt in Assembler programmieren, und
> irgendwie in meinen jetzigen C Code einbinden.

Bei der GCC Toolchain gibt es dafür das AVR GCC Inline Assembler 
Cookbook. Dein Vorhaben ist aber weit über Anfängerlevel! An deiner 
Stelle würde ich unbedingt versuchen das Problem komplett in C in den 
Griff zu bekommen. Ich glaube noch nicht, dass es ein grundsätzliches 
Problem von C bzw. des Compilers ist.

Ansonsten

1) Bist du sicher, dass dieser Code If(PA0) in C das gewünschte macht? 
Ich hätte ein if(PORTA.IN & (1<<0)) bzw. if(PORTA.IN & (1<<PA0)) (mit 
#define PA0 0) erwartet, um diesen Eingangspin abzufragen.

2) In Inline Assembler kannst du auch Labels setzen und im RJMP darauf 
verweisen.

3) D.h. du müsstest auch Assembler lernen. Ein Weg ist, das Programm im 
Einzelschritt abzuwarbeiten und jeden Befehl im Instruction Set Manual 
nach zu lesen. Grundsätzlich könntest du den X,Y,Z-Pointer als 
Basisadresse des Arrays nehmen. Die Instructions LDD/STD bzw. LD/ST sind 
da interessant.

4) Nein geht nicht direkt. Du schaust den Code wahrscheinlich im AVR 
Assembler format an, während du in der C Toolchain bei Inline Assembler 
im GCC Assembler Format arbeitest. Weitere Probleme kann es bei Adressen 
geben und bei der Verwendung von Übergaberegistern für Variablen.

von MWS (Gast)


Lesenswert?

Ronald schrieb:
> anstatt If(PA0) schreiben.

So ist PA0 definiert:
1
#define PA0     0
Ein If(PA0) wird daher immer "falsch" sein.

von Düsendieb (Gast)


Lesenswert?

den Pin A0 abfragen geht so:

if ( PINA & (1<<0) )  PORTD |= _BV(0)  ; else PORTD &=~_BV(0);

von Ronald (Gast)


Lesenswert?

Peter II schrieb:
> Es wird nichts optimiert was du nicht willst oder was ungünstig ist.

Das dachte ich eigentlich auch.^^

Vielen Dank schon mal für euere Antworten.
Also die einzelnen Funktionen funktionieren!
Wenn ich nur die Funktion mit der Präambelerkennung und dem Einlesen des 
Signals kompilieren lasse, dann funktioniert alles.
Erst wenn ich weitere Unterfunktionen dazunehme bei denen dann mit dem 
eingelesenen Signal gerechnet wird, dann "optimiert" der Compiler den 
Code so dass es nicht mehr funktioniert.
Obwohl ich an dieser Unterfunktion NICHTS verändere.


Peter II schrieb:
>
Zeige mal bitte ein Beispiel von dem du denkst das es nicht so übersetzt
wird wie du es willst.

Nehmen wir einfach mal meine Präambelerkennung.
Folgendes steht am Anfang:
1
#define PA0 (PortA.IN & 0x01)
2
3
while (!PA0);

Aber die Erkennung sieht wie folgt aus:
1
if (PA0)
2
asm volatile (
3
   "nop \n\t"
4
   "nop \n\t"
5
   ::);
6
if (!PA0)
7
asm volatile (
8
   "nop \n\t"
9
   "nop \n\t"
10
   ::);
11
usw.
Das würde jetzt die Signalfolge 1 0 erkennen.
Die NOPs sind genau abgezählt um auf die Signaldauer zu kommen. (Ich hab 
hier nur mal zwei als Beispiel gelassen.)

Daraus macht der Compiler folgendes:
1
LDD R24, Z+8
2
SBRS R24, 0
3
RJMP PC+...
4
NOP
5
NOP
6
LDD R24, Z+8
7
SBRC R24, 0
8
RJMP PC+...
9
NOP
10
NOP
11
usw.

Wie gesagt, nur die Erkennung und das einlesen des Signals alleine 
funktionieren.
Wenn ich später weitere Funktionen hinzufüge macht der Compiler 
folgendes daraus:
1
MOVW R30, R16
2
LDD R24, Z+8
3
SBRS R24, 0
4
RJMP PC+...
5
NOP
6
NOP
7
MOVW R26, R16
8
ADIW R26, 0x08
9
LD R24, X
10
SBIW R26, 0x08
11
SBRC R24, 0
12
RJMP PC+...
13
NOP
14
NOP
15
usw.

Und das ohne dass ich an diesem Code etwas geändert habe.
Die Präambel bzw. einlesen ist bei mir eine eigenständige Unterfunktion.

Eigenständig getestet funktioniert es.
Aber die komplette Arbeit wird irgendwie anders übersetzt.

Als Optimierung ist -O1 eingestellt.

Und das Problem tritt einfach auf, wenn er mit den gelesenen Daten 
weiterrechnen soll. Und dabei dann ein anderer Datentyp als char 
hinzukommt.

Mit dem neuen Assemblercode den er daraus macht ist mein ATxmega aber 
leider zu langsam für das Signal.

Danke für euere Hilfe.
Ich weiß echt nicht mehr weiter...

von Uwe (Gast)


Lesenswert?

> Und das ohne dass ich an diesem Code etwas geändert habe.
tja dann liegt es wohl an dem Code drumherum den du uns jedoch nicht 
zeigen willst

von Karl H. (kbuchegg)


Lesenswert?

Was ist das eigentlich für ein Signal?
Als Neuling ein Signal einzulesen, bei dem es auf jeden Taktzyklus 
ankommt, ist schon extrem .... sportlich.

Und das hier
> Aber die Erkennung sieht wie folgt aus:
> if (PA0)
> asm volatile (
>   "nop \n\t"
>   "nop \n\t"
>   ::);
> if (!PA0)
> asm volatile (
>   "nop \n\t"
>   "nop \n\t"
>   ::);
> usw.
> Das würde jetzt die Signalfolge 1 0 erkennen.
> Die NOPs sind genau abgezählt um auf die Signaldauer zu kommen. (Ich hab
> hier nur mal zwei als Beispiel gelassen.)

bestärkt mich da nicht wirklich im meinem Vertrauen. Wieviele NOP sind 
es denn wirklich?

von Ronald (Gast)


Lesenswert?

Uwe schrieb:
>> Und das ohne dass ich an diesem Code etwas geändert habe.
> tja dann liegt es wohl an dem Code drumherum

Ja, sobald größere Datentypen hinzukommen optimiert der Compiler 
anscheinend das komplette Programm (mit allen Unterprogrammen) für 
diesen größeren Datentyp. (Finde ich zwar dämlich, ist aber anscheinend 
so...)
Das Programm ist relativ groß (zumindest für meine Verhältnisse) und 
nachdem die einzelnen Teile funktionieren will ich hier eigentlich 
keinen Seitenlangen Code posten. Außerdem würde dann jeder wissen wie 
schlecht ich programmiere.^^


Karl Heinz Buchegger schrieb:
> Was ist das eigentlich für ein Signal?
> Wieviele NOP sind es denn wirklich?

Jeder Impuls von dem Signal dauert genau 0,5 µs bzw. 16 Takte.
Das Beispiel oben (also LDD, SBRS + RJMP) dauert 4 Takte. Deshalb habe 
ich dort noch 12 NOPs eingefügt. (Wobei ich am Anfang einige NOPs als 
Verzögerung habe damit das Signal auch sicher anliegt.


Wie schon gesagt. Eigentlich funktioniert alles. (Zumindest jeder Teil 
an sich.)
Nur komplett mag er nicht mehr...


Gibt es irgendwo eine gute Assembler Erklärung für Anfänger aus der ich 
folgendes herausfinden kann:
- einbinden eines Assembler Unterprogramms in ein C-Programm in 
AVR-Studio 5
- Übergabe einer Variablen (ein Array) an dieses Unterprogramm
- einlesen eines Pins in Assembler bei einem ATxmega
- und schreiben dieses Wertes in diese globale Variable

Wenn mir jemand diese vier Punkte erklären könnte, würde ich 
wahrscheinlich schon ein großes Stück weiter kommen.

Um die genaue Anzahl der NOPs zu wissen habe ich mir ja auch schon die 
Taktanzahl der einzelnen Befehle raus geschrieben.
Die Dokumentation von Atmel ist da ja recht gut.
Wenn mir jemand die 3 Punkte oben erklären könnte, schaffe ich es 
vielleicht das Assembler-Programm selber zu schreiben.

Ich habe zwar schon danach gesucht, aber auf diese 4 Fragen habe ich 
leider noch nichts passendes gefunden.
Und wenn es etwas gibt, dann nur als theoretische Erklärung. Ich lerne 
aber wesentlich leichter wenn ich ein Beispiel sehe...
Außerdem ist wohl der XMega auch nicht gerade geeignet für einen 
Anfänger.^^
Aber daran lässt sich jetzt leider nichts ändern.

Ich würde mich freuen wenn jemand mir die vier Punkte oben erklären 
würde.
Oder zumindest einen Link zu einer guten und einfach zu verstehenden 
Anleitung (bzw. Beispielen) posten könnte. (Ich habe leider nichts 
gefunden. Wobei ich auch nur mit google suche da ich noch keine guten 
Seiten zu Mikrocontrollern kenne.)

Vielen Dank für eure Hilfe.
Ich hätte nicht gedacht dass ich hier so viel Hilfe bekomme. :-D
Danke :-)

von Krapao (Gast)


Lesenswert?

Das bereits erwähnte AVR GCC Inline Assembler Cookbook erklärt das. Das 
ist allerdings nicht anfängerfreundlich - es ist halt kein Thema für 
Anfänger. Es kann helfen, aus zwei Quellen gleichzeitig zu lernen. Im 
Roboternetz-Wiki ist ein breiter Abschnitt über AVR GCC Inline 
Assembler. Beide Quellen ergänzen sich IMHO gut.

Deinen Fall würde ich anders angehen. Die zeitkritische Routine würde 
ich mit möglichst wenig anderem Code mixen. Und ich würde nach 
Möglichkeit meine Programmlogik überprüfen. Das NOPen gefällt mir 
überhaupt nicht (abgesehen davon, dass es hierfür Funktionen in der 
avr-libc gibt). Ich würde prüfen, ob der Code bei dieser Erkennung mit 
Input Capture IRQ oder Exterem IRQ plus Sampling in eine Bitvariable 
(vgl. Entprellung) nicht geschickter wäre.

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.