Forum: Mikrocontroller und Digitale Elektronik Frage zu AVR-Assembler


von Joachim Schäfer (Gast)


Lesenswert?

Hallo,

ich habe vor einigen Tagen mit der AVR-Assemblerprogrammierung
angefangen. Mittlerweile sind bei mir jedoch ein paar Fragen
aufgetaucht, vielleicht kann mir da ja jemand helfen...
1) Die Definitionsdateien (*.inc) der einzelnen uCs enthalten ja
dutzende symbolische Namen für alle möglichen
Register-/Port-/etc.-Adressen. Aber gibt es keine symbolischen Namen
für die Adresse des Resetbehandlungsroutine? Ok, der Wert ist eh immer
0 - aber irgendwie fände ich es einfach schöner wenn ich an den Anfang
meines Programmes etwas a la
.org RESETaddr
    rjmp main
schreiben könnte, genauso wie es für die Adressen der anderen
Interruptvektoren ja auch symbolische Namen gibt...

2) Auch für die Adresse der ersten frei benutzbaren Speicherstelle
(direkt nach der Interrupttabelle) gibt es scheinbar keinen
symbolischen Namen, kann das sein?

3) Ich würde gerne bestimmte Funktionalitäten in eigene Dateien
ausgliedern, z.B. eine Datei UARTlib.asm, die dann alle Funktionen für
UART-Kommunikation enthält - so dass ich, wenn ich in einem Programm
UART benutzen möchte, in der Hauptdatei einfach nur noch
.include "UARTlib.asm"
schreiben muss.
Grundsätzlich ist das natürlich kein Problem. Aaaaber ich hätte das
dann gerne so, dass die Interrupt-Vektoren für UART-Empfang etc. direkt
in der Datei UARTlib.asm definiert werden.
Das Problem das ich dabei sehe ist folgendes:
Wenn ich am Anfang meiner Hauptdatei die Datei UARTlib.asm include, und
in dieser Datei steht dann
...
.org URXCaddr
    rjmp int_urxc
int_urxc:
    [code]
...

und in der eigentlichen Hauptdatei kommt dann sowas wie
.org 0x0
    rjmp main
main:
    [code]
dann scheint der Assembler den Code der Datei UARTlib.asm einfach zu
überschreiben (oder irre ich mich da?)
Ist es also irgendwie möglich, die Interrupt-Vektoren in verschiedenen
Dateien festzulegen?

von crazy horse (Gast)


Lesenswert?

zu 1 und 2: wer hindert dich, die .inc-Dateien nach deinen besonderen
Wünschen anzupassen? Richtig, niemand.
zu 3: der Interrupt-Vektor der UART unterscheidet sich von Typ zu Typ

von Joachim Schäfer (Gast)


Lesenswert?

@crazy horst
zu 1 und 2)
Schon klar, dass ich mir die .inc Dateien selbst anpassen kann. Aber
zum einen will ich das nicht (weil ich dann gleich alle .inc-Dateien
anpassen müsste und ich, wenn ich jemandem ein Sourcefile schicken
will, ich immer auch die .inc Datei mitschicken müsste), zum anderen
war das ja bloss eine kruze Frage, OB es für diese Adressen tatsächlich
keine vordefinierten Symbole gibt. Ich werte Deine Antwort also mal als
"Nein" und kann damit gut leben (auch wenn mir nicht ganz klar ist,
WIESO es keinen vordefinierten Wert für die erste freie Speicherstelle
gibt...)

zu 3)
Offenbar hast Du da meine Frage nicht richtig verstanden, es ging
überhaupt nicht um UART etc., das war ein Beispiel um meine Frage
besser zu erklären

von Ingo Henze (Gast)


Angehängte Dateien:

Lesenswert?

zu 3)
Eine Möglichkeit wäre, alls Libs am Ende des Hauptmoduls zu includieren
und dann folgenden Konstrukt zu verwenden:
siehe Dateianhang

Man definiert sich also am Ende jeden Moduls ein Label und setzt direkt
dannach eine Variable (immer die selbe) mit .set auf diesen Wert.
Am Anfang eines Moduls kann man dann mit .org die gewünschten
Interrupt-Vektoren eintragen. Vor dem Beginn des eigentlichen Codes
wird dann mit .org die Adresse wieder auf das mit .set gemerkte Ende
des vorhergehenden Moduls gesetzt.

Im Mainmodul muß man allerdings darauf achten, das der Code erst hinter
dem letzen Interruptvektor beginnt (im Bsp. mit .org $030). Dadurch hat
man gegbenfalls ein paar ungenutzte bereiche im Flash.


Gruß
Ingo

von Winfried (Gast)


Lesenswert?

Die .inc Dateien sind kein Heiligtum, man kann die schon anpassen.
Außerdem ist nirgendwo garantiert, dass die sich nicht im Laufe der
Zeit ändern können. Damit läuft dann dein Programm vielleicht nicht
mehr korrekt, wenn du es ohne die .inc weitergibst.

Du kannst aber auch mehrere Dateien includen, denke zumindest mal. Du
könntest also alle Ergänzungen oder Änderungen in eine eigene Datei
auslagern.

Weil Reset immer bei 0 liegt, hat man sowas wohl nicht definiert.

Ich definiere mir eher eine Vorlage, wo schon sämtliche
Interruptvektoren und weiteres angelegt ist, was immer so ist.
Sozusagen einen Rahmen, den ich dann je nach Programm fülle.

von Joachim Schäfer (Gast)


Lesenswert?

Tausend Dank, Ingo, das ist genau die Idee die ich gebraucht habe!

Ziemlich genau sowas hatte ich mir nämlich auch schon überlegt, aber
mangels Programmiererfahrung ist mir keine Idee eingefallen wie die
späteren Module wissen können wo der Code der vorherigen Module
aufhört... aber die Idee mit Sprungmarke - klar, so geht's!

Aber eine kleine Zusatzfrage hätte ich noch: Diese Lösung müsste doch
auch dann funktionieren, wenn ich die externen Dateien gleich am Anfang
include, oder? (z.B. indem ich statt MODUL_END ganz am Anfang der
Hauptdatei eine Variable namens MODUL_START mit dem Wert 0x30
definiere, und jedes Modul setzt am Ende seines Codes diese Variable
dann weiter (und auch das Hauptprogramm startet bei MODUL_START))

von Ingo Henze (Gast)


Lesenswert?

Ja, sollte auch so wie von Dir gesagt funktionieren.
Ich mach es nur immer so, das ich die Module am Ende einbinde, deshalb
mein Beispiel in der Form.

Im Zweifellsfall würd der Assembler sowieso meckern, wenn sich Bereich
im Flash überschneiden. Und wenn man ganz sicher gehen will, guckt man
in das List-File, da sieht man dann sehr schnell, ob die Adresen
stimmen.

Gruß
Ingo

von Joachim Schäfer (Gast)


Lesenswert?

Die verschiedenen AVRs haben ja soweit ich weiss unterschiedlich viele
Interrupts.
Was ist denn die momentane Maximalanzahl an Interrupts bei den AVRs?
(Die kleinste Speicheradresse, die bei ALLEN AVRs für Code frei ist (=
nicht im Bereich der Interrupttabelle liegt) müsste demnach ja 2 *
[diese Anzahl] sein, oder?)

von Ingo Henze (Gast)


Lesenswert?

Ich hab mal alle .def Dateien abgeklappert, die höchste Adresse eines
Interruptvektors hab ich beim AT90CAN128 gefunden:

.equ SPMRaddr = $048 ; Store Program Memory Ready Interrupt Address

Aber den gibt es ja nicht wirklich, oder doch?
Ansonsten liegen Mega128/104/64 ebenfalls mit der SPMRaddr am
höchsten:

.equ SPMRaddr = $044 ; Store Program Memory Ready Interrupt Address

Wobei man sich hier nicht wundern darf, die oben genannten µC haben
nicht etwa so viel mehr Interrupts als die kleineren, sondern die
Adressen liegen im 2er-Raster, also 4 Byte voneinander entfernt, wo es
bei den kleinen nur in Einerschritten (2 Byte) weiterzählt.

Man könnte also pauschal als erste Flashadresse für Programmcode $04a
nehmen, verschenkt damit aber gegebenfalls einiges an Speicherplatz.
Besser ist es wohl doch, die Startadresse jeweils im Projekt direkt
einzutragen. Und damit man das nicht vergißt, könnte eventuell
folgender Konstrukt helfen:

