Guten Tag Ich versuche mich aktuell - mithilfe des hiesigen AVR Tutorials - an einem Bootloader. Ich arbeite mit einem Atmega644P. Das Problem nun: Der Bootloader springt nicht an, bzw. das Programm in der Bootloadersektion des Flashs nicht ausgeführt zu werden. Entsprechend funktionieren auch keine Interrupts der UART Schnittstelle. Ich vermute schlicht, dass ich dem Compiler die Boot Reset Address falsch mitteile und ich bin diesbezüglich auch etwas verwirrt. In den Linker Options definiere ich mit .text=0x7000 die Boot Reset Address, die gemäss Datenblatt für eine Boot Size von 4096 Words deklariert ist. Wenn ich das .hex File dann aufmache sehe ich, dass das Programm ab Addresse 0xE000 startet. Ist nun 0x7000 die Word Addresse und 0xE000 dann die Byteaddresse? Hab dann auch mal versucht in den Linker Options die Addresse 0x3800 zu definieren, damit das Programm ab Byteaddresse 0x7000 startet.. Allerdings hatte das auch keinen Zweck, das Programm wird einfach nicht richtig ausgeführt... Irgendwo hab ich da nen Knoten. Hab auch schon gegoogled und hier so manchen Thread durchgeschaut, bin allerdings auf keinen grünen Zweig gekommen. Was mache ich falsch?
Eros S. schrieb: > In den Linker Options definiere ich mit .text=0x7000 die Boot Reset > Address Nein, damit schiebst du das Programm in den Bootsektor. Definiert wird damit gar nichts. Damit das Programm dort auch nach dem Reset ausgeführt wird, musst du auch den Resetvektor mit den Fuses entsprechend setzen. Das ist damit: Veit D. schrieb: > hast du die Fuses auch dementsprechend gesetzt? gemeint.
Fuses sind folgendermassen gesetzt: Extended: 0xFF High: 0x90 Low: 0xFF
:
Bearbeitet durch User
Eros S. schrieb: > Was mache ich falsch? Vielleicht setzt du IVSEL nicht innerhalb von 4 CPU-Zyklen?
1 | MCUCR = temp | (1<<IVCE); |
2 | 000071BF LDI R24,0x55 Load immediate |
3 | 000071C0 LDI R25,0x00 Load immediate |
4 | 000071C1 LDD R18,Y+8 Load indirect with displacement |
5 | 000071C2 ORI R18,0x01 Logical OR with immediate |
6 | 000071C3 MOVW R30,R24 Copy register pair |
7 | 000071C4 STD Z+0,R18 Store indirect with displacement |
8 | MCUCR = temp | (1<<IVSEL); |
9 | 000071C5 LDI R24,0x55 Load immediate |
10 | 000071C6 LDI R25,0x00 Load immediate |
11 | 000071C7 LDD R18,Y+8 Load indirect with displacement |
12 | 000071C8 ORI R18,0x02 Logical OR with immediate |
13 | 000071C9 MOVW R30,R24 Copy register pair |
14 | 000071CA STD Z+0,R18 Store indirect with displacement |
LG, Sebastian
Eros S. schrieb: > Fuses sind folgendermassen gesetzt: Wenn du schon einen Screenshot zeigst - was sehr begrüssenswert ist - warum zeigst du dann nicht alle Fuses sondern listest in kryptischen Hex-Werten deine Konfiguration?
>Der Bootloader springt nicht an Woraus schließen Sie das - aus (dem Fehlen) der UART-Meldung "Hallo ..."? Ich hätte erstmal zu Beginn des Bootloaders eine LED geschaltet.
Eros S. schrieb: > Allerdings hatte das auch keinen Zweck, > das Programm wird einfach nicht richtig ausgeführt... Woran stellest du das fest? "Geht nicht" ist nicht besonders aussagekräftig. Deinem Screenshot nach zu urteilen benutzt du einen Atmel Ice, damit kannst du ja ausführlich debuggen. Damit solltest du erkennen können wo und wie es nicht funktioniert.
Wastl schrieb: > Wenn du schon einen Screenshot zeigst - was sehr begrüssenswert > ist - warum zeigst du dann nicht alle Fuses sondern listest in > kryptischen Hex-Werten deine Konfiguration? Da hast du absolut Recht! Angehängt ein neuer Screenshot Sebastian W. schrieb: > Eros S. schrieb: >> Was mache ich falsch? > > Vielleicht setzt du IVSEL nicht innerhalb von 4 CPU-Zyklen? > MCUCR = temp | (1<<IVCE); > 000071BF LDI R24,0x55 Load immediate > 000071C0 LDI R25,0x00 Load immediate > 000071C1 LDD R18,Y+8 Load indirect with displacement > 000071C2 ORI R18,0x01 Logical OR with immediate > 000071C3 MOVW R30,R24 Copy register pair > 000071C4 STD Z+0,R18 Store indirect with displacement > MCUCR = temp | (1<<IVSEL); > 000071C5 LDI R24,0x55 Load immediate > 000071C6 LDI R25,0x00 Load immediate > 000071C7 LDD R18,Y+8 Load indirect with displacement > 000071C8 ORI R18,0x02 Logical OR with immediate > 000071C9 MOVW R30,R24 Copy register pair > 000071CA STD Z+0,R18 Store indirect with displacement > > LG, Sebastian Ich habe es aus dem gängigen Tutorial übernommen. Das IVSEL innert 4 CPU Zyklen gesetzt werden muss war mir klar, dachte aber schneller als direkt nach IVCE kann ich es ja nicht setzen. Gibts da eine effizientere Methode fürs Setzen der Bits? Wastl schrieb: > Eros S. schrieb: >> Allerdings hatte das auch keinen Zweck, >> das Programm wird einfach nicht richtig ausgeführt... > > Woran stellest du das fest? "Geht nicht" ist nicht besonders > aussagekräftig. Deinem Screenshot nach zu urteilen benutzt > du einen Atmel Ice, damit kannst du ja ausführlich debuggen. > Damit solltest du erkennen können wo und wie es nicht > funktioniert. "Geht nicht" ist vielleicht der falsche Ausdruck. "Geht nicht wie gewünscht" wäre passender. Beim Debuggen habe ich festgestellt, dass das Programm zwar korrekt anläuft, aber die Zeile
1 | uart_puts("Hallo hier ist der Bootloader\n\r"); |
ist problematisch, denn die while-Schleife wird nie erreicht. Sobald ich den Transmit Interrupt für die UART einschalte, scheint das Programm die Addresse für die entsprechende Serviceroutine nicht zu finden und dann resetted die CPU. Kommentiere ich die oben genannte Zeile aus, springt das Programm normal in die while-Schleife und wartet dort auf ein Zeichen, läuft also so weit normal. Sobald ich über das Terminal dann allerdings ein Zeichen sende, somit also einen Receive Interrupt auslöse, scheint das Programm an eine undefinierte Stelle zu springen und ist dann irgendwo, nur nicht da wo es sein sollte (siehe Screenshot)
Sebastian W. schrieb: > Vielleicht setzt du IVSEL nicht innerhalb von 4 CPU-Zyklen? Das war auch das Problem, ich habe es gerade gelöst. Einerseits habe ich es genau so gemacht, wie im Datenblatt angegeben. Als ich jedoch das Assembly Code Example mit dem Assembler Code aus meinem Programm verglichen habe, habe ich gesehen, dass sehr wohl mehr als 4 CPU Zyklen vergehen, bis IVSEL gesetzt wird. Das bedeutet die Interrupts landen wieder am Anfang der Flash Sektion, was beim Auslösen ebenjener dann problematisch ist. Das Problem hierbei war, dass ich zum Debuggen die Optimierung ausgeschaltet habe. Hab dann die Option "Optimize most" angewählt und siehe da, das Programm funktioniert wie gewünscht. Man lernt nie aus. Danke an alle Beteiligten für die Tipps.
:
Bearbeitet durch User
Eros S. schrieb: > Atmel644_Bootloader.7z (35,4 KB) Wenn ich dein Projekt kompiliere kommt bei mir die Warnung "F_CPU not defined for <util/delay.h>" Solltest du auf jeden Fall im Projekt (nicht in den Sources) definieren (F_CPU=irgendwas Sinnvolles), sonst wird der UART-I/O nicht funktionieren wie du es erwartest. Vielleicht sagst du uns auch mal was für eine Test-Hardware du verwendest?
Eros S. schrieb: > Das Problem hierbei war, dass ich zum Debuggen die Optimierung > ausgeschaltet habe. Damit du weiter ohne störende Optimierung debuggen kannst lagere die Vektor-Umbiegung in eine eigene Funktion aus, dann kannst du Funktions-lokal die Optimierung so einstellen wie du es brauchst. Etwa so:
1 | #pragma GCC push_options
|
2 | #pragma GCC optimize ("O3")
|
3 | void Set_IVTable (void) |
4 | {
|
5 | unsigned char temp; |
6 | /* Interrupt Vektoren verbiegen */
|
7 | char sregtemp = SREG; |
8 | cli(); |
9 | temp = MCUCR; |
10 | MCUCR = temp | (1<<IVCE); |
11 | MCUCR = temp | (1<<IVSEL); |
12 | SREG = sregtemp; |
13 | }
|
14 | #pragma GCC pop_options
|
Wastl schrieb: > Eros S. schrieb: >> Atmel644_Bootloader.7z (35,4 KB) > > Wenn ich dein Projekt kompiliere kommt bei mir die Warnung > > "F_CPU not defined for <util/delay.h>" > > Solltest du auf jeden Fall im Projekt (nicht in den Sources) > definieren (F_CPU=irgendwas Sinnvolles), sonst wird der UART-I/O nicht > funktionieren wie du es erwartest. > > Vielleicht sagst du uns auch mal was für eine Test-Hardware > du verwendest? Ich verwende ein eigens designtes Board, das ich universell für Prüfgeräte einsetze. Konnte das Projekt auch ohne define kompilieren, was mir aber aufgefallen ist beim UART Treiber, dass die Baud Rate falsch berechnet wird. Im Bootloader Tutorial wird für die Baud Rate folgendes definiert:
1 | #define BOOT_UART_BAUD_RATE 9600
|
Die Funktion uart_init schreibt mir dann ins UBRR0 Register den Wert 128, gemäss Datenblatt muss für die Baudrate 9600 aber der Wert 129 rein. Wenn man den define oben anpasst zu
1 | #define F_CPU 20000000UL
|
2 | #define BOOT_UART_BAUD_RATE UART_BAUD_SELECT(9600, F_CPU)
|
funktionierts dann. Weiss aber nicht, ob das nur mit dem Define der CPU Taktfrequenz zusammenhängt, denn mit UART_BAUD_SELECT_DOUBLE_SPEED funktionierts wieder nicht und es wird ein falscher Wert ins UBRR0 Register geladen.
Wastl schrieb: > Damit du weiter ohne störende Optimierung debuggen kannst > lagere die Vektor-Umbiegung in eine eigene Funktion aus, > dann kannst du Funktions-lokal die Optimierung so einstellen > wie du es brauchst. Interessant, das kenne ich so noch nicht. Habs gerade ausprobiert und funktioniert. Super Lösung.
Eros S. schrieb: > Konnte das Projekt auch ohne define kompilieren Weil dann ohne weitere Meldung ein Default-Wert gesetzt wird der natürlich nicht deinen Erwartungen entspricht. Ich schreib's nochmal: Wastl schrieb: > Solltest du auf jeden Fall im Projekt (nicht in den Sources) > definieren
Eros S. schrieb: > schneller als direkt nach IVCE kann ich es ja nicht setzen. Gibts da > eine effizientere Methode fürs Setzen der Bits? Direkt den gewünschten Wert in MCUSR schreiben. Das ist auch ohne Optimierung schnell genug. Nach einem Reset sind die Werte der anderen Bits ja bekannt. In meinem Bootloader verzichte ich auf UART-Interrupts. Dann braucht es auch keine Vektortabelle und kein IVSEL. Brauchst du Interrupts? LG, Sebastian
Eros S. schrieb: > Konnte das Projekt auch ohne define kompilieren, was mir aber > aufgefallen ist beim UART Treiber, dass die Baud Rate falsch berechnet > wird. Im Bootloader Tutorial wird für die Baud Rate folgendes definiert: > #define BOOT_UART_BAUD_RATE 9600 > > Die Funktion uart_init schreibt mir dann ins UBRR0 Register den Wert > 128, gemäss Datenblatt muss für die Baudrate 9600 aber der Wert 129 > rein. Wenn man den define oben anpasst zu > #define F_CPU 20000000UL > #define BOOT_UART_BAUD_RATE UART_BAUD_SELECT(9600, F_CPU) > > funktionierts dann. Weiss aber nicht, ob das nur mit dem Define der CPU > Taktfrequenz zusammenhängt, denn mit UART_BAUD_SELECT_DOUBLE_SPEED > funktionierts wieder nicht und es wird ein falscher Wert ins UBRR0 > Register geladen. Beitrag "UART Baudrate clever runden"
Sebastian W. schrieb: > In meinem Bootloader verzichte ich auf UART-Interrupts. Dann braucht es > auch keine Vektortabelle und kein IVSEL. Brauchst du Interrupts? Den Receive Interrupt halte ich schon für sehr sinnvoll. Wie weisst du sonst wann du das Datenregister auslesen musst wenn die CPU etwas empfängt?
Wastl schrieb: > Ich schreib's nochmal: > > Solltest du auf jeden Fall im Projekt (nicht in den Sources) > definieren Hab ich gemacht :)
Eros S. schrieb: > Den Receive Interrupt halte ich schon für sehr sinnvoll. Wie weisst du > sonst wann du das Datenregister auslesen musst wenn die CPU etwas > empfängt? Das sagt mir RXC0 in UCSR0A. In meinem Bootloader lese ich nur zu bestimmten Momenten Zeichen vom UART ein. LG, Sebastian
Eros S. schrieb: > Wie weisst du > sonst wann du das Datenregister auslesen musst wenn die CPU etwas > empfängt? Durch Lesen des Statusregisters und Prüfen des entsprechenden Flags. Interrupt Steuerung ist nicht zwingend .... hilft aber beim Empfangen enorm, beim Senden nicht so sehr. Eros S. schrieb: > Hab ich gemacht :) Deine nicht vorhandene Beratungsresistenz ehrt dich sehr und unterscheidet dich deutlich von anderen Leuten (Betonköpfen).
Sebastian W. schrieb: > In meinem Bootloader lese ich nur zu bestimmten Momenten Zeichen vom > UART ein. Und der Sender weiß, wann er senden darf? Ich würde das einfach "wie allgemein üblich" mit einem RX-Fifo machen.
Lothar M. schrieb: >> In meinem Bootloader lese ich nur zu bestimmten Momenten Zeichen vom >> UART ein. > Und der Sender weiß, wann er senden darf? Braucht er nicht wissen. Man muss halt im Empfänger nur jederzeit schnell genug pollen, dann geht das natürlich auch ohne Interrupts. Ist ja nicht so, dass der Bootloader nebenbei noch Pi auf zwei Millionen Stellen genau berechnet oder sowas. Nö, der hat eine einfache Aufgabe, zu deren Ausführung er nur maximal vier Quellen pollen muss, deren Behandlung jeweils trivial und in maximal einigen zehn Takten zu schaffen ist. Das geht ganz locker auch ohne die Verwendung von Interrupts zu lösen. Das ist, genau genommen, sogar etwas einfacher. Und es spart (übrigens besonders in C!) ganz erheblich Codesize.
Eros S. schrieb: > Den Receive Interrupt halte ich schon für sehr sinnvoll. Wie weisst du > sonst wann du das Datenregister auslesen musst wenn die CPU etwas > empfängt? Das Flag kann man auch pollen.
Ich habe auch mal einen Bootloader fuer einen Mega32/64 geschrieben. Und dabei auch keine Interrupts verwendet. Laden auch ueber die serielle Schnittstelle. mit 9600 baud. Da war ueppig Zeit zwischen den Zeichen. Auch mit CRC. Das Limit fuer die Kommunikation war eh das Flash. Das wollte nicht schneller. Jeder empfangene Block wurde grad ins Flash geschrieben. Die einzelnen Bloecke waren mit CRC gesichert, aber ob alles stimmt sieht man erst am Schluss. Heutzutage wuerde ich daher ein externes Flash, MagnetRAM oder EEPROM fuer den gesammten Code verwenden. Also erst die Kommunikation, bis vollstaendig, Ueberpruefungen, und dann erst programmieren.
:
Bearbeitet durch User
Purzel H. schrieb: > Ich habe auch mal einen Bootloader fuer einen Mega32/64 geschrieben. Und > dabei auch keine Interrupts verwendet. Laden auch ueber die serielle > Schnittstelle. mit 9600 baud. Da war ueppig Zeit zwischen den Zeichen. > Auch mit CRC. Das Limit fuer die Kommunikation war eh das Flash. Das > wollte nicht schneller. Hmmm. Die Classic-Megas besitzen einen eingebauten Write-Buffer für den Flash. Der ist in wenigen Takten/Byte beschreibbar. Nennenswerte Zwangspausen ergeben sich eigentlich nur in den Momenten, wenn Pages gelöscht werden oder der Writebuffer tatsächlich in's Flash geschrieben wird, also einmal pro Flash-Page. Dazu kommt noch, dass die Classic-Megas für große Teile des Flash RWW unterstützen. Zumindest für diese Teile können also selbst diese Zwangspausen für die Datenübertragung genutzt werden. Bei den neuzeitlichen AVR8 ist das leider alles weggespart worden. Kein Pagebuffer mehr und auch kein RWW mehr. Wohl deshalb, weil die meisten Bootloader-Frickler sowieso nicht in der Lage waren, diese Features adäquat zu nutzen...
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.