Hallo zusammen,
ich habe eine Anwendung, aus der ich mit
1
uint8_tsregtemp=SREG;
2
cli();
3
uint8_ttemp=MCUCR;
4
MCUCR=temp|(1<<IVCE);
5
MCUCR=temp|(1<<IVSEL);
6
SREG=sregtemp;
7
((void(*)())0x1E000)();//jump to bootloader
herausspringe, ohne Watchdog-Reset.
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.
Hat jemand eine Idee?
... to change the IVSEL bit:
a. Write the Interrupt Vector Change Enable (IVCE) bit to one.
1. Within four cycles, write the desired value to IVSEL while writing a
zero to IVCE.
Hmm, okay, habe ich falsch gesehen.
> ((void (*)())0x1E000)(); //jump to bootloader
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.
Geändert zu:
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?
S. L. schrieb:> 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.
S. L. schrieb:> Bei mir läuft das, auf einem ATmega1284P von '1809': zwei kurze> (Assembler-) Programme.
Wie springen Sie aus der Anwendung in den Bootloader, über normalen Jump
oder mit Watchdog Reset?
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.
(mein ATmega1284P läuft mit 1 MHz)
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.
Vielleicht sollten Sie Ihre beiden Programme vorstellen; es gibt ja
sicher noch weitere Mitleser, und die arbeiten höchstwahrscheinlich mit
C.
> ... der BP in 0xF000 löst nochmal aus, ich weiß nicht wieso.
Ich kenne mich damit nicht aus, hätte an dieser Stelle aber ein ungutes
Gefühl.
S. L. schrieb:> 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.
Tycho B. schrieb:> 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."
> Nun habe ich eine andere Frage: Kann es sein ...
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.