Hallo Leute, ich bastel jetzt schon einige Zeit mit AVRs herum, allerdings habe ich sie nur in Assembler programmiert. Letztens habe ich was mit Interrupts gemacht, und kam dann an eine Stelle, bei der ich mir nicht mehr sicher war ob ich es nur funktionierend oder auch richtig gemacht habe. Was ich also suche ist eine Sammlung von Hinweisen, wie man etwas üblicherweise macht. Zum Beispiel die Problematik, daß man in einer ISR die Register nicht verfummeln sollte, die in dem drumherum laufenden Code für was anderes verwendet werden. Lege ich das für mich irgendwie fest, daß ich z.B. r16 bis r20 für dies und das verwende, und r21 bis r24 nur für Interrupt-Routinen? Oder sollte man hergehen und die Register irgendwo hin sichern? Stack, RAM, einzelne Variablen...? Wenn ich eine Funktion aufrufe, wie übergebe ich am klügsten meine Argumente? In Registern und diese dann auch als Ergebnis-Parameter zu verwenden scheint effizient (riecht allerdings etwas nach der allgemeinen globale-Variablen-Problematik und damit eng verzahntem Code), aber wenn man eher auf leicht wartbaren Code zielt, wie geht man da vor? Stackframe anlegen?
Das sind sehr gute Fragen - und die Antwort lautet: Du kannst es halten, wie Du willst. Das schöne am AVR ist die enorme Anzahl der Register, so ist es teilweise garnicht notwendig, die Register bei Interruptaufrufen zu sichern. Ich persönlich halte es so: Prinzipiell mache ich Interrupt-Aufrufe Registerneutral, d.h. alle im Interrupt genutzten Register (Ausnahme: Global benutzte Register, deren Inhalt für das Hauptprogramm genutzt werden soll) werden auf dem Stack gesichert und beim Ende zurückgeholt. Ist ein Interrupt wirklich zeitkritisch, dann reserviere ich einige Register für diesen speziellen Interrupt. Unterprogrammaufrufe - das ist auch eine Glaubensfrage. Bei Prozessoren mit wenig Registern benutzt man gerne die Methode, Übergabewerte auf dem Stack zu platzieren, das ist aber beim Atmel auch ziemlich egal. Natürlich wäre es eine tolle Sache, sich auf einen "quasi-Standard" zu einigen. Wenn man sich die Beispiele auf der Atmel-Home anschaut, so wird dort (bei den Beispielen, die ich bisher gesehen habe) nicht mit dem Stack, sondern mit Registern zur Übergabe von Parametern gearbetet. Wäre wieder mal ein nettes Thema fürs Wiki.
Villeicht interessiert es dich ja wie avr-gcc mit Registern umgeht: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage Auch wenn du kein C programmierst, so wird der C Code doch zunächst in Assembler gewandelt.
Den Stack nimmt man üblicherweise, weil man sich dann nicht mehr darum zu sorgen braucht, welche Subroutine welche andere aufruft; selbst Rekursionen sind dann kein Problem mehr. Häßlich sind natürlich dabei die Performanceverluste: das Pushen und Poppen ist vergleichsweise langsam; für den Rücksprung mußt Du erst die Rücksprungadresse poppen, dann den Rückgabewert pushen und die Rücksprungsadresse wieder drauf ... mit einem übersichtlichen Programmstil mit viel kurzen Unterprogrammen rouiniert man sich da schnell die Performance. Aber die Alternative erfordert Disziplin! Jedes Register ist entweder global, d.h. hat überall dieselbe Funktion, oder lokal, d.h. wenn ich ein Unterprogramm aufrufe, muß ich es vorher sichern, oder Interrupt-lokal, darf also ausschließlich in (gleichberechtigten) Interrupts verwendet werden. Wenn Du ohne Ausnahmen nicht hinkommst ("na, hier kann ich das ja ruhig "ausborgen", schließlich komme ich in diese Routine nur in Zustand A, in dem ich nicht in Routine B komme, wo es anders benutzt wird ..."), dann steige auf eine höhere Programmiersprache um! Ich kann ein die Stellung eines Schachbrettes in einem Dutzend Zügen abwägen, aber die Konsequenzen einer Mehrfachregisternutzung nach drei Programmerweiterungen kann ich nicht mehr im Blick behalten. In der glücklichen Zeit, als man ein X- und ein Y-Register und einen Akkumulator hatte, konnte man drauflos programmieren und die Register intuitiv nebenher nutzen, aber beim AVR wohl nicht mehr. Das Ding ist so gestrickt, daß ein C-Compiler sich darin zurecht findet, aber nicht ich und Du und Müllers Kuh.
Man könnte gucken, wie es andere machen, z.B.: http://www.mikrocontroller.net/forum/read-4-32158.html#new Algemein gilt, man sollte Regeln aufstellen und sich weitmöglichst daran halten. Auch sollte man nie direkt Register verwenden, sondern immer nur mit Namen arbeiten. Z.B. nehme ich a0..a3 als Arbeitsregister und zur Parameterübergabe. Und vor allem viel kommentieren, jede Funktion mit Header, wo die Funktion und die Registerverwendung erläutert wird. Peter
@Zotteljedi: Naja, das kommt ganz auf Deine Disziplin an... Also wenn ich etwas in Assembler mache (eigentlich nur in den Tinys ohne RAM, den Rest mach' ich in C), dann belege ich Register oft mehr als doppelt; aber ausschliesslich in den Interruptroutinen. Grundsätzlich verwende ich nur symbolische Namen für die Register. Alle globalen Variablen in meinen Programm bekommen feste Register. Das ist logisch. Dann bekommt das Hauptprogramm meistens zwei oder mehr Scratch-Register, manchmal auch die verschiedenen Subroutinen eigene. Aber oft braucht man das nicht, weil man eh nur einen kleinen Hardwarestack hat und nicht beliebig tief und willkürlich in den Subroutinen herumspringt und einen feste Aufruffolge der Subroutinen hat. Die Interruptroutinen können sich ihre Datenregister natürlich nicht teilen, also die Register die vergleichbar sind mit "static" in C. Die Scratch-Register können sich die Interruptroutinen teilen, wenn sichergestellt ist, dass immer nur eine Interruptroutine läuft. Es müssen natürlich eigene Scratch-Register ausschliesslich für die Interruptroutinen sein. Die Scratch-Register für das Hauptprogramm sind tabu. Naja, es geht schon, und wenn man diszipliniert ist, kann man beliebige Doppelt- und Mehrfachnutzung machen. Aber man muss alles schön Dokumentieren. Sobald der Controller etwas größer ist und RAM hat, dann verwende ich C. Da mach' ich mir keine Arbeit mehr, die einzelnen Register zu zählen und das RAM aufzuteilen usw. Das soll mal schön der Compiler machen. Dazu ist er da.
Naja, das Problem bei anderen abzuschauen liegt darin, daß man sich auch Unsinn angewöhnen kann. Hätte ich mir meinen C-Stil von Beispielprogrammen aus dem Internet zusammengebraut, au weia. Klar, hier gibt es auch viele Profis, will ich ja nicht in Frage stellen, aber wenn man hier nicht sehr lange mitliest, dann weiß man nicht wer wie professionell vorgeht. Ansonsten sehe und mache ich es grösstenteils auch so, wie Du sagst. Dachte nur, daß es für die AVR-Plattform, die es nun ja schon einige Zeit gibt, eine Art ABI existiert. Aber mangels Vorhandensein irgendwelcher kommerziellen binary-only-Bibliotheken von Drittanbietern scheint das wohl nicht nötig zu sein.
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.