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.
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.
> 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.
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...
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?
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 :-)
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.