;-------------------------------------------------
; FLASH_START sollte auf die letzte verwendete
; Interruptvektor-Adresse +2 gestzt werden
; Bsp. für einen ATmega16:
; .equ FLASH_START = SPMRaddr+2
;-------------------------------------------------
.equ FLASH_START = -1
.if FLASH_START < 0
  .error "FLASH_START hat einen ungueltigen Wert!"
.endif

.org $00
        rjmp main


.org FLASH_START
; Hauptprogramm
main:
        ldi temp, LOW(RAMEND)
        out SPL, temp
        ldi temp, HIGH(RAMEND)
        out SPH, temp
        ...

Damit bekommt man dann zumindest eine Fehlermeldung.

Gruß
Ingo

von Joachim Schäfer (Gast)


Lesenswert?

Vielen Dank nochmal, Ingo!
Da Flash-Speicher bei mir bisher eh noch kein Problem war, werde ich
dann vorerst einfach immer ab 0x50 starten. Verstehe trotzdem nicht,
wieso so ein doch recht interessanter Wert wie die erste frei
verfügbare Speicherstelle nicht auch in der .inc Datei zu finden ist...

von Joachim Schäfer (Gast)


Lesenswert?

Eben bin ich nochmal über etwas gestolpert, wo ich mir nicht so ganz
sicher bin.
Ich habe jetzt erfolgreich meinen Code in "Module" gegliedert, und an
den Anfang jedes Moduls habe ich eine Assembler-Direktive geschrieben a
la

.equ DELAY_AVAILABLE = 1 ; Flag that indicates that the DELAY module
has been loaded

Sinn des ganzen sollte sein, dass ein Modul, welches ein anderes
benötigt, dieses bei Bedarf automatisch einbinden kann, sofern es nicht
eh bereits eingebunden wurde. In meinem HD44780-Modul z.B. wollte ich
das dann ungefähr so benutzen:

.ifndef DELAY_AVAILABLE ; Check if the DELAY module is loaded
  .message "DELAY module not available, auto-including..."
  .include "delay.asm"
.endif

Schön gedacht, funktioniert nur leider nicht :-( Wenn besagtes Modul
nicht eh eingebunden wurde, dann kommt beim Kompilieren nämlich nun
dutzende Male der gleiche Fehler:
"error : Internal - label changed between passes - conditoonal on
forward reference?" - und das bei jeder Sprungmarke die ab diesem
zeitpunkt kommt. Wo der Fehler herkommt, kann ich mir fast denken
(Compiler ist verwirrt, weil zwischen zwei Läufen noch Code dazu
gekommen ist), aber die grosse Frage ist: Gibt es auch dafür eine
Lösung?

von Andi (Gast)


Lesenswert?

Man, der Reset- bzw. Power-On-Vector ist doch immer bei Adresse $000.
Da braucht man wirklich keinen Symbolnamen für die Adresse $000.
Halte es für Verschwendung und Unsinn mit dem Main-Prog immer bei
Adresee $050 zu beginnen da es auch AVRS gibt mit nur 12 IRQ´s oder
sogar weniger.
Deine mit der Zeit gesammelten Routinen kannst Du ja ruhig in eine .inc
Datei direkt oder als Macros ansammeln.
Als Macro hats den Vorteil, das der Code innerhalb des Macros erst in
den gesammten Code mit einbezogen wird, wenn der Name des Macros in der
Hauptdatei gefallen ist.
Wenn es mehr als 2 Words im Macro sind und es an verschiedenen Stellen
aufgerufen wird dann sollte man den Macronamen in einem UP
reinschreiben und im Hauptprogramm dieses UP aufrufen.

Gruß
Andi

von crazy horse (Gast)


Angehängte Dateien:

Lesenswert?

@Ingo
"hab ich beim AT90CAN128 gefunden, aber den gibt es ja nicht wirklich,
oder doch?"
Gibts schon, und sind auch recht nett :-)

von Johannes Raschke (Gast)


Lesenswert?

zu 2)
Du mußt ja bei kleinen Programmen, die keine oder nicht alle Interrupts
verwenden, nicht die komplette Interrupt-Verktor-Tabelle ins Programm
schreiben und kannst dementsprechend früher mit dem programmcode
anfangen. Wäre eine feste Adresse  füer den Codebeginn definiert, würde
man in diesem Fall einige Bytes verschwenden.

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.