Forum: Mikrocontroller und Digitale Elektronik Atmega1284 Bootloader mit Interrupten resettet sich


von Tycho B. (asellus)


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?

von Adam P. (adamap)


Lesenswert?

Müsste man nach dem Code
1
MCUCR = temp | (1<<IVCE);
2
MCUCR = temp | (1<<IVSEL);

nicht auch noch das MCUSR löschen?
Sollte auch ohne gehen, sehe da jetzt keinen Fehler.

: Bearbeitet durch User
von Tycho B. (asellus)


Lesenswert?

Ich sehe in der Registeranzeige im Bootloader, dass IVCE gesetzt ist. 
Die Vektortabelle ist also verbogen

von S. L. (sldt)


Lesenswert?

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

von Tycho B. (asellus)


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.

von S. L. (sldt)


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.

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

In der Hoffnung, dass ich nicht wieder danebenliege, also:

Bei mir wird aus besagter Zeile
1
  b4:  e0 e0         ldi  r30, 0x00  ; 0
2
  b6:  f0 ee         ldi  r31, 0xE0  ; 224
3
  b8:  09 95         icall
Und die Wortadresse 0xE000 liegt beim ATmega1284 nicht im Bootbereich.

von Tycho B. (asellus)


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)

: Bearbeitet durch User
von S. L. (sldt)


Lesenswert?

Wie sehen die Fuse-Bytes aus?

von Tycho B. (asellus)


Lesenswert?

S. L. schrieb:
> Und dann sollte eigentlich noch die Frage nach dem Fuse-High-Byte
> folgen.
EXTENDED 0xFC
HIGH     0xB0
LOW      0xFF

Beitrag #7708394 wurde vom Autor gelöscht.
von S. L. (sldt)


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?

von Tycho B. (asellus)


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?

von S. L. (sldt)


Lesenswert?

> Wie springen Sie ...

jmp 0xF000

von Tycho B. (asellus)


Lesenswert?

Machen Sie noch mehr oder nur wie hier Interrupte deaktivieren und 
Vektortabelle verbiegen? Vielleicht sollte noch etwas gesichert werden?
1
cli();
2
uint8_t temp = MCUCR;
3
MCUCR = temp | (1<<IVCE);
4
MCUCR = temp | (1<<IVSEL);

von S. L. (sldt)


Angehängte Dateien:

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)

: Bearbeitet durch User
von Tycho B. (asellus)


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.

von S. L. (sldt)


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.

von S. L. (sldt)


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.

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
Noch kein Account? Hier anmelden.