Im Bootloader angekommen, funktionier alles, bis der erste Interrupt kommt. Dann, wenn ein Interrupt ausgelöst, aber noch bevor die ISR angesprungen wird, springt der Programmzeiger zum Anfang des Bootloaders.
Ohne den Sprung aus der Anwendung, also wenn der Bootloader nach einem Reset startet, funktionier alles normal. Auch wenn man aus dem Bootloader in die Anwendung ohne Reset mit
1
cli();
2
temp=MCUCR;
3
MCUCR=temp|(1<<IVCE);
4
MCUCR=temp&~(1<<IVSEL);
5
((void(*)())0x0000)();
springt, dann funktioniert die Anwendung. Nur eben aus der Anwendung in den Bootloader, da funktionieren die Interrupte nicht.
Ich vermute, ich sollte aus der Anwendung mit einem Watchdog-Reset in den Bootloader springen. Aber es geht generell um das Verständniss was da schiefläuft. Vor Allem da das Springen aus dem Bootloader in die Anwendung scheinbar funktioniert.
Sind Sie sicher, dass dort eine Byte-Adresse stehen muss? Meine C-Kenntnisse sind leider nur rudimentär (wie Sie oben erkennen konnten).
Und dann sollte eigentlich noch die Frage nach dem Fuse-High-Byte folgen.
Ja, das stimmt. Da der Opcode 16-bit breit ist, macht es keinen Sinn, zu Byte-Adressen zu springen. Ich habe mir das angeschaut:
1
2
((void (*)())0x1E000UL)(); //jump to bootloader
3
00000910 LDI R30,0x00 Load immediate
4
00000911 LDI R31,0xE0 Load immediate
5
}
6
00000912 POP R28 Pop register from stack
7
((void (*)())0x1E000UL)(); //jump to bootloader
8
00000913 IJMP Indirect jump to (Z)
Es passiert wirklich nicht das, was ich vermutet habe. IJMP benutzt r31:r30, und so wird mit E000 beladen. Die 1 vorne fliegt einfach weg.
IJMP ist ein Sprung innerhalb der unteren 64K words und der Bootloader ist eigentlich an der Adresse 0xF000 (Wordadresse). Ich lande bei 0xE000, dann werden die "nop" durchgegangen bis der program counter bei 0xF000 landet und der Bootlader startet.
Jetzt habe ich es geändert, aber das Problem mit dem Reset besteht weiterhin, weil eben, obwohl bei 0xE000 gelandet, wurde nach 4096 NOPs trotzdem Bootloader gestartet.
Bei mir läuft das, auf einem ATmega1284P von '1809': zwei kurze (Assembler-) Programme.
Stellen sich also zwei Fragen: wie sehen Ihre Programme aus (wobei ich sicher Mühe haben würde, das C zu verstehen), und wie stellen Sie eigentlich fest, dass der erste Interrupt im Bootloader, nach dem Sprung aus dem Anwenderprogramm, verworfen wird und stattdessen ein Reset erfolgt?
wie stellen Sie
eigentlich fest, dass der erste Interrupt im Bootloader, nach dem Sprung
aus dem Anwenderprogramm, verworfen wird und stattdessen ein Reset
erfolgt?
Ich habe zwei Breakpoints gesetzt, einen an die Adresse 0xF000, den Anderen an den Eintrag der Timer3-ISR in der Vektortabelle. Ich lasse die Anwendung laufen, springe mit einem Befehl in den Bootloader. Dort wird zuerst der Breakpoint 0xF000 ausgelöst. Ich lasse den uC weiterlaufen, der BP in 0xF000 löst nochmal aus, ich weiß nicht wieso. Ist aber eine andere Baustelle, denke ich. Dann geht's weiter zum CRC-Check. Den sehe ich durch Toggeln einer Leitung auf einem Oszi. Danach geht es in die main Boot-Schleife. Das sehe ich am Toggeln einer zweiten Leitung am Oszi. Nun verbleibt der uC in der Schleife für ca. 8 Sekunden und springt danach wieder in die Anwendung, das wäre das normale Startverhalten falls kein weiteres Kommando innerhalb dieser 8 Sekunden kommt. 8 Sekunden sind zu lang, ich habe es nur für's manuelle Testen so eingestellt. Wenn innerhalb dieser 8 Sekunden ein spezielles Kommando über die Schnittstelle kommt (OWI, realisiert mit InputCapture vom Timer3), so soll der Bootloader in der Schleife verbleiben und auf Firmware warten. Wenn ich nun ein Kommando sende, egal welches, dann löst der 0xF000-Breakpoint aus. Ohne diesen Breakpoint sehe ich, dass die komplette Initialisierung durchgelaufen ist und der CRC-Check beginnt eher der Timer3-Breakpoint ausgelöst wird. Das ist für mich ein Hinweis, dass der Bootloader neu startet. Da der Input Capture jede Flanke detektiert und mit OWI es einige gibt, denke ich, dass die erste Flanke den Reset auslöst und die folgende löst die ISR aus, deswegen kommt der Breakpoint am Timer3 erst mitten im CRC-Check.
Das Umschalten von MCUCR erfolgt bei mir ausschließlich im Bootloader.
Ihre obige Erläuterung ist mir, ich muss es leider gestehen, zu komplex.
Kurz zu meinem Vorgehen: ich verwende GPIOR0, das ja bei einem Reset auf 0 gesetzt wird ('Initial Value'); ich setze es im Anwenderprogramm auf 3, springe in den Bootloader, wo es auf vier LEDs angezeigt und im Timer1-Interrupt hochgezählt wird.
Siehe Anhang - falls Sie Assembler besser lesen können als umgekehrt ich C.
Man kann aber in diesem Beispiel nicht feststellen, ob der Bootloader sich ein mal resettet hat, oder? Wenn nach erstem Overflow ein Reset auftritt und der zweite overflow-Interrupt normal funktioniert, dann kann man das Verhalten, welches ich beschrieben habe, nicht nachstellen.
Man kann aber in diesem Beispiel nicht feststellen,
ob der Bootloader sich ein mal resettet hat, oder?
Die LED-Anzeige im Bootloader beginnt immer mit 4 (0100), und zählt dann hoch bis 11 (1011), dann blinkt wieder die Anwender-LED - bei einem Reset würde mit 0 (0000) begonnen, so wie es beim Einschalten der Fall ist.
Ich kenne mich damit nicht aus, hätte an dieser Stelle aber ein ungutes
Gefühl.
Das Gefühl trügt nicht) Ich habe den Fehler gefunden und das war auch ein Symptom davon.
Beim Sprung aus der Anwendung in den Bootloader habe ich die Interrupte nicht deaktiviert. Einer, der in der Hauptanwendung benutzt wird, aber nicht im Bootloader, war aktiv und löste auch aus. Es gab zu ihm aber keine ISR. Jeder Vektor ohne ISR springt zu dieser Zeile:
1
0000F06A RJMP PC-0x006A Relative jump
und damit zum Anfang des Bootloaders, und das habe ich als Reset beobachtet.
Nun habe ich eine andere Frage: Kann es sein, dass alle Pages, die ich aus dem Bootloader in der Applikationssektion programmiert habe, erst nach einem Hardware-Reset vom uC als Code gelesen werden können? Wenn ich mit
1
cli();
2
temp=MCUCR;
3
MCUCR=temp|(1<<IVCE);
4
MCUCR=temp&~(1<<IVSEL);
5
((void(*)())0x0000)();
in die Anwendung springe, dann läuft der Instruction Pointer durch den kompletten Flash, ohne dass irgend etwas passiert. Das sehe ich, weil ich an der adresse 0xF000-1, also 0xEFFF, einen Breakpoint gesetzt habe und dieser auslöst. Wenn ich das Board danach resette, dann startet der Bootloader normal, springt in die Anwendung, so wie er soll, und die Anwendung ist plötzlich da und startet.
Kann es sein, dass alle Pages, die ich
aus dem Bootloader in der Applikationssektion programmiert habe, erst
nach einem Hardware-Reset vom uC als Code gelesen werden können?
Nö, es reicht, das RWWSB zu löschen:
"24.8.6 Prevent Reading the RWW Section During Self-Programming
...
Before addressing the RWW section after the programming is completed, the user software must clear the RWWSB by writing the RWWSRE."
Die Antwort, wie von Peter Dannegger gegeben, lässt sich auch im Beispielprogramm des Datenblattes und ebenso in meinem gezeigten Programm finden - beide allerdings in Assembler.