Hallo,
für einen kleinen µC (Atmega48) würde ich gerne einen Bootloader
benutzen. Leider ist der µC relativ klein, so dass ich keine Chance habe
den Bootloader und die Application gleichzeitig in den µC zu bekommen.
Da aber der Bootloader viele Funktionen der Application beinhaltet
(interne RC Kalibrierung, komplettes Telegram Handling) wär mein
Gedanke, dass im Bootloader diese Funktionen eingabaut werden, aber
nicht in der Application. Es müsste dann möglich sein, diese Funktionen
von der Application aus im Bootloadercodebereich mitzubenutzen.
Aber wie mache ich das genau?
Vielleicht hat jemand von euch schon Erfahrungen damit gemacht! Es würde
mich freuen, wenn mir einer dabei zur Seite stehen könnte!
Grüße
Martin
Martin S. wrote:
> nicht in der Application. Es müsste dann möglich sein, diese Funktionen> von der Application aus im Bootloadercodebereich mitzubenutzen.>> Aber wie mache ich das genau?
Wieviel Millionen Stück willst Du denn verwenden, daß sich der kleine
Preisunterschied von ATmega48 nach 88 auch lohnt?
Theoretisch ist es nicht unmöglich.
Du wirst bloß viele Tage dran sitzen und eventuell zusätzliche
Fehlerquellen einbauen, die Du ohne diese Klimmzüge nicht hättest.
Du wirst Dich erstmal intensivst mit den Internas Deines Compilers
vertraut machen müssen, damit sich die beiden Programme nicht
gegenseitig den SRAM zerschießen.
Ich vermute mal, daß warscheinlich keiner vor Dir sich diese Pain ans
Bein gebunden hat, nur um ein paar Eurocent zu sparen.
Peter
Eine Möglichkeit: Bootloader enthält ein Feld mit den Funktionsaddressen
auf die gemeinsam genutzten Funktionen an einer bekannten Position. Kann
man z.B. bei GNU-tools mit section-Attribut und angepasstem
Linker-Skript machen, bei EWAVR gibt es vergleichbare Möglichkeiten
(kenne ich aber kaum).
Bootloader und Anwendung "kennen" beide die Startadresse des Feldes und
den Index der Funktion. Damit kann die Endwendung Funktionspointer mit
den Adressen aus der Tabelle setzten. Im Prinzip ähnlich PC-BIOS oder
ARM SWI.
Mann kann auch eine "magische Zahl" an den Anfang der Tabelle im
Bootloader-Bereich legen und dann in der Anwendung nach dieser Zahl
suchen. Spart etwas Gebastel mit sections und Linkeroptionen, kostet
jedoch ein wenig vom knappen Programmspeicher. Aufruf über
Funktionszeiger düfte ebenfalls etwas mehr Maschinencode produzieren. So
man keine statischen u./o. globalen Variablen in den gemeinsam
verwendeten Funktion nutzt, düfte es mit dem SRAM kein Probleme.
Ansonsten kann man aber auch eigene Speicherbereiche reservieren (ja -
wieder linker-script Gebastel u. evtl. spezielle sections). Langer Rede:
Nicht wirklich einfach - aber das hat Peter Dannegger ja bereits
geschrieben.
Hallo
@ peda
> Wieviel Millionen Stück willst Du denn verwenden, daß sich der kleine> Preisunterschied von ATmega48 nach 88 auch lohnt?
Muss es sich den immer gleich um große Stückzahlen handeln? Oft ist auch
nur der Weg das Ziel und gerade solche Probleme sind sehr lehrreich, da
man sich sehr in eine Architektur einarbeiten muss.
Natürlich wär es einfacher in diesem Fall einen größeren µC zu benutzen.
Aber wer schonmal z.B. viel mit FPGAs zu tun gehabt hat, der nimmt
vielleicht den steinigen Weg und spart sich schnell mal 20-30€ oder je
nach Typ kann man sich schnell mal mehrere hundert Euro sparen! Und nur
mehr bezahlen, weil man zu faul ist sich dem Problem zu stellen - welch
eine Arbeitsmoral!
> Theoretisch ist es nicht unmöglich.
Warum soll es auch unmöglich sein? Der Code steht im Flash als eine
Funktion, und ich rufe nur den Code auf - ich muss ja nur sagen wo ich
hinspringen soll - So schnell "unmöglich" aussprechen - viel zu früh!
> Ich vermute mal, daß warscheinlich keiner vor Dir sich diese Pain ans> Bein gebunden hat, nur um ein paar Eurocent zu sparen.
Wie gesagt, jetzt sind es mal ein paar Cent - es können aber auch mal
schnell Euro bzw. xxxx Euros werden!
@ mthomas
Danke erstmal für deine Antworten. Ich wollte einfach mal fragen, ob es
schon jemand mal versucht hat. Ich bin selber auch schon auf die Idee
gekommen es mit Sections zu probieren. Aber ich habe bis jetzt nur davon
gehört, aber in dieser Weise noch nichts damit gemacht. Aber vielen Dank
für die Hinweise.
Ich bin gerade dabei es mit Sections zu probieren. Nach einen halben
Abend (keine Tage oder Wochen) hatte ich schon die ersten Erfolge. Waren
bis jetzt nur Funktionen ohne Rückgabewert etc. nur Pins ansteuern. Aber
diese ersten Tests waren schonmal sehr erfolgreich. Werde es jetzt noch
mit komplizierteren Funktionen ausprobieren und mir noch was wegen der
IRQ-Table überlegen. Aber im großen und ganzen bin ich sehr
zuversichtlich, dass das was wird.
Ich werde die Tage mein Vorgehen erläutern!
Vg und danke für die Anregungen!
Martin
Das Problem daran ist, dass es zwei Codesections gibt: Eine Application
Section und eine Bootloader Section. Der Code der einen kann nur den
Bereich der anderen beschreiben. Wenn die Anwendersoftware in beiden
ist, müssen beide beschrieben werden, also auch gelöscht werden.
Deswegen muss der Bootloader zweiteilig sein, wobei der eine Bootloader
den anderen löscht und zusammen mit dem ersten Teil des Application Code
neu schreibt. Dann gibt es einen Sprung auf den gerade neu geschriebenen
Bootloader und dieser ersetzt jetzt den ersten Bootloader zusammen mit
dem Rest des Application Codes.
Da gibt es keinen Schutz des Bootloaders, weil sich beide zwangsläufig
löschen und wenn ein Schreibfehler passiert, hat man sich ausgesperrt.
Zudem sind die Adressen der Bootloader schwierig zu wählen, das hängt ja
davon ab wie der Application Code aussieht.
Ich finde diese Variante gefährlich und unnötig umständlich. Wenn es
privat und "just for fun" ist, kannst du es ja probieren, kommerziell
würde ich so etwas nie einsetzen.
Viele Grüße,
Peter
Hallo,
so wie pdiener das erklärt hat würde ich es natürlich auch nicht machen.
Der Bootloader darf nicht geändert werden. Das Risiko ist zu hoch den µC
wegen eines Schreibfehlers abzuschießen. Sprich - der Bootloader
beschreibt nur den Adressraum der Application - nicht sich selber, auch
nicht die Application den Bootloader!
Aber:
Bei mir beinhaltet die Application Code, den der Bootloader auch
beinhaltet (z.B. die Routine um Daten über Uart zu empfangen +
Überprüfung des Telegrams (Checksumme, Adresse, etc) - auch wird beim
Senden das Telegram aufgesetzt und die Checksumme hinzugefügt) Wenn
diese Routine nicht 100% sicher läuft, dann kann man das eh vergessen.
Aber warum soll die Application nicht diesen Code mitbenutzen?
Klar geht das. Wenn es sich für dich lohnt, dann mach das so. Der
Bootloader ist dann neben seiner Grundfunktion auch eine Art Library,
wie schon skizziert wurde.
Generell: Wenn das ein neues Teil ist und keine Erweiterung eines
bestehenden Gerätes, dann würde ich davon abraten, das Ding schon
vorneweg auf's Byte genau zu verplanen. Es sei denn du weisst wirklich
ganz genau, dass weder irgendwelche Zusatzwünsche kommen und das dir
nirgends durch Code/Design-Fehler anschliessend doch ein paar Bytes ROM
fehlen.
Martin S. wrote:
> Natürlich wär es einfacher in diesem Fall einen größeren µC zu benutzen.> Aber wer schonmal z.B. viel mit FPGAs zu tun gehabt hat, der nimmt> vielleicht den steinigen Weg und spart sich schnell mal 20-30€ oder je> nach Typ kann man sich schnell mal mehrere hundert Euro sparen!
Ich weiß jetzt nicht, was das mit FPGAs zu tun hat.
Kann man denn in FPGAs 2 getrennt compilierte Applikationen reinladen?
Kann ein FPGA sich überhaupt selber umprogrammieren?
Und wenn ja, dann ist das bestimmt ein völlig anderer Weg als bei einem
AVR. Und warum man damit 30€ sparen soll, ist mir völlig unklar.
Hier geht aber nicht um 30€, sondern eher um 30 Cent.
> Und nur> mehr bezahlen, weil man zu faul ist sich dem Problem zu stellen - welch> eine Arbeitsmoral!
Arbeitsmoral heißt, man hat eine gewisse Verpflichtung gegenüber dem
Auftraggeber.
Und das bedeutet, daß die Applikation möglichst sicher und zuverlässig
sein soll und die bezahlte Arbeitszeit effektiv genutzt. Und nichts
davon ist durch Deine Experimente gegeben.
Ich schreibe den Bootloader immer so, daß die Applikation nichts von
seiner Existenz merkt. Damit kann ich ganz normale bewährte
Standardeinstellungen des Compilers benutzen und muß keine
Besonderheiten beachten, die Fehlerquellen beinhalten. Ich kann daher
auch jeden Compiler und jede Compilerversion benutzen.
Wer garantiert Dir z.B., daß bei einer neuen Compilerversion oder einem
anderen Optmierungslevel die Parameterübergaben immer noch stimmen?
Und der Debugger wird auch schön blöd aus der Wäsche gucken, wenn Du
nach außerhalb des Sourcecodes springst. Das Debuggen wird also spürbar
schwerer.
Peter
Peter Dannegger wrote:
> Ich weiß jetzt nicht, was das mit FPGAs zu tun hat.> Kann man denn in FPGAs 2 getrennt compilierte Applikationen reinladen?> Kann ein FPGA sich überhaupt selber umprogrammieren?> Und wenn ja, dann ist das bestimmt ein völlig anderer Weg als bei einem> AVR. Und warum man damit 30€ sparen soll, ist mir völlig unklar.> Hier geht aber nicht um 30€, sondern eher um 30 Cent.
Man kann bei FPGAs ne ganze Menge (Multiboot, automatische Konfiguration
von verschiedenen Quellen etc...). Und bei FGPAs KANN ein umstieg auf
die nächtgrößere Version schon empfindlich teurer sein, ist aber in
diesem Fall echt zu weit hergeholt.
Genauso könnte man Argumentieren, das man sich immer den PC zulegt
sodass die gewünschte Apllikation gerade so darauf läuft...
Martin S. wrote:
>Es müsste dann möglich sein, diese Funktionen> von der Application aus im Bootloadercodebereich mitzubenutzen.
Das ist ein interessantes Problem, mit dem ich mich auch gerade
beschäftige. Wichtig scheinen mir folgende Punkte zu sein (die Liste ist
nicht abschließend und stellt den momentanen Stand meiner Überlegungen
dar):
- Der Bootloader muss komplett autonom funktionieren und auch dann in
der Lage sein, ein Anwendungsprogramm z.B. nach einem Reset zu laden,
wenn die aktuell geladene Version des Anwendungsprogramms nicht
funktioniert.
- Alle Funktionen des Bootloaders, die dem Anwendungsprogramm als
Bibliothek zur Verfügung gestellt werden, müssen ohne globale Variablen
auskommen. Da Bootloader und Anwendungsprogramm unabhängig voneinander
kompiliert werden, würde es sonst zu Überschneidungen im RAM kommen.
- Die Adressen der bereitgestellten Funktionen sollten in Form einer
Sprungtabelle an einer festgelegten Adresse stehen (z.B. FLASHEND),
damit verschiedene Varianten von Bootloadern und Anwendungsprogrammen
miteinander kompatibel bleiben.
- Man muss für die Funktionen entweder eigene Aufrufkonventionen
festlegen, oder dafür sorgen, dass Bootloader und Anwendungsprogramm nur
mit Kompilern erzeugt werden, die identische Aufrufkonventionen haben.
Gruss
Andreas
Guten Abend,
jetzt mal ein kurzer Einstieg in das was ich bisher gemacht habe.
Vorweg, ich arbeite mit Eclipse und dem zugehörigen AVR Plugin, deswegen
werde ich nicht viel über Makefiles verlieren, eher mehr welche
Parameter man wo ändern muss.
Gemeinsamer Code in Bootloader und Application als Library vorkompiliert
(libLED.a) (sollte garantieren, dass der Code in Application und
Bootloader immer gleich sind.)
1
#include<avr/io.h>
2
3
__attribute__((section(".LED")))voidInit(void)
4
{
5
DDRB=0xFF;
6
DDRC=0xFF;
7
}
8
9
__attribute__((section(".LED")))voidLEDa(void)
10
{
11
PORTB=0x00;
12
PORTC=0xFF;
13
}
14
15
__attribute__((section(".LED")))voidLEDb(void)
16
{
17
PORTB=0xFF;
18
PORTC=0x00;
19
}
generiert folgende lss Datei (Ausschnitt):
1
Disassembly of section .LED:
2
3
00000000 <Init>:
4
0: 8f ef ldi r24, 0xFF ; 255
5
2: 84 b9 out 0x04, r24 ; 4
6
4: 87 b9 out 0x07, r24 ; 7
7
6: 08 95 ret
8
9
00000008 <LEDa>:
10
8: 15 b8 out 0x05, r1 ; 5
11
a: 8f ef ldi r24, 0xFF ; 255
12
c: 88 b9 out 0x08, r24 ; 8
13
e: 08 95 ret
14
15
00000010 <LEDb>:
16
10: 8f ef ldi r24, 0xFF ; 255
17
12: 85 b9 out 0x05, r24 ; 5
18
14: 18 b8 out 0x08, r1 ; 8
19
16: 08 95 ret
Bootloader Hauptprogramm:
1
#include<util/delay.h>
2
externvoidInit(void);
3
externvoidLEDa(void);
4
externvoidLEDb(void);
5
6
intmain(void)
7
{
8
Init();// set port direction
9
LEDa();// switch on LEDa
10
_delay_ms(5000);// wait 5 sec
11
asm("rjmp 0x0094");// jump to application
12
return0;
13
}
Jetzt muss nur noch der Linker geändert werden um die Library einzufügen
und um die Stelle zu definieren, wo sich die Funktionen Init, LEDa und
LEDb befinden sollen:
Dazu Properties -> AVR C Linker -> Libraries
bei Libraries (-l) die Library hinzufügen: LED
bei Libraries Path (-L) den Pfad hinzufügen, wo sich diese lib befindet.
Jetzt muss noch der Linker Aufruf geändert werden:
aus
wobei -section-start=.LED=0x300 den Anfangspunkt der Funktionen
bestimmt.
Das Ergbenis der lss File (Ausschnitt):
1
0000006c <main>:
2
6c: 49 d1 rcall .+658 ; 0x300 <Init>
3
6e: 4c d1 rcall .+664 ; 0x308 <LEDa>
4
70: 80 e5 ldi r24, 0x50 ; 80
5
72: 93 ec ldi r25, 0xC3 ; 195
6
74: 28 ec ldi r18, 0xC8 ; 200
7
76: 30 e0 ldi r19, 0x00 ; 0
8
78: f9 01 movw r30, r18
9
7a: 31 97 sbiw r30, 0x01 ; 1
10
7c: f1 f7 brne .-4 ; 0x7a <main+0xe>
11
7e: 01 97 sbiw r24, 0x01 ; 1
12
80: d9 f7 brne .-10 ; 0x78 <main+0xc>
13
82: 3e c0 rjmp .+124 ; 0x100 <_etext+0x72>
14
84: 80 e0 ldi r24, 0x00 ; 0
15
86: 90 e0 ldi r25, 0x00 ; 0
16
88: 08 95 ret
17
18
Disassembly of section .LED:
19
20
00000300 <Init>:
21
300: 8f ef ldi r24, 0xFF ; 255
22
302: 84 b9 out 0x04, r24 ; 4
23
304: 87 b9 out 0x07, r24 ; 7
24
306: 08 95 ret
25
26
00000308 <LEDa>:
27
308: 15 b8 out 0x05, r1 ; 5
28
30a: 8f ef ldi r24, 0xFF ; 255
29
30c: 88 b9 out 0x08, r24 ; 8
30
30e: 08 95 ret
31
32
00000310 <LEDb>:
33
310: 8f ef ldi r24, 0xFF ; 255
34
312: 85 b9 out 0x05, r24 ; 5
35
314: 18 b8 out 0x08, r1 ; 8
36
316: 08 95 ret
Dabei ist deutlich der Sprung zu den Funktionen der library zu sehen
(0x6c und 0x6e) und auch der Einstiegspunkt der Application (0x82 ->
Sprung an die Adress 0x100). Der Sprung zur Application kann frei
gewählt werden, nur sollte darauf geachtet werden, dass dieser am Begin
einer Page ist, da beim Updaten, bevor neuer Code geschrieben wird die
Page gelöscht werden muss.
Nun zur Application:
Auch hier muss wieder die library eingebunden werden und die
Anfangsadresse der Funktionen dem Linker übergeben werden. Nicht zu
vergessen, auch die Application muss versetzt werden und zwar auf die
Adresse 0x100:
Wenn man nun die beiden lss vergleicht sind man, dass die Funktionen an
der selben Adresse stehen und von beiden Programmen (Bootloader,
Application) richtig angesprungen werden.
Das hier sollte der Grundstein zum lösen des Problems sein!
Ich bin gerade dabei eine saubere Lösung für die Interrupts zu machen,
da sich hier das Problem aufgetan hat, dass nur eine
Interruptvektortabelle zur Verfügung steht und nicht zwei separate wie
beim Atmega88.
Bevor jetzt wieder die Diskussion entflammt, warum man denn doch nicht
einen größeren µC nimmt will ich ausdrücklich auf die Fragestellung
eingehen, bei der es hieß, WIE man es realisiert und nicht welche
Alternativen auf Hardwarebasis es gibt! Danke!
Schau mal hier rein Beitrag "Re: AVR-Bootloader mit Verschlüsselung"
Dort habe ich das gemacht wie du es dir vorstellst, ein mit
Anwendungsfunktionen erweiterter Bootloader. Allerdings eben eine
Kombination aus Assembler für den Bootloader und WinAVR GCC C Source für
die Anwendung. Das Include "Special.inc" enthält alle Funktionen wie
FLASH lesen/schreiben usw. die aus der Anwendung heraus später benutzbar
sein sollen. Im Ordner \test\ Datei "AVRootloader.h" zeigt dann wie man
diese Funktionen aufrufen kann. Die Aufrufkonventionen müssen natürlich
an den zu verwendenden Compiler angepasst sein.
Ich persönlich halte sowas aber nur für eine Optimierung eines Projektes
und nicht als eine besonders geschickte Vorgehensweise. Bootloader und
Anwendung sind separate Applikationen die strikt voneinander getrennt
betrachtet werden sollten. In Ausnahmen wie zb. bei meinem Bootloader,
der mehrfach in die kleinste Bootloader Sektion auf großen AVRs
reinpasst, und somit unnötig FLASH verplempert, kann man diesen
ungenutzten FLASH eben nutzen für Lookup Tabellen, Konfigurationen oder
anwendungsspezifische generische Funktionen. Restriktionen wie zb.
keinen Stack/SRAM zu verbrauchen existieren da nicht für diese
Anwednungsfunktionen, man könnte sogar globale Variablen gemeinsam
zwischen Applikation und Anwednungsbezogene Bootloaderfunktionen
benutzen. Ist nur eine Frage der Konfiguration des Compilers.
Einzigst die durch den Entwickler per Definition vereinbarte strikte
Trennung von Bootloader und Anwendung aus Sicht der Stabilität und
Wartung exisistieren, aber die kann jeder Entwickler nach seinem Gusto
festlegen, mit entsprechenden Konsequenzen. Ein solcher Ausnahmefall
wäre in meinen Augen eine im Bootloader verankerte Failsave Routine für
die Anwendungen. Wenn zb. der Bootloader eh schon auf den Watchdog Reset
reagiert und der WDT in der eigenen Anwendung zum Failsave benutzt wird
dann ist es durchaus sinnvoll eventuell diesen Failsave mit in den
Bootloader als Funktion zu integrieren. Das zerstört aber eben das
Konzept das der Bootloader vollständig transparent für die Applikation
und unabhängig vom jeweiligen Projekt zu separieren ist. Ansichtssache
eben ;)
Gruß Hagen