Hi, ich bin gerade dabei optiboot [1] an meine Bedürfnisse anzupassen und bin dabei auf ein konzeptuelles Problem gestoßen und erhoffe mir nun ein wenig Input, um aus dieser "Sackgasse" wieder heraus zu kommen. Meine Umbauarbeiten [2] waren bisher auch erfolgreich und soweit funktioniert alles. Allerdings möchte ich nun auch aus der eigentlichen Anwendung heraus in den Bootloader gelangen können (beim Empfangen eines bestimmten UART Strings). Dies geschieht derzeit mittels einem "jmp" auf die entsprechende Adresse. Allerdings halte ich das für unschön, weil ich die Adresse einkodieren muss und sich diese je nach Fuse-Einstellungen bzw. konkretem Mikrocontroller unterscheiden. Erstrebenswerter wäre ein Reset über den Watchdog. Allerdings finde ich keine Möglichkeit wie dies in meinem Fall zu bewerkstelligen ist, da der Bootloader selbst auch schon den Watchdog benutzt. Das Ganze läuft in etwa so ab: - Der Bootloader wird gestartet und aktiviert den Watchdog (Timeout: 1 Sekunde). - Wird während dieser Zeit etwas empfangen, dann wird der Watchdog jeweils zurück gesetzt. - Ist das Programmieren/Verifizieren abgeschlossen, "schlägt" der Watchdog irgendwann einmal zu. - Beim Start wird nun festgestellt, dass das Watchdog Reset Flag (WDRF) gesetzt ist. Ist dies der Fall, wird nun mittels "jmp" auf 0x0000 die eigentliche Anwendung gestartet. Soweit, so gut: Problem ist nur, dass ich mit einem Watchdog Timeout in der eigentlichen Applikation dafür sorge, dass der Bootloader direkt in die Anwendung springt und ihn damit effektiv umgehe. Beim Arduino wird das Ganze anders gelöst, weil der Mikrocontroller über eine externe Beschaltung resettet wird und somit das EXTRF zur Auswertung zur Verfügung steht. Nun war meine Idee den Bootloader dahingehend abzuändern, dass man das Timeout nicht mit dem Watchdog, sondern mittels eines Zählers und der dazugehörigen ISRs realisiert. Das würde aber "größere" Umbauarbeiten nach sich ziehen, weil das aktuelle Design bisher ohne Interrupts im Bootloader auskommt. Daher wollte ich, bevor ich mich an die Arbeit mache, fragen, ob jemand ggf. eine bessere bzw. cleverere Idee hat ;). Ideal z.B. wäre ein echter Reset ohne das WDRF zu setzen. Vielen Dank schon einmal im vorab! Mit freundlichen Grüßen, Karol Babioch [1]: https://code.google.com/p/optiboot/ [2]: https://github.com/Wordclock/firmware/blob/master/bootloader/wordboot/wordboot.c
Nimm eine eeprom Zelle sollte aber nicht 00 sein. EE = derzeitiger code, ansonsten wird in bootloader verzweigt und vorher eeprom Zelle incrementiert. Eventuell auch nur das high nibble und low nibble wird als config Register verwendet z.b. um eeprom/flash Bereiche für den bootloader zu sperren. Kann auchm HR als das low nibble sein, bit 7 und 6 wurde ich aber als Zähler reservieren.
Chris S. schrieb: > Nimm eine eeprom Zelle sollte aber nicht 00 sein. Vielen Dank für den Vorschlag. Allerdings gefällt mir die Vorstellung bei jedem Start im EEPROM herum zu pfuschen. Zum einen sind die Schreibzyklen begrenzt (klar sind es genug, es geht mir hier aber ums Prinzip) und zum anderen benutzt auch die eigentliche Applikation das EEPROM, insofern müsste man Überschneidungen ausschließen, um Seiteneffekte auszuschließen. Mit freundlichen Grüßen, Karol Babioch
Kannst ja auch nen IO-Pin vom AVR mit dem Reset vom AVR verbinden(natürlich noch nen Pullup ran und nen Widerstand in serie zum ISP-Header).
Es ist ein byte an eeprom Speicher. Normalerweise wird die letzte Speicherstelle verwendet. Ansonsten geht nur das hard codieren der Sprung Adresse bzw auch eine fixe Adresse dafür zB. 0004 mit einem weiteren long jump dort.
Es gibt im Prozessor bestimmt ein, zwei Register, die ungenutzt sind. Diese wiederum sollten sowohl vom Bootloader, als auch vom Programm aus erreichbar sein. Der Bootloader schreibt hier rein was Sache ist und das Programm kann dann nachschauen. Indirekte Sprungbefehle gibt es auch für diesen Fall. Eine weitere Möglichkeit ist die Verwendung von Festadressen im RAM. Für die normale Verwendung des Speichers muss ja ein Mechanismus vorhanden sein, der verhindert, dass Bootloader und Programm die gleichen Adressen verwenden. >Timeout nicht mit dem Watchdog, sondern mittels eines Zählers und der >dazugehörigen ISRs realisiert. Geht auch, kostet aber einen Timer. Diese sind aber oft Mangelware.
Karol Babioch schrieb: > Erstrebenswerter wäre ein Reset über den Watchdog. Nein. Wirklich erstrebenswert wäre, einfach seinen Anwendungscode zu beherrschen. Dann kann der nämlich gezielt aufräumen, bevor er in den Bootloader springt. Sprich: Er kann die gesamte Hardware geordnet und gezielt in genau den "jungfräulichen" Zustand versetzen, den der Bootloader nach einem Reset erwartet. Und das ist echt nur für die Leute ein Problem, die nicht selber programmieren, sondern nur geklauten Code zusammenkleben. Alle anderen sehen einfach zu jeder init_* eine passende uninit_* Routine vor. Der Sprung in den Bootloader besteht dann darin, die Interrupts zu disablen und dann all diese netten uninit_*-Routinen aufzurufen. Wenn keiner einen Fehler gemacht hat, dann sieht jetzt jedes verschissene Hardware-Register ganz genau so aus wie nach einem PowerUp-Reset. Mit einer einzigen generellen Ausnahme: MCUSR. Die allerletzte Aktion vor dem Sprung in den Bootloader muß sein, auch hier alle Flags zurückzusetzen. Daran kann der Bootloader dann nämlich völlig problemlos unterscheiden, ob er absichtlich von der Anwendung aufgerufen wurde, oder als Folge irgendeines Resets (gewollt oder ungewollt) und er kann dann auch die Quelle dieses Resets bestimmen und entsprechend verzweigen. Und ja, bevor du fragst: Es ist ein absolut fantastisches Gefühl, wirklich kompetent programmieren zu können und nicht hilflos rumfrickeln zu müssen...
uwe schrieb: > Kannst ja auch nen IO-Pin vom AVR mit dem Reset vom AVR > verbinden(natürlich noch nen Pullup ran und nen Widerstand in serie zum > ISP-Header). An der Hardware kann ich leider nichts mehr ändern. Amateur schrieb: > Es gibt im Prozessor bestimmt ein, zwei Register, die ungenutzt sind. > Diese wiederum sollten sowohl vom Bootloader, als auch vom Programm aus > erreichbar sein. Wobei das der Philosophie widerspricht, dass Bootloader und Anwendung unabhängig voneinander sein sollen. Bei gemeinsam genutzten Registern besteht (theoretisch) ja immer die Möglichkeit, dass es zu unerwünschten Seiteneffekten kommt, wenn eine der beiden Komponenten "fehlerhaft" agiert. Amateur schrieb: > Geht auch, kostet aber einen Timer. Diese sind aber oft Mangelware. Naja, im Bootloader ist das ja nicht wirklich ein Problem, und vor dem Verlassen des Bootloaders würde ich den Timer natürlich wieder frei geben bzw. zurücksetzen. c-hater schrieb: > Nein. Wirklich erstrebenswert wäre, einfach seinen Anwendungscode zu > beherrschen. Du bist ja im Forum bekannt für deine "provokanten" und frechen Beiträge. Die Sinnhaftigkeit bzw. Korrektheit der Aussagen allerdings sind oft anzuzweifeln, so auch in diesem Fall. c-hater schrieb: > Und das ist echt nur für die Leute ein Problem, die nicht selber > programmieren, sondern nur geklauten Code zusammenkleben. Falls das an mich gerichtet war, so behaupte ich einfach mal, dich enttäuschen zu müssen. Ich habe schon ein paar Jährchen Programmiererfahrung und hier keine konkrete Programmierfrage gestellt, sondern grundsätzlich nach Möglichkeiten und Konzepten gefragt, um mich prinzipiell für die beste (TM) entscheiden zu können. Falls du damit auf das Anpassen des bereits vorhandenen Bootloaders anspielst, so kann ich auch das begründen: Ein guter Programmierer fängt eben nicht immer bei 0 an, sondern ist auch in der Lage fremden Code zu beherrschen und anzupassen. In diesem Fall erschien mir das durchaus sinnvoll, weil o.g. Bootloader täglich von vielen tausenden Personen getestet wird, und damit im Prinzip alle gängigen Inkompatibilitäten zwischen verschiedenen Systemen ausgeschlossen sind. Außerdem ist die Auswahl bei meinen Anforderungen nicht unbedingt groß gewesen. Eine Eigenentwicklung hätte nicht nur mehr Zeit beansprucht (und vermutlich mehr unentdeckte Fehler), sondern eben auch das selbe Problem mit sich gebracht, weil es sich hier um eine konzeptuelle Frage handelt. Ich will das jetzt sicherlich zu keinem virtuellen Schwanzvergleich unserer Programmierkünste ausarten lassen, aber du scheinst ein Talent dafür zu haben technische Diskussionen persönlich werden zu lassen, indem du pauschal die Kompetenz des Fragestellers subtil in Abrede stellst und gleichzeitig dich und deine subjektiven Fantasien über alles andere stellst. Jedenfalls nach meiner Auffassung ist das nicht in Ordnung. c-hater schrieb: > Dann kann der nämlich gezielt aufräumen, bevor er in den Bootloader > springt. Klar ist das eine Möglichkeit. Allerdings kostet das neben dem Speicherplatz auch ein bisschen Hirnschmalz, damit die De-Initialisierungen geordnet vonstatten gehen. Ich habe mich auch schon mehr als nur einmal durch die Quellen von (größeren) Mikrocontroller Projekten gewälzt und ein gezieltes De-Initialisieren nur selten angetroffen. Wenn dann mit dem Hintergrund, dass man eine gegebene Funktionalität auch dediziert abschalten kann bzw. aus Energiespargründen - nicht aber, um den Bootloader zu betreten. Dies widerspricht nämlich der Philosophie, dass ein Bootloader unabhängig von der Anwendungssoftware funktionieren soll. Hast du denn ein paar Beispielprojekte für dieses Vorgehen parat? Dies an sich ist aber noch nicht einmal das Problem, sondern die o.g. Abhängigkeiten, die ich damit schaffe. Ein Sprung an die Bootloader-Adresse ist nämlich gewissermaßen beliebig. Je nach Mikrocontroller und Fuses ändert sich die Adresse. Das ist eine potentielle Fehlerquelle - gerade bei der Migration auf andere Mikrocontroller-Modelle & Co. Bei einem Reset mittels Watchdog-Timer kann dieses Problem nicht auftreten. Ein Sprung aus dem Bootloader heraus an die Adresse 0 kann bei AVRs auch nicht schief gehen und somit ist dieser Ansatz empfehlenswerter. Das machen die meisten von mir angesehen Bootloader auch so, und auch in den App-Notes von Atmel wird darauf hingewiesen. c-hater schrieb: > Wenn > keiner einen Fehler gemacht hat, dann sieht jetzt jedes verschissene > Hardware-Register ganz genau so aus wie nach einem PowerUp-Reset. Eben, und der Sinn eines Bootloaders ist es gerade nicht die Fehlerlosigkeit der Anwendung einzufordern. Fehlerlose Software gibt es oftmals nur in der Theorie. Meiner Erfahrung nach sind gerade diejenigen, die behaupten "fehlerfrei" zu programmieren, zuverlässige Produzenten von schwierig zu debuggendem Fehlverhalten. Ein Update der Anwendungssoftware ist in den meisten Fällen nämlich genau dann notwendig, wenn irgendetwas nicht wie gewollt funktioniert und sollte auch dann noch möglich sein, wenn die Anwendungssoftware "kaputt" bzw. nicht vorhanden ist. c-hater schrieb: > Und ja, bevor du fragst: Es ist ein absolut fantastisches Gefühl, > wirklich kompetent programmieren zu können und nicht hilflos rumfrickeln > zu müssen... Darf man denn in den Genuss kommen eines deiner fehlerlosen Meisterwerke selbst zu begutachten? Oder geschieht all das hinter verschlossenen Toren und der hier durch das Forum gegebenen Anonymität? Mit freundlichen Grüßen, Karol Babioch
Der Vollständigkeit halber möchte ich nun kurz noch den von mir verfolgten Ansatz skizzieren. Ich hatte zunächst in der Tat auf einen Interrupt-basierten Ansatz gesetzt. Das hat allerdings Anpassungen am Makefile erfordert, sodass die entsprechende Interrupt-Tabelle generiert und an der richtigen Stelle platziert wird. Zusätzlich war es notwendig die Interrupts "umzubiegen" und vor dem Sprung in die Applikation wieder "gerade" zu biegen. Zusammen hat all das einige hundert Bytes an Flash-Speicher gekostet und hat sich als unpraktisch erwiesen. Stattdessen benutzte ich nun einen Timer/Counter und polle das Timer Overflag Flag. Das Ganze sieht in etwa so aus:
1 | uint8_t counter = 0; |
2 | |
3 | while(!(UCSR0A & _BV(RXC0))) { |
4 | |
5 | if (TIFR0 & _BV(TOV0)) { |
6 | counter++; |
7 | TIFR0 = _BV(TOV0); |
8 | }
|
9 | |
10 | if (counter > BOOTLOADER_TIMEOUT_COMPARE_VALUE) { |
11 | start_application(); |
12 | }
|
13 | |
14 | }
|
Innerhalb von start_application() wird dann die verwendete Hardware (Timer, UART, usw.) zurückgesetzt, sodass der Mikrocontroller sich effektiv im selben Zustand wie nach einem echten Reset befindet. Der Inhalt des MCUSR Registers wird über ein dediziertes Register an die eigentliche Anwendung weitergegeben. Der gesamte Quellcode (d.h. der komplette Bootloader auf Basis von optiboot) kann hier [1] eingesehen werden. Mit freundlichen Grüßen, Karol Babioch [1]: https://github.com/Wordclock/firmware/blob/master/bootloader/wordboot/wordboot.c
Hi, nochmals ein Addendum für Leute, die das ggf. im Nachhinein finden sollten und nachvollziehen möchten. Ich habe den Bootloader mittlerweile in ein eigenes Repository ausgelagert, siehe [1]. Der o.g. Link hingegen ist nicht mehr gültig. Mit freundlichen Grüßen, Karol Babioch [1]: https://github.com/Wordclock/wordboot
Ich weiß nicht, ob die Idee von mir gerade gut ist. Ist ja nur eine fixe Idee. In der Praxis ist es ja so, dass die .fini0 Section die letzte section ist. Danach kommen im Speicher nur 0xFF --> NOP!!! die .fini0 Section definiert dann nur eine Endlosschlefe, die man aber überschreiben kann! Also, überschreiben, prüfen ob in den Bootloader gesprungen werden soll und laufen lassen. Dann wird er schon im Bootloader landen. Denke ich. http://www.nongnu.org/avr-libc/user-manual/mem_sections.html#sec_dot_init Oder etwas so in der Art:
1 | int main(void) |
2 | {
|
3 | extern uint8_t __data_load_end; |
4 | asm volatile( "push %A0" "\n\t" |
5 | "push %B0" "\n\t" |
6 | "ret"
|
7 | : : "e" ( &__data_load_end ) ); |
8 | |
9 | while(1) |
10 | {
|
11 | //TODO:: Please write your application code
|
12 | }
|
13 | }
|
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.