Atmega1284 Bootloader mit Interrupten resettet sich

OP #7708120
Lesenswert?

Hallo zusammen,

ich habe eine Anwendung, aus der ich mit

1
uint8_t sregtemp = SREG;
2
cli();
3
uint8_t temp = 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?

OP #7708290
Lesenswert?

1
  cli();
2
0000F4CA  CLI     Global Interrupt Disable 
3
  uint8_t temp = MCUCR;
4
0000F4CB  IN R24,0x35    In from I/O location 
5
  MCUCR = temp | (1<<IVCE);
6
0000F4CC  MOV R18,R24    Copy register 
7
0000F4CD  ORI R18,0x01    Logical OR with immediate 
8
0000F4CE  OUT 0x35,R18    Out to I/O location 
9
  MCUCR = temp | (1<<IVSEL);
10
0000F4CF  ORI R24,0x02    Logical OR with immediate 
11
0000F4D0  OUT 0x35,R24    Out to I/O location 
12
  SREG = sregtemp;
13
0000F4D1  OUT 0x3F,R25    Out to I/O location

In "0000F4CE OUT 0x35,R18" wird IVCE gesetzt und in "0000F4D0 OUT 0x35,R24" IVSEL. Dazwischen liegt nur ein ORI-Befehl, also nur 1 Taktzyklus.

#7708306
Lesenswert?

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.

OP #7708348
Lesenswert?

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:

1
((void (*)())0xF000UL)(); //jump to bootloader
2
00000910  LDI R30,0x00    Load immediate 
3
00000911  LDI R31,0xF0    Load immediate 
4
00000912  POP R28    Pop register from stack 
5
((void (*)())0xF000UL)(); //jump to bootloader
6
00000913  IJMP     Indirect jump to (Z)
Beitrag #7708394 wurde von einem Moderator gelöscht.
#7708560
Lesenswert?

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?

OP #7708572
Lesenswert?

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?

#7708598
Lesenswert?

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)

Angehängte Dateien:
OP #7708654
Lesenswert?

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.

#7708716
Lesenswert?

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.

#7708780
Lesenswert?

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.

OP #7710248
Lesenswert?

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.

#7710268
Lesenswert?

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."

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren