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.
Ronald schrieb: > anstatt If(PA0) schreiben. So ist PA0 definiert:
1 | #define PA0 0
|
Ein If(PA0) wird daher immer "falsch" sein.
den Pin A0 abfragen geht so: if ( PINA & (1<<0) ) PORTD |= _BV(0) ; else PORTD &=~_BV(0);
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...
> 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
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.
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.