⇐ zurück zur Hauptseite
Zunächst muss die Speicheraufteilung und der Programmstart nach einem Reset geregelt werden:
.section .init0
;-------------------------------------------------------
; Interrupt-Vekroren
;-------------------------------------------------------
; Beim Laden eines Programms achtet der Uploader darauf,
; dass diese Interrupt-Vektoren erhalten bleiben
.ORG 0x0000
rjmp BootLoaderStart ; Power-On-Reset: Springe zum Bootloader
.ORG 0x001a
rjmp __vector_13 ; USI Start Condition
rjmp __vector_14 ; USI Counter Overflow
.ORG 0x001e ; Platz für die Interrupt-Vektoren lassen
;--------------------------------------------------------------------
Init: ; C-Programme landen hier
rjmp Init ; Endlos-Schleife, wenn kein Programm geladen ist.
Für diese Aufgabe gibt GCC die Section "init0" vor. An der Speicher-Adresse 0x0000 sind
zwei Byte für eine Sprunganweisung reserviert, die beim Power-On-Reset ausgeführt wird.
Hier wird zum Bootloader (Marke: BootloaderStart) gesprungen. C-Programme, die mit gcc übersetzt
wurden, springen an eine Adresse direkt hinter den anderen Interrupt-Vektoren, hier mit "
Init"
deklariert. Wenn der Bootloader merkt, dass er nicht gefordert ist, wird hierhin gesprungen.
Damit dieser Mechanismus erhalten bleibt, muss der Uploader dafür sorgen, dass der Sprung
zum Bootloader und die Interrupt-Vektoren erhalten bleiben. Außerdem muss er prüfen,
ob das zu ladende Programm auch wirklich mit einem Sprung zu 0x001e (
Init)
beginnt. Wenn nicht handelt es sich nicht um ein Standard-C-Programm und die Logik, wie der Bootloader
an das eigentliche Programm weitergibt, wird nicht funktionieren.
;------------------------------------------------------
; Bootloader
;------------------------------------------------------
.ORG BOOTLOADERSTART, 0xFF
rjmp I2CSlave_Initialize
rjmp I2CSetAddress
Mit
.ORG
BOOTLOADERSTART wird der Speicherbereich
des Bootloaders festgelegt. Der Parameter 0xFF bewirkt, das die übersprungenen Bytes mit
0xFF vorbelegt werden. 0xFF ist der Wert eines unprogrammierten Flash-Bytes.
Der eigentliche
Code beginnt hier allerdings noch nicht. Zunächst ist ein Sprung zur Initialisierungs-Routine
des I²C-Slave-Treibers codiert. BOOTLOADERSTART hat einen bekannten Wert. Die eigentliche Funktion
jedoch nicht. Die wird vom Linker festgelegt und hängt von vielen nicht beinflussbaren Parametern
ab. Die über BOOTLOADERSTART bekannte Sprunganweisung kann von den geladenen Programmen
genutzt werden, um den Treiber zu initialisieren. Analog funktioniert der Sprung zur Adressänderungsfunktion.
Beide Sprünge werden über "Interface.h" und "Interface.o" mit den Bezeichnungen "I2C_Initialize"
und "I2C_SetAddress" veröffentlicht.
BootLoaderStart:
.global BootLoaderStart
;------------------------------------------------------
; Initialisierung des Stackpointer
;------------------------------------------------------
ldi temp,HIGHBYTE(RAMEND);Stackpointer auf letzte
out SPH,temp ;RAM-Adresse setzen
ldi temp,LOWBYTE(RAMEND)
out SPL,temp
rcall BootLoader ; Sprung zum BootLoader-Code(in C codiert)
rjmp Init ; Sprung ins geladene Programm.
; Diese Stelle wird nur dann erreicht,
; wenn der Bootloader erkennt, dass
; nichts geladen werden soll.
.global BootLoaderStart veröffentlicht die
Marke, so dass im C-Code darauf zugegriffen werden kann.
Die ersten vier Anweisungen initialisieren
den Stack.
rcall BootLoader vezweigt in den in C geschriebenen
Teil des Bootloaders. Der Funktionsaufruf per
rcall
hat den Vorteil, dass die aufgerufene C-Funktion einfach nur eine
return-Anweisung auszuführen braucht um zurückzukehren.
Das tut sie dann auch, wenn der Bootloader nicht aktiv werden soll (der
aktive Bootloader jedoch verweilt bis zum nächsten Reset
in einer Endlosschleife).
Der Sprung zur Marke
Init bewirkt die Ausführung des regulären
Programms, wenn der Bootloader erkennt, dass er nicht aktiv werden soll.
.section boot
PI2CSlave_TransmissionStart: .word 0
PI2CSlave_DataRequest: .word 0
PI2CSlave_DataReceived: .word 0
I2CSlave_DeviceAddress: .byte 0
USI_TWI_Overflow_State: .byte 0
.global PI2CSlave_TransmissionStart
.global PI2CSlave_DataRequest
.global PI2CSlave_DataReceived
.global I2CSlave_DeviceAddress
.global USI_TWI_Overflow_State
Der I²C-Slave-Treiber benötigt acht Bytes für die Speicherung von Adressen und
Status-Informationen. Diese werden reserviert und veröffentlicht. Es sind die Adressen für
die Eventhandler, die Geräte-Adresse und ein Status-Byte. Nähers hierzu findet man an den
ensprechenden Stellen.
Das Besondere ist, dass für diesen Speicherbereich eine eigene
Section ("boot") vergeben wurde. Hierdurch kann der Adressbereich genau vorgegeben werden. Das geladene
Programm kennt dadurch den reservierten Speicherbereich und muss sicherstellen, dass er nicht überschrieben
wird!
Die Section "boot" wird an den Anfang des RAMs gelegt. Das Ende ist zum einem vom Prozessortyp
abhängig und zum anderen durch den Stack belegt. Der Stack wird von Standard-Bibliotheken initialisiert.
Hier etwas zu ändern würde bedeuten, dass bei jeder neuen Version der Bibliotheken die
Anpassung nachgefahren werden müsste. Ich kenne keinen Weg, dieses mit erträglichem Aufwand
zu bewerkstelligen.
Der Anfang des RAMs wird vom GCC mit der Section ".data" belegt. Diese
muss nach hinten verschoben werden. Im AVR-Studio sieht das wie folgt aus:
Die entsprechenden Schalter für den Linker sind: "-section-start=boot=0x800060" und "-section-start=.data=0x800068".
Geladene Programme müssen natürlich nur die Section ".data" verlegen. "boot" kennen
sie nicht.
Zu beachten ist, das die Startsequenz
der Standarbibliothek nicht eingebunden werden darf. Die entsprechenden Schalter für den Linker
sind: "-nostartfiles" und "-nostdlib".
⇐ zurück zur Hauptseite