Forum: Mikrocontroller und Digitale Elektronik Ein paar generelle Fragen zur Programmierung


von Zotteljedi (Gast)


Lesenswert?

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?

von thkais (Gast)


Lesenswert?

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.

von Malte (Gast)


Lesenswert?

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.

von Philipp Sªsse (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Michael (ein anderer) (Gast)


Lesenswert?

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

von Zotteljedi (Gast)


Lesenswert?

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