Hallo Leute,
ich verwende einen XMEGA256A3U von Atmel. Ich habe bereits einen
Bootloader geschrieben und diesen auch in den Flash unter der
Wordadresse 0x20000 hinterlegt. Wenn ich mit den AVRSTUDIO das BOOTRST
Fuse auf Bootloader stelle, dann startet nicht meine Application sondern
der Bootloader. Soweit so gut nun zu meinem Problem:
Wie kann ich den Bootloader aus der Application starten. Ich habe
gelesen das man dies mit einem Extended Indirect Jump (eijmp) macht.
Dies ist notwendig da ich mehr als 64kByte Flash habe.
ich habe dies im Internet bereits gefunden jedoch hat es nicht
funktioniert:
asm volatile (
"ldi r30, 0x02" "\n\t" // set up EIND
"sts 60, r30" "\n\t"
"ldi r30,0x00" "\n\t" //indirect call address
"ldi r31,0x00" "\n\t" //16bit address z
"eijmp" "\n\t"
Ich habe daraus eine Funktion erstellet und rufe diese auf.
void Jump_To_Bootloader(void)
{
#asm
cli ; Disable Interrupts
ldi r30,0x02 ; Set Up EIND to 0x02
sts 60,r30 ;
ldi r30,0x00 ; Indirect call adress
ldi r31,0x00 ; Indirect call adress
eijmp ; Jump to bootloader adress
#endasm
}
Jedoch scheint da ein Fehler drin zu sein. Ich kenne mich leider nicht
perfekt mit Assembler aus. Vielleicht kann mir jedmand ja einen Hinweis
geben
Hi
>Wie kann ich den Bootloader aus der Application starten. Ich habe>gelesen das man dies mit einem Extended Indirect Jump (eijmp) macht.>Dies ist notwendig da ich mehr als 64kByte Flash habe.
Wieso? Ein simpler 'jmp' kann 4MWord adressieren.
MfG Spess
Hat leider auch nicht funktioniert. Ich versuche es jetzt mal mit dem
Debugger. Jedoch habe ich mit dem Debugger noch so meine Probleme. Ich
downloade gerade das aktuelle AVR Studio.
Johann schrieb:> :-)>> Diese Zeile überfordert mich ein wenig :-)
Funktioniert aber. Ich habe mal den Einsprungoffset weggenommen
(also Einsprung direkt an den Anfang des Laders) und ein typedef
eingeführt:
Danke für die Info Jörg,
aber macht der eicall nicht einen Sprung und legt die Rücksprungadresse
auf den Stack ab so das er dann wieder zurückspringt und es dann zu
Problemen kommt?
Kannst Du vielleicht noch etwas zu diesen 2 Zeilen schreiben was diese
genau machen?
typedef void (*bldr_t)(void) __attribute__((noreturn));
const bldr_t jump_to_bootloader = (bldr_t)(BOOT_SECTION_START/2);
Johann schrieb:> aber macht der eicall nicht einen Sprung und legt die Rücksprungadresse> auf den Stack ab
Ja, macht er.
> so das er dann wieder zurückspringt und es dann zu> Problemen kommt?
Wer ist „er“? Wer springt denn da wann zurück? Dein Bootloader?
Warum sollte er?
Allerdings sollte der Bootloader den Stackpointer (re-)initialisieren.
> Kannst Du vielleicht noch etwas zu diesen 2 Zeilen schreiben was diese> genau machen?
C-Buch, Kapitel über Zeiger?
> typedef void (*bldr_t)(void) __attribute__((noreturn));
Vereinbart einen Datentypen (namens „bldr_t“) für einen
Funktionszeiger, wobei die Funktion keinen Rückgabewert hat (void),
ja nicht einmal überhaupt zurückzukehren gedenkt (attribute noreturn),
und keine Parameter übernimmt ((void)).
> const bldr_t jump_to_bootloader = (bldr_t)(BOOT_SECTION_START/2);
Bildet einen solchen Zeiger per Typecast aus einer Konstante.
das ist dann aber schon advance c.
In meinem Bootloader ist eine komplette Intitialisierungsroutine. Jedoch
bin ich mir gerade nicht sicher ob ich da den Stackpointer zurücksetze
ich versuche das nachschlagen.
Danke für die Erklärung. Mit Pointer auf Funktionen selber muste ich
noch nicht arbeiten. Aber ich werde mich da mal im Internet belesen.
Johann schrieb:> das ist dann aber schon advance c.
Nö, nur Zeiger bis zu Ende verstanden. ;-)
Allerdings gebe ich gern zu, dass das Abtrennen des Typs in einem
typedef hier wirklich sinnvoll ist.
Hi,
sollte es nicht reichen, wenn Johann den Offset in sein Konstrukt
einbaut und auf die word-address (das Teilen durch 2) umstellt -
(0x40000 + 0x1FC) / 2 = 0x200FE:
AVR 1916:
Internal firmware request: The user application can decide to start a
DFU session. This can be achieved by jumping to a specific address in
the boot loader firmware. The entry point to start a DFU session
initiated by a user application firmware jump is BOOT_SECTION_START +
0x1FC for all devices
Also:
1
voidJump_To_Bootloader(void)
2
{
3
#asm
4
cli;DisableInterrupts
5
ldir30,0x02;SetUpEINDto0x02
6
sts60,r30;
7
ldir30,0xFE;Indirectcalladress
8
ldir31,0x00;Indirectcalladress
9
eijmp;Jumptobootloaderadress
10
#endasm
11
}
Wobei ich bei "word-address" (jetzt auf 0x20000 angepasst) auf extrem
wackligen Boden stehe ...
Dieter Frohnapfel schrieb:> sollte es nicht reichen, wenn Johann den Offset in sein Konstrukt> einbaut und auf die word-address (das Teilen durch 2) umstellt -> (0x40000 + 0x1FC) / 2 = 0x200FE:
Zumindest mit dem AVR-GCC nicht: der hat nur 16 bit breite Zeiger.
> Internal firmware request: The user application can decide to start a> DFU session. This can be achieved by jumping to a specific address in> the boot loader firmware.
Setzt aber voraus, dass er einen DFU-Bootloader installiert hat. Ich
habe aber den Eindruck, dass es sein eigener Bootloader ist.
So Jung ich habe mit dem Debugger das ganze genau untersucht. Wenn ich
per RS232 ein Kommando an den XMEGA sende dann wird wirklich ein Sprung
zur Adresse 0x20000 ausgeführt.
Dort steht auch mein Bootloader drin. Dieser wird aber anscheinend nicht
richtig abgearbeitet. Ich kann mit dem Debugger die Bootsection anzeigen
dort steht auf jedenfall etwas drin. Nur bleibt der Mikrocontroller dann
hängen.
Wenn ich jedoch das BOOTRST Fuse auf Bootloader stelle, dann startet der
Booloader richtig.
Ich verwende eine RS232 Interrupt um die neuen RS232 Flash Daten zu
empfangen.
Johann schrieb:> Ich verwende eine RS232 Interrupt um die neuen RS232 Flash Daten zu> empfangen.
IVSEL gesetzt?
Ansonsten kann man einen Bootloader auch gepollt schreiben.
Du solltest bedenken, dass wenn du von der Applikation in den Bootloader
springst, dass alle Register so bleiben wie sie sind. Also würde ich
vorher einen Zustand herstellen, der quasi dem Reset entspricht. Also
alle Interrupts voher deaktivieren, RTC deaktivieren...
Arbeitest du im Bootloader mit Interrupts? Wenn ja musst du den
ISR-Vektor noch verbiegen, das kannst du über BOOTRST und IVSEL.
Ich arbeite in der Application Firmware und der Bootloader Firmware mit
mit Interrupts. Ich habe natürlich nicht den Interruptverktor verbogen
:-)
Ok dann werde ich IVSEL und BOOTRST konfigurieren.
Das ist doch alles total ... Man ist kurz vor dem Ziel und es geht
nichts. Das ist heute wieder echt so ein Tag :-(
Meiner setzt den CLOCk auf 32MHZ konfiguriert einen RS232 Receiver
Interrupt. In der Interruptroutine werden dann Daten empfangen.
In der RS232 Interruptroutine verwende ich noch einen Hardwaretimer.
Diesen benutze ich zum Abbruch der RS232 Routine falls nicht genügend
Daten kommen. Der Timer benutzt aber keine Interruptroutine.
Das Hauptprogramm ist das While(1)
Ich verstehe Atmel nicht. Ich könnte ja einfach aus der Application
herraus das BOOTRST Fuse verändern anschließnd einen Reset auslösen und
alles wäre wunderbar. Das hätte mich nur wenige Minuten gekostet jetzt
glüht mein Kopf und nichts geht.
Ich dachte die Einsprungadresse ist die Adresse 0x20000? Das ist doch
der Startbereich vom Bootloader. Ich dachte diese wird dann
abgearbeitet.
Oder liege ich da falsch? ich habe mal meinen Bootloader angehängt
Johann schrieb:> Ich könnte ja einfach aus der Application herraus das BOOTRST Fuse> verändern anschließnd einen Reset auslösen und alles wäre wunderbar.
Der Sinn einer Fuse ist es aber, nun gerade nicht durch die
Applikation änderbar zu sein.
Nur, weil du gerade deine Software nicht im Griff hast, brauchst du
jetzt nicht die Schuld beim Hardwarehersteller suchen …
Es steht dir ja völlig frei, die Fuse immer zu setzen und bei Bedarf
einfach per Watchdog einen Reset zu veranlassen. Dann hast du das
Problem halt andersrum, der Bootloader muss dann entscheiden, wann
und wie er die Applikation anspringen will.
Johann schrieb:> ich habe mal meinen Bootloader angehängt
Sourcecode wäre einfacher zu entziffern …
> Ich dachte die Einsprungadresse ist die Adresse 0x20000?
Ja, ist sie auch.
Sorry, aber der Frust sitzt momentan sehr tief. Ich bin für jede Hilfe
sehr dankbar
Ich habe mal das Main-File beigefügt und meine Initialisierungsroutine.
Johann schrieb:> Der Programm Counter bleibt bei 0x2021E stehen. Als wenn es eine> Endlosschleife ist wie bei While(1)
Ist es ja auch, das ist deine while(1)-Schleife aus main(). Was
sollte denn in dieser sonst noch passieren?
Alles, was passiert, muss ja über einen Interrupt passieren.
Am Einsprungpunkt des Codes erfolgt ein Stück Initialisierung,
dann ein Sprung auf Byteadresse 0x4043a:
1
…
2
(Hier wird übrigens SP initialisiert)
3
402a8: ef ef ldi r30, 0xFF ; 255
4
402aa: ed bf out 0x3d, r30 ; 61
5
402ac: ef e5 ldi r30, 0x5F ; 95
6
402ae: ee bf out 0x3e, r30 ; 62
7
402b0: c0 e0 ldi r28, 0x00 ; 0
8
402b2: d0 e3 ldi r29, 0x30 ; 48
9
402b4: 1c 94 1d 02 jmp 0x4043a ; 0x4043a
Dort steht offenbar main():
1
4043a: d7 d1 rcall .+942 ; 0x407ea
2
4043c: ff cf rjmp .-2 ; 0x4043c
3
4043e: ff cf rjmp .-2 ; 0x4043e
Die zweite Endlosschleife scheint dein Compiler (welcher auch immer
es ist, hast du uns nicht verraten) nach main() hinzuzufügen, die
erste ist deine eigene.
0x407ea ist die Init-Routine, die dein Compiler auch stur so wie
geschrieben aufdröselt:
So ich habe mir das List-File noch mal genauer angeschaut.
Dort steht in Adresse 0x020000 der Resetvector vom Bootloader. Demnach
habe ich mir die Resetroutine angeschaut. Dort wird auf jedenfall das
IVESL Bit 1 1 gesetzt und somit Interrupt Vector im Bootbereich
platziert.
Durch den Debugger kann ich auch feststellen das ich im Bootloadebereich
bin. Jedoch wenn ich ein Kommando per RS232 Sende komme ich nicht in die
RS232 Routine vom Booloader.
Johann schrieb:> komme ich nicht in die RS232 Routine vom Booloader.
PMIC-Setup?
Ich müsste mir die Details für dieses Teil jetzt im Datenblatt
durchwälzen. Das wäre aber potenziell ein Unterschied zwischen dem,
was dir deine Applikation beim Einsprung in den Bootloader hinterlässt
und dem, was du beim Anspringen des Bootloaders per Reset hast.
Ich würde wohl nach möglichster Möglichkeit versuchen, in einem
Bootloader nur minimal an der Hardware herumzufummeln (nur das
wirklich für den Loader nötige) und ohne Interrupts auszukommen. Ob
du nun da in einem while(1) im main() Däumchen drehst oder an der
Stelle den UART-Rx pollst, bleibt sich schließlich gleich.
Jörg Wunsch schrieb:> PMIC-Setup?
Lass dir doch mal die PMIC-Register dumpen einerseits wenn du die
BOOTRST-Fuse gesetzt hast und andererseits wenn dein Loader von der
Applikation aus angesprungen worden ist.
Wenn mit AVR das Fuse-Bit BOOTRST veränder, so das er aus dem Bootloader
startet (beim Power Up) dann funktioniert ja auch die RS232
Interruptroutine. Ich kann dann auch wieder zurückspringen auf dem
Application bereich und auch dort funktioniert dann die RS232-Routine.
Ich werde mal eine LED Toggeln wenn ich im Bootloader bin. Und eine 2
LED einschalten wenn ich im Bootloader in die RS232 Routine kommen. -->
Oldscool halt.
Ok ich habe den Code geändert. Wenn die XMEGA Bootloader Initialisierung
abgeschlossen ist schalte ich eine LED an. Diese leuchtet auch. Demnach
muss der XMEGA vollständig initialisiert sein. Jedoch wenn ich einen
RS232 Befehl sende wird die 2. LED in der USART Interruptserviceroutine
nicht gesetzt.
Dies ist schon ein Hinweis das mit dem Interrupt etwas nicht in Ordnung
ist. Da habe ich wenigstens etwas wo ich den Fehler suchen kann.
Johann schrieb:> Demnach muss der XMEGA vollständig initialisiert sein.
Ja klar, du bist ja schließlich in deiner while(1)-Schleife in main().
Wie ich schon schrob: guck dir den Interruptcontroller an.
Ich konnte leider nichts finden.
Was mir jedoch aufgefallen ist. Wenn ich aus der Application den Jump in
den Bootloader mache, dann komme ich auch in den Bootoader rein und eine
LED wird angeschaltet.
Wenn ich jedoch anschließend die Betriebsspannung vom Board entferne und
anschließend diese wieder aktiviere, dann bin ich wieder im Booloader
und die Interruptserviceroutine von der USART funktioniert und alles
geht :-(
Nach einen Power Up sollte doch die Applikation wieder starten und nicht
der Bootloader. Das BOOTRST Fuse Bit steht auf Application und wird auch
nicht von mir verändert.
So ich HABE DEN FEHLER GEFUNDEN:
Ich habe aus der USART_INTERRUPT_SERVICE_ROUTINE den Sprung zum
Bootloader gemacht. Das scheint so nicht zu gehen oder ich habe etwas
nicht richtig gelöscht.
Wenn ich in der Interrupt Service Routine nur ein globales Flag setze
und dieses in der Mainroutine abfrage und dann beim gesetzten Flag den
Sprung zum Bootloader ausführe dann geht es auch sofort.
Mir ist jedoch aufgefallen das wenn ich im Bootloader bin und die
Versorgungspannung ausschalte und anschließend wieder Einschalte bootet
der Mikrocontroller immer automatisch aus dem Booloaderbereich. Ist das
eine Sicherheitsfunktion so das ich aktive erst wieder den
Bootloaderbereich verlassen muss?
Dies ist eigentlich sehr gut, denn wenn beim Flashen der Strom ausfällt
könnte ich in aus der Application herraus nicht mehr in den Bootloader
springen, da die Application ja bereits gelöscht ist und nur bis zu
einem gewissen Prozentsatz neu geflasht wurde.
Solange du in einer ISR bist sind globale Interrupts aus (ISRs sind
normalerweise nicht unterbrechbar)
Und wenn du aus der ISR einen Sprung machst, denkt der Controller das du
immer noch in der ISR bist.
Der ASM-Befehl RETI aktiviert beim Beenden der ISR wieder die globalen
Interrupts.
Ich kenne mich mit den XMegas nicht aus, die haben ja so ein tolles
Eventsystem.
Bei den normalen Megas, sollte eigentlich ein sei() reichen, was du
vermutlich in deinem Bootloadercode auch tust.
Vor deinem Sprung sollte man alle ISRs deaktivieren.
Und noch was, eigentlich ist die elegante Methode einen Reset
auszulösen, den Watchdog ablaufen zu lassen. Danach wird der Controller
auch Hardwareseitig komplett neu initialisiert.
Du hast ja geschrieben das bei einem Reset bei dir zuerst der Bootloader
startet.
Christian K. schrieb:> Ich kenne mich mit den XMegas nicht aus, die haben ja so ein tolles> Eventsystem.
Insbesondere haben sie einen Interruptcontroller (wies ich nicht
oben ausdrücklich darauf hin?), und der denkt natürlich auch, dass
die zugehörige ISR nach wie vor in Bearbeitung ist …
Christian K. schrieb:> Solange du in einer ISR bist sind globale Interrupts aus (ISRs sind> normalerweise nicht unterbrechbar)
Nicht beim XMEGA. Dort kann ein höher priorisierter Interrupt einen
niedriger priorisierten unterbrechen. Aber Du hast schon Recht,
irgendwann muss schon ein passendes reti kommen, um die noch laufende
ISR abzuschließen.
Mein Resetvektor im Bootloader verbiegt ja auch die Interrupttabelle.
Aber über welchen Befehl kann ich dann das RETI löschen so das mein
Bootloader nicht mehr denkt das er noch im Interrupt ist?
Ein RETI macht das gleiche wie ein RET, oberstes Element des Stacks in
den PC laden und zusätzlich das I-Flag im SREG setzen.
Ich konnte jetzt keine Informationen finden ob das RETI bei einem XMega
mehr macht.
Da ein XMega ein Interrupt Controller hat, würd ich dir mal raten das
Disassembly einer ISR anzuschauen, um zu checken ob am Ende der ISR vor
dem RETI noch irgendwelche anderen Registerbits verändert werden.
Johann schrieb:> Aber über welchen Befehl kann ich dann das RETI löschen so das mein> Bootloader nicht mehr denkt das er noch im Interrupt ist?
Warum zum Geier™ tust du nicht endlich mal, was ich dir die ganze
Zeit lang predige, und guckst dir das Kapitel zum Interruptcontroller
im Datenblatt an? Ich mein', ich klapp' das Ding auf, und es guckt
mich ein Statusregister an, bei dem sowas steht:
1
Bit 0 LOLVLEX: Low-level Interrupt Executing
2
This flag is set when a low-level interrupt is executing or when the interrupt handler has been interrupted by an
3
interrupt from higher level or an NMI. The flag will be cleared when returning (RETI) from the interrupt handler.
Gut zu wissen das ein RETI bei einem XMega mehr macht.
Im Offiziellen "AVR Instruction Set Manual" und im Kapitel Instruction
Set Summary eines XMegas-Datenblatts gibt es keinen Verweis darauf.
@Johann
Jetzt hab ich mir mal das Datenblatt vom Xmega A3U geladen, aber ich
find die zitierte Passage nicht. Auch die Volltextsuche bring nichts.
EDIT:
Atmel hat bei den Datenblättern der XMegas outgesourced.
Im Xmega AU hab ich es gefunden.
spess53 schrieb:> Die interessanten Sachen stehen im entsprechenden Manual:
Ja, diese doppelten Datenblätter sind Scheibenkleister. Wenn sie
wenigstens im per-device datasheet dann die unnötigen Kapitel-stubs
gleich weggelassen hätten, aber so erwecken die Dinger immer noch
den Eindruck, als würden sie alles abdecken, obwohl man in Wirklichkeit
darin nur einige ganz wenige Details findet, während der große Rest im
Familien-Datenblatt steht.
Aber wenn man irgendwie ernsthaft was mit einem Xmega macht, sollte
man das eigentlich kennen.
Christian K. schrieb:> Im Offiziellen "AVR Instruction Set Manual" und im Kapitel Instruction> Set Summary eines XMegas-Datenblatts gibt es keinen Verweis darauf.
Es gibt auch keinen Hinweis drauf, dass bspw. ein OUT auf ein
bestimmtes Portregister ein Pin toggeln kann oder ein Interruptflag
löschen.
Spätestens, wenn man aber mal irgendwie versucht nachzudenken, wie sie
die Interruptpriorisierung gelöst haben können, wird einem klar, dass
der Interruptcontroller auf irgendeine Weise wissen muss, dass gerade
eine ISR in Bearbeitung ist, denn nur so kann er verhindern, dass bei
global zugelassenen Interrupts ein Interrupt gleicher oder niederer
Priorität die aktuelle ISR unterbricht.
Hallo Leute,
ich versuche im Moment den Bootloader mit der Version 1.04 auf meinem
AtxMega63A3U zum Laufen zu bekommen.
Ich möchte den bootloader aus meiner Applikation starten.
Die Abfrage des IOs zum Starten des Bootloaders habe ich ausgebaut.
So dass dieser starten sollte. Kompiliert habe ich mit IAR.
Die Fusebits habe ich auf die Applikaiton gestellt.
In meiner Applikation benutze ich die USB-Schnittstelle um mit dem PC zu
kommunizieren.
Mir fehlt jetzt noch der Jump aus der Applikation in den Bootloader.
Ich habe zwar schon einiges hier gelesen aber ich kriege es nicht hin.
Hat jemand dies schon zuverlässig lösen können?
Danke.
Gruß,
Georg.
Georg X. schrieb:> Hat jemand dies schon zuverlässig lösen können?
Bitte öffne einen eigenen Thread, dein Problem hat nur den Namen
gemeinsam mit dem Problem, welches in diesem Thread hier behandelt
worden ist (verklemmter Interruptcontroller).
Ach, und beschreibe dein Problem umfassend, insbesondere was du
im Moment tust.