Hallo,
ich habe mich nun endlich mal daran gewagt, einen Bootloader für meine
Projekte zu bauen.
Als Vorlage habe ich das wunderbare Tutorial hier aus dem Forum
verwendet:
http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung
Ich möchte den Bootloader nach und nach um einige Funktionen erweitern,
jedoch zunächst mal das Basis-Modell zum laufen bekommen.
Der Bootloader wird im Moment auf einem ATMega2560 mit 16 Mhz
ausgeführt.
Benutz wird Atmel Studio 6.1 als IDE und USBProg 3.3 mit AVRIPS
mkII-Clone.
Dem Linker habe ich auch schon mitgeteilt, das er mein Programm bitte
auf den Adressbereich -Ttext=0x7C00 verschiebt.
Die Fusebits sind auch gesetzt (BootRST und 1024W Size)
nachdem ich nun eine ganze Weile herumprobiert habe, und meine LED, die
ich eingebaut habe, endlich zuckt, habe ich gemerkt, das mein Bootloader
nur funktioniert, wenn ich die UART-Lib auskommentiere.
sobald ich mehr als den Init-part der Lib mit kompilieren lasse, hängt
das Programm (LED macht Nüschts und auch über den Serial-Port kommt
nichts an)
Hängt das hier vielleicht an den Interrupts, weil die UART-Lib arbeitet
ja mit Interrupts ?
die Interrupt-Vektoren habe ich 1:1 aus dem Tutorial übernommen und
verbiege sie eben so, wie es nötig sein sollte. Optimize steht im
Compiler auf -Os. So sollte er doch die geforderten 4 Takte für die
Vector-Bits schaffen.
Leider weiss ich nicht wo ich mir den Assembler-Code ansehen kann, den
mir das Studio generiert, um das zu prüfen.
Hat jemand noch eine Idee woran das liegen könnte ?
Ich möchte den Fehler gerne selbst finden. aber ich weiss nicht wo ich
ansetzen soll.
Vielen Dank und Grüße ^^
Marcel Peterkau schrieb:> Dem Linker habe ich auch schon mitgeteilt, das er mein Programm bitte> auf den Adressbereich -Ttext=0x7C00 verschiebt.
Was genau hast du wo genau eingetragen?
0x7C00 ist schon mal in jedem Fall die falsche Adresse.
Aus dem Datenblatt Seite 328 habe ich die Adresse.
Jetzt sehe ich aber gerade. Das ist ja die Adresse für den ATMega640
für den 2560 wäre 0x1FC00 richtig, oder ?
Im Screenshot sieht man, wo ich das eingetragen habe.
Ich habe es jetzt mit den neuen Einstellungen (0x1FC00) nochmal
probiert.
LED zuckt immer noch, aber wenn ich den UART wieder mit rein nehme,
gehts nicht.
Marcel Peterkau schrieb:> Ich habe es jetzt mit den neuen Einstellungen (0x1FC00) nochmal> probiert.
Das ist die Word-Adresse. So wie du die Einstellung vorgenommen hast,
wird aber die Byte-Adresse benötigt. Also entweder die Adresse
korrigieren, oder aber die Einstellung (mit Word-Adresse) unter "Memory
Settings" vornehmen.
Schön dass das Tutorial noch Anwendung findet, ist ja schon etwas
angestaubt... wenn ich Zeit habe muß ich es mal für das neue Atmel
Studio erweitern...
Ein Hinweis noch: man muß selbst darauf achten das der Programmcode <
Bootsize ist.
Öhm... das heisst, ich muss dann simpel die Word-Adresse 0x1FC00 mal 2
nehmen, dann bin ich bei Byte, oder ?
Und diese kann ich dann aber dort eintragen, wo jetzt mein
-Ttext=0x1FC00 drin steht, oder ?
Da käme dann nach Adam Riesling 0x1FC00 * 2 = 0x3F800 raus ?
Ich muss also -Ttext=0x3F800 eintragen...
Habe ich getestet, und siehe da. Er sendet mir brav den String per
uart_puts.
Allerdings tut er das dauernd in Schleife.
Obwohl am Ende des Bootloaders ein
Hier der aktuelle Quellcode.
Mit Optimize -Os wird er laut Atmel Studio so gross:
Program Memory Usage : 1014 bytes 0,4 % Full
sollte ja also in 1024 reinpassen, oder ?
EDIT: Rechtschreibfehler gebugfixt ;-)
Moment mal, wann genau sendet er den String?
Meinst du diesen:
1
uart_puts("start\r\n");
Was soll denn das Konstrukt
1
while(1)
2
{
3
return0;
4
}
bewirken? Wozu soll "return 0;" gut sein? Wohin soll er denn
zurückkehren?
Du hast ja schon eine Endlosschleife weiter oben:
1
while(boot_state!=BOOT_STATE_EXIT);
2
{
3
...
4
}
welche nur verlassen wird wenn du "q" drückst. Beim rausfallen aus der
Schleife werden die Interrupts wieder grade gebogen und zur Adresse
"0x0000" gesprungen. Die zusätzliche "while"-Schleife ist da sinnlos.
Tipp: Versuch doch erstmal den 2Hallo Welt"-Bootloader aus Kapitel 5.3
des Tutorials zum laufen zu bringen. Wenn das alles funktioniert, kanns
du das parsen der hex-Datei hinzufügen...
genau diesen String meine ich.
den sendet er ja am Anfang des Bootloaders einmal.
das Konstrukt mit dem while ? öhm, ja, gute Frage, wieso das return 0
drin steht..... -> gelöscht.
die Endlosschleife wird in der Tat nur verlassen wenn ich q drücke.
ABER ich frage ja weiter oben folgendes ab:
1
if(BOOTLOADER_INPUTPORT&(1<<BOOTLOADER_HWPIN))
2
{
3
boot_state=BOOT_STATE_EXIT;// Bootloader-Parser überspringen, wenn Bootloader-Enable high
4
#ifdef BOOTLED
5
BOOTLED_OUTPUTPORT&=~(1<<BOOTLED_HWPIN);//BootLED auf low ziehen (einschalten gg. VCC)
6
#endif // BOOTLED
7
}
ich habe bei dem Pin, der hier abgefragt wird, den internen Pullup
aktiv. solange er auf GND gezogen ist, sollte er ja direkt in die
besagte endlosschleiffe springen und in dieser bleiben bis ich q drücke.
Das tut er nicht. Er startet den Bootloader dauernd neu.
auch hier sendet er mir den String permanent ohne Pause. er ignoriert
die 1000ms delay und die while-schleife und sendet permanent den String
"Bootloader".
Das neu booten würde ich erstmal auf das "return 0" schieben. Du hast
aber noch einen Denkfehler: Die ganze Hauptschleife verhält sich wie
eine State-Machine. Neben der Entscheidung, ob die Hauptschleife
übersprungen wird (BOOT_STATE_EXIT) gibt es nur den Zustand des Parsens
(BOOT_STATE_PARSER). Ich habe das ganze mit eine fußgesteuerten
Schleife gemacht, damit spare ich mir noch einen State für das Menu,
z.B. "BOOT_STATE_MENU". Das ganze funktioniert dann eigentlich nur wenn
man schon während des startens "p" drückt, sonst springt der Bootloader
zum Hauptprogramm. Ich habe mir den Bootloader-Pin gespart.
Ich würde dir folgendes Empfehlen:
Implementiere noch einen State
1
#define BOOT_STATE_MENU 2
und starte in diesem Modus. Du mußt dann noch die Abfrage:
uart_puts("Hallo hier ist der echte Bootloader\n\r");
2
_delay_ms(2000);
Du hast also 2 Sekunden Zeit die Taste "p" zu drücken sonst springt der
Bootloader unweigerlich zum Hauptprogramm. Du hast dieses Delay ja
folgerichtig weggelassen weil du einen Pin zum signalisieren des
Bootvorganges benutzt. Du mußt aber eben noch einen neuen Zustand für
das "Menu" hinzufügen.
Also, das ganze ist so gedacht:
Gerät aus -> Pin auf LOW (Jumper) -> Einschalten -> Springt in den
Parser und wartet auf Anweisungen per UART
ODER
Gerät aus -> Pin offen (interner Pullup -> High) -> Einschalten ->
Überspringt den Paser und haut das Anwenderprogramm rein (Adresse
0x00000)
sollte doch dann so passen.
ich habe jetzt mal das return 0; auskommentiert. gleiches verhalten.
Ja, das einfache Programm zeigt das gleiche verhalten.
das while am Ende habe ich nur zu testzwecken rein gemacht, um zu sehen,
ober er den Bootloader wirklich resettet oder einfach nur mehrfach
durchläuft.
normalerweise sollte er ja am ende im while stehen bleiben. tut er aber
nicht. er sendet dauernd den ersten string. "start" - genau so bei dem
einfachen Programm. Er ignoriert bei dem einfachen Programm auch das
delay !
Das delay funktioniert eigentlich nur nicht wenn die Definition der
Taktfrequenz nicht vorhanden ist (F_CPU), aber die ist ja drin.
Irgendwas veranlasst den Controller zum Reset. Könnte auch ein
Hardwareproblem sein...
Als Hardware benutze ich ein Arduino Mega 2560-Board.
Natürlich ohne den Arduino Bootloader und mit ISP programmiert ;-)
ich habe einfach mal das gesammte Atmel-Studio-Projekt angehängt
(Version 6.1)
Wiegesagt, das ist das erste mal, das ich mich an einen Bootloader
gewagt habe, weshalb ich auch die Vorlage hier aus dem Forum 1:1 kopiert
habe und nur meinem Bedarf angepasst habe.
Aktuell ist das verhalten so:
Wenn ich den Bootloader-Enable-Pin (definition am Anfang der Hauptdatei)
auf low ziehe, sendet er einmal den Befehl
1
uart_puts("start\r\n");
und bleibt dann stehen. Er schaltet nichtmal die LED ein, die direkt
nach der Statusabfrage des Pins kommt.
Ist der Pin jedoch offen (durch internen Pullup auf High) dann sendet er
mir immer nur "st" auf den Serial-Port und startet sofort neu und sendet
wieder nur st. Sieht im HTerm dann so aus:
ststststststststststststststststststst so lange bis ich Reset drücke
oder den Strom weg nehme.
Marcel Peterkau schrieb:> Wenn ich den Bootloader-Enable-Pin (definition am Anfang der Hauptdatei)> auf low ziehe, sendet er einmal den Befehluart> _puts("start\r\n");> und bleibt dann stehen.
Weil das, was deine Hauptschleife sein soll, dann in Wirklichkeit eine
leere Endlosschleife ist (wegen eines bösen ';').
Marcel Peterkau schrieb:> Er schaltet nichtmal die LED ein, die direkt> nach der Statusabfrage des Pins kommt.
Logisch, das Einschalten der LED steht ja auch innerhalb des if, erfolgt
also bei einem High am Pin, nicht bei Low.
Marcel Peterkau schrieb:> Ist der Pin jedoch offen (durch internen Pullup auf High) dann sendet er> mir immer nur "st" auf den Serial-Port und startet sofort neu und sendet> wieder nur st.
Jetzt wird der Code deiner "Hauptschleife" (wieder wegen dem ';') nur
genau einmal abgearbeitet. Und dann verschiebst du die Vektoren bevor er
mit dem Senden fertig ist, so dass der nächste UDRE-Interrupt ins Leere
geht, was sich faktisch wie ein Reset des Programms auswirkt.
Very Big Facepalm
verdammich, jetzt gehts...
wie war das mit dem Wald und den Bäumen...
diese verdammte Semikolon habe ich irgendwie permanent übersehen.
Das mit der LED muss ich wieder zurecht frickeln. Bei meinem späteren
Projekt, wird diese LED gegen VCC geschalten. Leuchtet also ab Reset,
bis ich sie auf high setze.
Auf dem Arduino-Brett, was ich hier liegen habe ist das Mistding aber
gegen GND geschalten. Da hab ich wohl im Eifer des Gefechts ein bischen
was durcheinander geworfen, aber das schaff ich alleine ;-)
Und das mit den Interrupt-Vektoren ist mir jetzt auch klar !
VIelen Vielen Dank an alle Helfer !
Sobald ich den ersten Release meines Bootloaders fertig habe, werde ich
den natürlich auch hier veröffentlichen.
Immerhin gibt es dann eine kleine GUI für Windows dazu ;-)
Grüße Marcel aka Souko
Ist doch mit XON/XOFF schon drin, oder ?
Jetzt tut sich mir aber das nächste Problem auf.
die Gurke empfängt die Zeichen, welche vom PC kommen nicht richtig.
Ich habe inzwischen einen dritten Zustand definiert (#define
BOOT_STATE_START 2)
mit diesem Zustand wird mein Code auch Initialisiert. er springt mir
auch in die Hauptschleife und dann nach unten zu diesem Part hier:
1
/* Programmzustand: UART Kommunikation */
2
elseif(boot_state!=BOOT_STATE_PARSER)
3
{
4
switch((uint8_t)c)
5
{
6
case'p':
7
boot_state=BOOT_STATE_PARSER;
8
uart_puts("wait for Hex-File\r\n");
9
break;
10
case'q':
11
boot_state=BOOT_STATE_EXIT;
12
uart_puts("leave Bootloader!\r\n");
13
break;
14
default:
15
uart_puts("invalid char\r\n");
16
uart_putc(c);
17
break;
18
}
19
}
ich bekomme aber IMMER nur den default-Zustand zurück.
wenn ich z.B ein p sende (Hex 0x70) kommt als Antwort 0xF0
und was bedeutet das ((uint8_t)c) ? c ist doch unsigend char und das
sind doch 8 bit, oder ?
Schau dir mal bitte die Doku UART-Lib an:
http://homepage.hispeed.ch/peterfleury/group__pfleury__uart.html#ga1
Die uart_getc empfängt nicht nur das Zeichen (untere 8 bit), sondern
codiert in den oberen 8 bit noch Zustände wie Overflow, NoData und
Error.
Der cast auf (uint8_t)c soll dem Compiler sagen das er in diesem Fall
nur das Zeichen auswerten soll.
Warum ein falsches Zeichen empfangen wird weiß ich nicht, kann eine
fehlerhafte Baudrate sein.
Frage: Die Ausgabe
1
uart_puts("start\r\n");
kommt aber richtig?
Versuch mal ein "uart_putc" in der Ausgabe direkt nach "uart_getc"
einzubauen, also so:
1
...
2
c=uart_getc();
3
uart_putc(c);
4
...
und sag mir ob das richtige Zeichen angezeigt wird.
Zu XON/XOFF: Das Software-Handshake habe ich zwar eingebaut, ABER: Wenn
der Controller mit Zeichen zugeballert wird kommt er nicht mehr dazu das
XOFF zu senden, da der Sende-Interupt nicht mehr ausgeführt
wird...besser wär ein Hardware Handshake.
Moin,
also erstmal danke für eure Gedult hier, das ist ja Super ;-)
Ich habe gestern Abend noch ein bischen herum probiert, und genau deinen
VOrtschlag umgesetzt. Leider Bombardiert mich mein µC dann so dermassen
mit 0x00 über den Serial zu, das ich überhaupt nicht mehr zum Senden
komme.
Also habe ich mal die Baudrate auf 9600 verringert, und siehe da, er
frisst das 'p' und sagt "waiting for Hex-File"
Er kommt also nicht mit den 115200 baud klar, was mich wundert, da ja
aber alles was mir mein uC sendet einwandfrei ankommt.
Ich habe dann mal unter 9600 baud versucht zu programmieren, aber nach
ein paar erfolgreichen punkten kommt dann ganz viel #
Ich vermute mal das er sich da mit dem XON/XOFF verhaut. Da werde ich
wohl doch auf Hardware-Handshake gehen. Ist ja auch besser.
Aber nochmal zum Problem der Baudrate. Wieso schmecken meinem uC die
115200 nicht ?
Ich glaube ich weiss es.
Es liegt daran:
Mit dieser Formel aus der uart.h kommt 8,1805555 raus für UBRR
(((xtalCpu) + 8UL * (baudRate)) / (16UL * (baudRate)) -1UL)
Bei einem ATMega mit 16MHz und 115200 Baud ist der Wert laut Datenblatt
UBBRL=8 !
Nach dieser Formel (((Round(UBRR) +1) / UBRR+1)-1)*100 kommt ein Fehler
von
2,006 % heraus.
Da ist zuviel ! - Ich kann also die 115200 garnicht so einfach benutzen.
So ist es!
Das '#' kommt eigentlich nur wenn die Checksumme nicht stimmt. bei 9600
Baud sollte es aber keine Fehler geben. Irgendwie bekommt er scheinbar
einige Zeichen nicht mit. Bist du sicher das die Software-Flußsteuerung
auf dem PC aktiviert hast? Benutzt du Putty?
Für das Flashen noch folgender Hinweis: Der Parser ist nur für
Codegrößen < 65k ausgelegt. Für Größen >65K mußt du noch die
Record-Typen 2 bis 5 implementieren, also ungefähr hier:
1
/* Parse Zeilentyp */
2
casePARSER_STATE_TYPE:
3
hex_buffer[hex_cnt++]=(uint8_t)c;
4
if(hex_cnt==2)
5
{
6
uart_putc(XOFF);
7
hex_cnt=0;
8
hex_data_cnt=0;
9
hex_type=(uint8_t)hex2num(hex_buffer,2);
10
hex_check+=hex_type;
11
switch(hex_type)
12
{
13
case0:parser_state=PARSER_STATE_DATA;break;
14
case1:parser_state=PARSER_STATE_CHECKSUM;break;
15
case2:// Extended Segment Address -> ToDo
16
case3:// Start Segment Address -> ToDo
17
case4:// Extended Linear Address -> ToDo
18
case5:// Start Linear Address -> ToDo
19
default:parser_state=PARSER_STATE_DATA;break;
20
}
21
uart_putc(XON);
22
}
23
break;
Du mußt also Sprünge und Offsets implementieren. Ich glaube es gab schon
mal einen Thread wo das jemand gemacht hat.
Marcel Peterkau schrieb:> Ich habe gestern Abend noch ein bischen herum probiert, und genau deinen> VOrtschlag umgesetzt. Leider Bombardiert mich mein µC dann so dermassen> mit 0x00 über den Serial zu, das ich überhaupt nicht mehr zum Senden> komme.
Wenn du es ganz genau so umgesetzt hast, wie von Mario gezeigt, dann ist
das kein Wunder.
Das
1
uart_putc(c);
muss natürlich hinter das
1
if(!(c&UART_NO_DATA))
(nur für den Fall, dass du diesen Echo-Test nochmal brauchst)
OK,
dann werde ich mir das mit der Baudrate nochmal ansehen.
Interessant ist es aber, wie die jungs von Arduino das dann hin
bekommen, weil die unterstüzen ja auf all Ihren Boards die 115200 - auch
mit dem Mega-Board, welches ich hier als Hardware nutze.
Mit der Codegröße. Auch wenn mein Code kleiner als 65k ist, ich aber
damit ein .hex für einen Controller mit mehr als 65k Flash generiere,
dann hat er ja trotzdem die 2-5 Record-Typen schon in der Adresse, oder
?
Daher funktioniert der Upload wohl auf meinem ATMega2560 nicht. Liege
ich da richtig ?
und das mit dem Echo-Test habe ich heute morgen dann auch noch gemerkt
^^
ist ja klar, da er ja bei jedem Schleifendurchlauf dann auch den leeren
Puffer sendet..
Marcel Peterkau schrieb:> Mit der Codegröße. Auch wenn mein Code kleiner als 65k ist, ich aber> damit ein .hex für einen Controller mit mehr als 65k Flash generiere,> dann hat er ja trotzdem die 2-5 Record-Typen schon in der Adresse, oder> ?>> Daher funktioniert der Upload wohl auf meinem ATMega2560 nicht. Liege> ich da richtig ?
Das ist zumindest möglich. Die Implementation ist aber nicht so
schwierig. Es sind lediglich Offsets für das Schreiben der Pages. Du
mußt also in Abhängikeit der Flags einen Offset auf "flash_page" für das
nächste Schreiben setzen (und evtl. vorherige Daten schreiben, auch wenn
die Page noch nicht voll ist). In der Praxis ist wahrscheinlich nur Type
02 interessant (s. http://de.wikipedia.org/wiki/Intel_HEX), Type 03, 04
und 05 sollten nicht auftauchen...
Marcel Peterkau schrieb:> Interessant ist es aber, wie die jungs von Arduino das dann hin> bekommen, weil die unterstüzen ja auf all Ihren Boards die 115200 - auch> mit dem Mega-Board, welches ich hier als Hardware nutze.
Keine ahnung wie die das gemacht haben. Laut Baudratentabelle im
Datenblatt (Tabelle 21-12) sollte es bis 38.4k ohne Probleme gehen. 250k
geht auch sogar mit 0% Fehler :)