<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mario</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mario"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Mario"/>
	<updated>2026-04-06T09:03:11Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=93398</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=93398"/>
		<updated>2016-07-12T06:50:09Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung! Bitte unbedingt die Bezeichnung des SFRs für das Umschalten der Interruptvektoren beachten: bei Atmega88: MCUCR, bei Atmega8: GICR (s. auch etwas weiter unten)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt vom Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0&lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uint16_t i;&lt;br /&gt;
	uint8_t sreg;&lt;br /&gt;
	&lt;br /&gt;
	/* Disable interrupts */&lt;br /&gt;
	sreg = SREG;&lt;br /&gt;
	cli();&lt;br /&gt;
	&lt;br /&gt;
	eeprom_busy_wait ();&lt;br /&gt;
	&lt;br /&gt;
	boot_page_erase (page);&lt;br /&gt;
	boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
	{&lt;br /&gt;
		/* Set up little-endian word. */&lt;br /&gt;
		uint16_t w = *buf++;&lt;br /&gt;
		w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
		&lt;br /&gt;
		boot_page_fill (page + i, w);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
	boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
	&lt;br /&gt;
	/* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
	/* to the application after bootloading. */&lt;br /&gt;
	boot_rww_enable ();&lt;br /&gt;
	&lt;br /&gt;
	/* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
	SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
	uint8_t  i;&lt;br /&gt;
	uint16_t val = 0;&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
	{&lt;br /&gt;
		uint8_t c = ascii[i];&lt;br /&gt;
		&lt;br /&gt;
		/* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
		if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;&lt;br /&gt;
		else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
		else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
		&lt;br /&gt;
		val = 16 * val + c;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return val;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void write_page(uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(100);&lt;br /&gt;
	program_page(page, buf);&lt;br /&gt;
	memset(buf, 0xFF, sizeof(SPM_PAGESIZE));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
        /* Intel-HEX Zieladresse */&lt;br /&gt;
	uint32_t        hex_addr = 0,&lt;br /&gt;
        /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
	hex_addr_offset = 0,&lt;br /&gt;
	/* Zu schreibende Flash-Page */&lt;br /&gt;
	flash_page = 0;&lt;br /&gt;
	/* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
	uint16_t        c = 0,&lt;br /&gt;
	/* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
	hex_check = 0,&lt;br /&gt;
	/* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
	flash_cnt = 0;&lt;br /&gt;
	/* temporäre Variable */&lt;br /&gt;
	uint8_t         temp,&lt;br /&gt;
	/* Flag zum steuern des Programmiermodus */&lt;br /&gt;
	boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
	/* Empfangszustandssteuerung */&lt;br /&gt;
	parser_state = PARSER_STATE_START,&lt;br /&gt;
	/* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
	flash_page_flag = 1,&lt;br /&gt;
	/* Datenpuffer für die Hexdaten*/&lt;br /&gt;
	flash_data[SPM_PAGESIZE],&lt;br /&gt;
	/* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
	hex_cnt = 0,&lt;br /&gt;
	/* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
	hex_buffer[5],&lt;br /&gt;
	/* Intel-HEX Datenlänge */&lt;br /&gt;
	hex_size = 0,&lt;br /&gt;
	/* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
	hex_data_cnt = 0,&lt;br /&gt;
	/* Intel-HEX Recordtype */&lt;br /&gt;
	hex_type = 0,&lt;br /&gt;
	/* empfangene HEX-Checksumme */&lt;br /&gt;
	hex_checksum=0;&lt;br /&gt;
	/* Funktionspointer auf 0x0000 */&lt;br /&gt;
	void            (*start)( void ) = 0x0000;&lt;br /&gt;
	&lt;br /&gt;
	/* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
	memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
	memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren verbiegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
	uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) );&lt;br /&gt;
	sei();&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(2000);&lt;br /&gt;
	&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		c = uart_getc();&lt;br /&gt;
		if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
		{&lt;br /&gt;
			/* Programmzustand: Parser */&lt;br /&gt;
			if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch(parser_state)&lt;br /&gt;
				{&lt;br /&gt;
					/* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
					case PARSER_STATE_START:&lt;br /&gt;
						if((uint8_t)c == START_SIGN)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_check = 0;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parse Datengröße */&lt;br /&gt;
					case PARSER_STATE_SIZE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_size;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zieladresse */&lt;br /&gt;
					case PARSER_STATE_ADDRESS:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 4)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_addr = hex_addr_offset;&lt;br /&gt;
							hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
							hex_check += (uint8_t) hex_addr;&lt;br /&gt;
							hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
							&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zeilentyp */&lt;br /&gt;
					case PARSER_STATE_TYPE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_data_cnt = 0;&lt;br /&gt;
							hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_type;&lt;br /&gt;
							switch(hex_type)&lt;br /&gt;
							{&lt;br /&gt;
								case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
								case 0: &lt;br /&gt;
								case 2: &lt;br /&gt;
								case 4: parser_state = PARSER_STATE_DATA; &lt;br /&gt;
                                       &lt;br /&gt;
									   /* Berechnen der neue Flash-Page (abhängig von hex_type) */&lt;br /&gt;
									   /* Liegen die Daten noch in der aktuellen Flash-Page? */&lt;br /&gt;
				                       if(!flash_page_flag &amp;amp;&amp;amp; (flash_page != (hex_addr - hex_addr % SPM_PAGESIZE)) )&lt;br /&gt;
				                       {&lt;br /&gt;
					                       /* Wenn die Daten nicht in der aktuellen Flash-Page liegen, */&lt;br /&gt;
					                       /* wird die aktuelle Page geschrieben und ein Flag          */&lt;br /&gt;
					                       /* zum berechnen der neuen Page-Startadresse gesetzt        */&lt;br /&gt;
					                       write_page(flash_page, flash_data);&lt;br /&gt;
					                       flash_cnt = 0;&lt;br /&gt;
					                       flash_page_flag = 1;&lt;br /&gt;
				                       }&lt;br /&gt;
				&lt;br /&gt;
				                      /* Muss die Page-Startadresse neu berechnet werden? */&lt;br /&gt;
				                      if(flash_page_flag)&lt;br /&gt;
				                      {&lt;br /&gt;
					                      /* Berechnen der neuen Page-Startadresse */&lt;br /&gt;
					                      flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Füllen des Flash-Puffers mit dem &amp;quot;alten&amp;quot; Inhalt der Page */&lt;br /&gt;
					                      memcpy_PF(flash_data, flash_page, SPM_PAGESIZE);&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Flag setzen um anzuzeigen das eine neue Adresse da ist */&lt;br /&gt;
				 	                      flash_page_flag = 0;&lt;br /&gt;
				                      }  								&lt;br /&gt;
								      break;&lt;br /&gt;
								default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
							}&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Flash-Daten */&lt;br /&gt;
					case PARSER_STATE_DATA:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						switch(hex_type)&lt;br /&gt;
						{&lt;br /&gt;
							case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
								if(hex_cnt == 2)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;.&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
									flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += flash_data[flash_cnt];&lt;br /&gt;
									flash_cnt++;&lt;br /&gt;
									hex_data_cnt++;&lt;br /&gt;
									if(hex_data_cnt == hex_size)&lt;br /&gt;
									{&lt;br /&gt;
										parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
										hex_data_cnt=0;&lt;br /&gt;
										hex_cnt = 0;&lt;br /&gt;
									}&lt;br /&gt;
									/* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
									if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
									{&lt;br /&gt;
										write_page(flash_page, flash_data);&lt;br /&gt;
										flash_cnt = 0;&lt;br /&gt;
										flash_page_flag = 1;&lt;br /&gt;
									}&lt;br /&gt;
									uart_putc(XON);&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
							case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
								if(hex_cnt == 4)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;J&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
							&lt;br /&gt;
									/* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
									write_page(flash_page, flash_data);&lt;br /&gt;
									flash_cnt = 0;&lt;br /&gt;
									flash_page_flag = 1;&lt;br /&gt;
							&lt;br /&gt;
									/* Berechnen der Offsetadresse */&lt;br /&gt;
									switch(hex_type)&lt;br /&gt;
									{&lt;br /&gt;
										case 2: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 4; break;&lt;br /&gt;
										case 4: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 16; break;&lt;br /&gt;
									}&lt;br /&gt;
								&lt;br /&gt;
									/* Addieren der empfangenen Werte für die Checksumme */&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer + 2, 2);&lt;br /&gt;
							&lt;br /&gt;
									parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
									hex_data_cnt=0;&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							default:&lt;br /&gt;
								break;&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Checksumme */&lt;br /&gt;
					case PARSER_STATE_CHECKSUM:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_checksum;&lt;br /&gt;
							hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
							/* Dateiende -&amp;gt; schreibe Restdaten */&lt;br /&gt;
							if(hex_type == 1)&lt;br /&gt;
							{&lt;br /&gt;
								write_page(flash_page, flash_data);&lt;br /&gt;
								boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
							}&lt;br /&gt;
							/* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
							if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
							else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parserfehler (falsche Checksumme) */&lt;br /&gt;
					case PARSER_STATE_ERROR:&lt;br /&gt;
						uart_putc(&#039;#&#039;);&lt;br /&gt;
						break;&lt;br /&gt;
					default:&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			/* Programmzustand: UART Kommunikation */&lt;br /&gt;
			else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch((uint8_t)c)&lt;br /&gt;
				{&lt;br /&gt;
					case &#039;p&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
					uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie hier ein\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					case &#039;q&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
					uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					default:&lt;br /&gt;
   					uart_putc((unsigned char)c);&lt;br /&gt;
					uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(1000);&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Reset */&lt;br /&gt;
	start();&lt;br /&gt;
	&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fehler bei Flash &amp;gt; 64k ==&lt;br /&gt;
Neuere avr-gcc optimieren den oben beschriebenen Bootloader mit so genannten &amp;quot;table jumps&amp;quot;. Darin verwendet der Compiler LPM Instruktionen. Laut dem &amp;quot;AVR Instruction Set&amp;quot; kann dieser Befehl aber nur für Adressen bis 64k verwendet werden. Liegt nun der Bootloader am Ende eines 128k Flashs kann dies zum Absturz resp. Neustart des Controllers führen. Abhilfe schafft z.B. die Optimierungsoption &amp;quot;-fno-jump-tables&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)] jetzt zu finden bei: [https://code.google.com/p/arduino/source/browse/tags/0015/hardware/bootloaders/atmega/ATmegaBOOT_168.c code.google.com]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85794</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85794"/>
		<updated>2014-11-18T16:05:50Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0&lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uint16_t i;&lt;br /&gt;
	uint8_t sreg;&lt;br /&gt;
	&lt;br /&gt;
	/* Disable interrupts */&lt;br /&gt;
	sreg = SREG;&lt;br /&gt;
	cli();&lt;br /&gt;
	&lt;br /&gt;
	eeprom_busy_wait ();&lt;br /&gt;
	&lt;br /&gt;
	boot_page_erase (page);&lt;br /&gt;
	boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
	{&lt;br /&gt;
		/* Set up little-endian word. */&lt;br /&gt;
		uint16_t w = *buf++;&lt;br /&gt;
		w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
		&lt;br /&gt;
		boot_page_fill (page + i, w);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
	boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
	&lt;br /&gt;
	/* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
	/* to the application after bootloading. */&lt;br /&gt;
	boot_rww_enable ();&lt;br /&gt;
	&lt;br /&gt;
	/* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
	SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
	uint8_t  i;&lt;br /&gt;
	uint16_t val = 0;&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
	{&lt;br /&gt;
		uint8_t c = ascii[i];&lt;br /&gt;
		&lt;br /&gt;
		/* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
		if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;&lt;br /&gt;
		else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
		else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
		&lt;br /&gt;
		val = 16 * val + c;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return val;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void write_page(uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(100);&lt;br /&gt;
	program_page(page, buf);&lt;br /&gt;
	memset(buf, 0xFF, sizeof(SPM_PAGESIZE));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
        /* Intel-HEX Zieladresse */&lt;br /&gt;
	uint32_t        hex_addr = 0,&lt;br /&gt;
        /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
	hex_addr_offset = 0,&lt;br /&gt;
	/* Zu schreibende Flash-Page */&lt;br /&gt;
	flash_page = 0;&lt;br /&gt;
	/* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
	uint16_t        c = 0,&lt;br /&gt;
	/* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
	hex_check = 0,&lt;br /&gt;
	/* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
	flash_cnt = 0;&lt;br /&gt;
	/* temporäre Variable */&lt;br /&gt;
	uint8_t         temp,&lt;br /&gt;
	/* Flag zum steuern des Programmiermodus */&lt;br /&gt;
	boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
	/* Empfangszustandssteuerung */&lt;br /&gt;
	parser_state = PARSER_STATE_START,&lt;br /&gt;
	/* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
	flash_page_flag = 1,&lt;br /&gt;
	/* Datenpuffer für die Hexdaten*/&lt;br /&gt;
	flash_data[SPM_PAGESIZE],&lt;br /&gt;
	/* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
	hex_cnt = 0,&lt;br /&gt;
	/* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
	hex_buffer[5],&lt;br /&gt;
	/* Intel-HEX Datenlänge */&lt;br /&gt;
	hex_size = 0,&lt;br /&gt;
	/* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
	hex_data_cnt = 0,&lt;br /&gt;
	/* Intel-HEX Recordtype */&lt;br /&gt;
	hex_type = 0,&lt;br /&gt;
	/* empfangene HEX-Checksumme */&lt;br /&gt;
	hex_checksum=0;&lt;br /&gt;
	/* Funktionspointer auf 0x0000 */&lt;br /&gt;
	void            (*start)( void ) = 0x0000;&lt;br /&gt;
	&lt;br /&gt;
	/* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
	memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
	memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren verbiegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
	uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) );&lt;br /&gt;
	sei();&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(2000);&lt;br /&gt;
	&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		c = uart_getc();&lt;br /&gt;
		if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
		{&lt;br /&gt;
			/* Programmzustand: Parser */&lt;br /&gt;
			if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch(parser_state)&lt;br /&gt;
				{&lt;br /&gt;
					/* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
					case PARSER_STATE_START:&lt;br /&gt;
						if((uint8_t)c == START_SIGN)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_check = 0;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parse Datengröße */&lt;br /&gt;
					case PARSER_STATE_SIZE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_size;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zieladresse */&lt;br /&gt;
					case PARSER_STATE_ADDRESS:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 4)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_addr = hex_addr_offset;&lt;br /&gt;
							hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
							hex_check += (uint8_t) hex_addr;&lt;br /&gt;
							hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
							&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zeilentyp */&lt;br /&gt;
					case PARSER_STATE_TYPE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_data_cnt = 0;&lt;br /&gt;
							hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_type;&lt;br /&gt;
							switch(hex_type)&lt;br /&gt;
							{&lt;br /&gt;
								case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
								case 0: &lt;br /&gt;
								case 2: &lt;br /&gt;
								case 4: parser_state = PARSER_STATE_DATA; &lt;br /&gt;
                                       &lt;br /&gt;
									   /* Berechnen der neue Flash-Page (abhängig von hex_type) */&lt;br /&gt;
									   /* Liegen die Daten noch in der aktuellen Flash-Page? */&lt;br /&gt;
				                       if(!flash_page_flag &amp;amp;&amp;amp; (flash_page != (hex_addr - hex_addr % SPM_PAGESIZE)) )&lt;br /&gt;
				                       {&lt;br /&gt;
					                       /* Wenn die Daten nicht in der aktuellen Flash-Page liegen, */&lt;br /&gt;
					                       /* wird die aktuelle Page geschrieben und ein Flag          */&lt;br /&gt;
					                       /* zum berechnen der neuen Page-Startadresse gesetzt        */&lt;br /&gt;
					                       write_page(flash_page, flash_data);&lt;br /&gt;
					                       flash_cnt = 0;&lt;br /&gt;
					                       flash_page_flag = 1;&lt;br /&gt;
				                       }&lt;br /&gt;
				&lt;br /&gt;
				                      /* Muss die Page-Startadresse neu berechnet werden? */&lt;br /&gt;
				                      if(flash_page_flag)&lt;br /&gt;
				                      {&lt;br /&gt;
					                      /* Berechnen der neuen Page-Startadresse */&lt;br /&gt;
					                      flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Füllen des Flash-Puffers mit dem &amp;quot;alten&amp;quot; Inhalt der Page */&lt;br /&gt;
					                      memcpy_PF(flash_data, flash_page, SPM_PAGESIZE);&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Flag setzen um anzuzeigen das eine neue Adresse da ist */&lt;br /&gt;
				 	                      flash_page_flag = 0;&lt;br /&gt;
				                      }  								&lt;br /&gt;
								      break;&lt;br /&gt;
								default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
							}&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Flash-Daten */&lt;br /&gt;
					case PARSER_STATE_DATA:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						switch(hex_type)&lt;br /&gt;
						{&lt;br /&gt;
							case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
								if(hex_cnt == 2)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;.&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
									flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += flash_data[flash_cnt];&lt;br /&gt;
									flash_cnt++;&lt;br /&gt;
									hex_data_cnt++;&lt;br /&gt;
									if(hex_data_cnt == hex_size)&lt;br /&gt;
									{&lt;br /&gt;
										parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
										hex_data_cnt=0;&lt;br /&gt;
										hex_cnt = 0;&lt;br /&gt;
									}&lt;br /&gt;
									/* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
									if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
									{&lt;br /&gt;
										write_page(flash_page, flash_data);&lt;br /&gt;
										flash_cnt = 0;&lt;br /&gt;
										flash_page_flag = 1;&lt;br /&gt;
									}&lt;br /&gt;
									uart_putc(XON);&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
							case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
								if(hex_cnt == 4)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;J&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
							&lt;br /&gt;
									/* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
									write_page(flash_page, flash_data);&lt;br /&gt;
									flash_cnt = 0;&lt;br /&gt;
									flash_page_flag = 1;&lt;br /&gt;
							&lt;br /&gt;
									/* Berechnen der Offsetadresse */&lt;br /&gt;
									switch(hex_type)&lt;br /&gt;
									{&lt;br /&gt;
										case 2: hex_addr_offset = ((uint32_t)(hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 4; break;&lt;br /&gt;
										case 4: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 16; break;&lt;br /&gt;
									}&lt;br /&gt;
								&lt;br /&gt;
									/* Addieren der empfangenen Werte für die Checksumme */&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer + 2, 2);&lt;br /&gt;
							&lt;br /&gt;
									parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
									hex_data_cnt=0;&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							default:&lt;br /&gt;
								break;&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Checksumme */&lt;br /&gt;
					case PARSER_STATE_CHECKSUM:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_checksum;&lt;br /&gt;
							hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
							/* Dateiende -&amp;gt; schreibe Restdaten */&lt;br /&gt;
							if(hex_type == 1)&lt;br /&gt;
							{&lt;br /&gt;
								write_page(flash_page, flash_data);&lt;br /&gt;
								boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
							}&lt;br /&gt;
							/* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
							if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
							else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parserfehler (falsche Checksumme) */&lt;br /&gt;
					case PARSER_STATE_ERROR:&lt;br /&gt;
						uart_putc(&#039;#&#039;);&lt;br /&gt;
						break;&lt;br /&gt;
					default:&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			/* Programmzustand: UART Kommunikation */&lt;br /&gt;
			else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch((uint8_t)c)&lt;br /&gt;
				{&lt;br /&gt;
					case &#039;p&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
					uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie hier ein\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					case &#039;q&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
					uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					default:&lt;br /&gt;
   					uart_putc((unsigned char)c);&lt;br /&gt;
					uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(1000);&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Reset */&lt;br /&gt;
	start();&lt;br /&gt;
	&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85787</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85787"/>
		<updated>2014-11-17T16:13:15Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0&lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uint16_t i;&lt;br /&gt;
	uint8_t sreg;&lt;br /&gt;
	&lt;br /&gt;
	/* Disable interrupts */&lt;br /&gt;
	sreg = SREG;&lt;br /&gt;
	cli();&lt;br /&gt;
	&lt;br /&gt;
	eeprom_busy_wait ();&lt;br /&gt;
	&lt;br /&gt;
	boot_page_erase (page);&lt;br /&gt;
	boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
	{&lt;br /&gt;
		/* Set up little-endian word. */&lt;br /&gt;
		uint16_t w = *buf++;&lt;br /&gt;
		w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
		&lt;br /&gt;
		boot_page_fill (page + i, w);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
	boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
	&lt;br /&gt;
	/* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
	/* to the application after bootloading. */&lt;br /&gt;
	boot_rww_enable ();&lt;br /&gt;
	&lt;br /&gt;
	/* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
	SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
	uint8_t  i;&lt;br /&gt;
	uint16_t val = 0;&lt;br /&gt;
	&lt;br /&gt;
	for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
	{&lt;br /&gt;
		uint8_t c = ascii[i];&lt;br /&gt;
		&lt;br /&gt;
		/* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
		if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;&lt;br /&gt;
		else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
		else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
		&lt;br /&gt;
		val = 16 * val + c;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	return val;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void write_page(uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
	uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(100);&lt;br /&gt;
	program_page(page, buf);&lt;br /&gt;
	memset(buf, 0xFF, sizeof(SPM_PAGESIZE));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
        /* Intel-HEX Zieladresse */&lt;br /&gt;
	uint32_t        hex_addr = 0,&lt;br /&gt;
        /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
	hex_addr_offset = 0,&lt;br /&gt;
	/* Zu schreibende Flash-Page */&lt;br /&gt;
	flash_page = 0;&lt;br /&gt;
	/* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
	uint16_t        c = 0,&lt;br /&gt;
	/* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
	hex_check = 0,&lt;br /&gt;
	/* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
	flash_cnt = 0;&lt;br /&gt;
	/* temporäre Variable */&lt;br /&gt;
	uint8_t         temp,&lt;br /&gt;
	/* Flag zum steuern des Programmiermodus */&lt;br /&gt;
	boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
	/* Empfangszustandssteuerung */&lt;br /&gt;
	parser_state = PARSER_STATE_START,&lt;br /&gt;
	/* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
	flash_page_flag = 1,&lt;br /&gt;
	/* Datenpuffer für die Hexdaten*/&lt;br /&gt;
	flash_data[SPM_PAGESIZE],&lt;br /&gt;
	/* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
	hex_cnt = 0,&lt;br /&gt;
	/* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
	hex_buffer[5],&lt;br /&gt;
	/* Intel-HEX Datenlänge */&lt;br /&gt;
	hex_size = 0,&lt;br /&gt;
	/* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
	hex_data_cnt = 0,&lt;br /&gt;
	/* Intel-HEX Recordtype */&lt;br /&gt;
	hex_type = 0,&lt;br /&gt;
	/* empfangene HEX-Checksumme */&lt;br /&gt;
	hex_checksum=0;&lt;br /&gt;
	/* Funktionspointer auf 0x0000 */&lt;br /&gt;
	void            (*start)( void ) = 0x0000;&lt;br /&gt;
	&lt;br /&gt;
	/* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
	memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
	memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren verbiegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
	uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) );&lt;br /&gt;
	sei();&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(2000);&lt;br /&gt;
	&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		c = uart_getc();&lt;br /&gt;
		if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
		{&lt;br /&gt;
			/* Programmzustand: Parser */&lt;br /&gt;
			if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch(parser_state)&lt;br /&gt;
				{&lt;br /&gt;
					/* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
					case PARSER_STATE_START:&lt;br /&gt;
						if((uint8_t)c == START_SIGN)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_check = 0;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parse Datengröße */&lt;br /&gt;
					case PARSER_STATE_SIZE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_size;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zieladresse */&lt;br /&gt;
					case PARSER_STATE_ADDRESS:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 4)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_addr = hex_addr_offset;&lt;br /&gt;
							hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
							hex_check += (uint8_t) hex_addr;&lt;br /&gt;
							hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
							&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Zeilentyp */&lt;br /&gt;
					case PARSER_STATE_TYPE:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_cnt = 0;&lt;br /&gt;
							hex_data_cnt = 0;&lt;br /&gt;
							hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_type;&lt;br /&gt;
							switch(hex_type)&lt;br /&gt;
							{&lt;br /&gt;
								case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
								case 0: &lt;br /&gt;
								case 2: &lt;br /&gt;
								case 4: parser_state = PARSER_STATE_DATA; &lt;br /&gt;
                                       &lt;br /&gt;
									   /* Berechnen der neue Flash-Page (abhängig von hex_type) */&lt;br /&gt;
									   /* Liegen die Daten noch in der aktuellen Flash-Page? */&lt;br /&gt;
				                       if(!flash_page_flag &amp;amp;&amp;amp; (flash_page != (hex_addr - hex_addr % SPM_PAGESIZE)) )&lt;br /&gt;
				                       {&lt;br /&gt;
					                       /* Wenn die Daten nicht in der aktuellen Flash-Page liegen, */&lt;br /&gt;
					                       /* wird die aktuelle Page geschrieben und ein Flag          */&lt;br /&gt;
					                       /* zum berechnen der neuen Page-Startadresse gesetzt        */&lt;br /&gt;
					                       write_page(flash_page, flash_data);&lt;br /&gt;
					                       flash_cnt = 0;&lt;br /&gt;
					                       flash_page_flag = 1;&lt;br /&gt;
				                       }&lt;br /&gt;
				&lt;br /&gt;
				                      /* Muss die Page-Startadresse neu berechnet werden? */&lt;br /&gt;
				                      if(flash_page_flag)&lt;br /&gt;
				                      {&lt;br /&gt;
					                      /* Berechnen der neuen Page-Startadresse */&lt;br /&gt;
					                      flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Füllen des Flash-Puffers mit dem &amp;quot;alten&amp;quot; Inhalt der Page */&lt;br /&gt;
					                      memcpy_PF(flash_data, flash_page, SPM_PAGESIZE);&lt;br /&gt;
 					&lt;br /&gt;
                                          /* Flag setzen um anzuzeigen das eine neue Adresse da ist */&lt;br /&gt;
				 	                      flash_page_flag = 0;&lt;br /&gt;
				                      }  								&lt;br /&gt;
								      break;&lt;br /&gt;
								default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
							}&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Flash-Daten */&lt;br /&gt;
					case PARSER_STATE_DATA:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						switch(hex_type)&lt;br /&gt;
						{&lt;br /&gt;
							case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
								if(hex_cnt == 2)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;.&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
									flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += flash_data[flash_cnt];&lt;br /&gt;
									flash_cnt++;&lt;br /&gt;
									hex_data_cnt++;&lt;br /&gt;
									if(hex_data_cnt == hex_size)&lt;br /&gt;
									{&lt;br /&gt;
										parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
										hex_data_cnt=0;&lt;br /&gt;
										hex_cnt = 0;&lt;br /&gt;
									}&lt;br /&gt;
									/* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
									if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
									{&lt;br /&gt;
										write_page(flash_page, flash_data);&lt;br /&gt;
										flash_cnt = 0;&lt;br /&gt;
										flash_page_flag = 1;&lt;br /&gt;
									}&lt;br /&gt;
									uart_putc(XON);&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
							case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
								if(hex_cnt == 4)&lt;br /&gt;
								{&lt;br /&gt;
									uart_putc(XOFF);&lt;br /&gt;
									uart_putc(&#039;J&#039;);&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
							&lt;br /&gt;
									/* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
									write_page(flash_page, flash_data);&lt;br /&gt;
									flash_cnt = 0;&lt;br /&gt;
									flash_page_flag = 1;&lt;br /&gt;
							&lt;br /&gt;
									/* Berechnen der Offsetadresse */&lt;br /&gt;
									switch(hex_type)&lt;br /&gt;
									{&lt;br /&gt;
										case 2: hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) * 16); break;&lt;br /&gt;
										case 4: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 16; break;&lt;br /&gt;
									}&lt;br /&gt;
								&lt;br /&gt;
									/* Addieren der empfangenen Werte für die Checksumme */&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer, 2);&lt;br /&gt;
									hex_check += (uint8_t) hex2num(hex_buffer + 2, 2);&lt;br /&gt;
							&lt;br /&gt;
									parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
									hex_data_cnt=0;&lt;br /&gt;
									hex_cnt = 0;&lt;br /&gt;
								}&lt;br /&gt;
								break;&lt;br /&gt;
							default:&lt;br /&gt;
								break;&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					/* Parse Checksumme */&lt;br /&gt;
					case PARSER_STATE_CHECKSUM:&lt;br /&gt;
						hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
						if(hex_cnt == 2)&lt;br /&gt;
						{&lt;br /&gt;
							uart_putc(XOFF);&lt;br /&gt;
							hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
							hex_check += hex_checksum;&lt;br /&gt;
							hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
							/* Dateiende -&amp;gt; schreibe Restdaten */&lt;br /&gt;
							if(hex_type == 1)&lt;br /&gt;
							{&lt;br /&gt;
								write_page(flash_page, flash_data);&lt;br /&gt;
								boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
							}&lt;br /&gt;
							/* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
							if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
							else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
							uart_putc(XON);&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
						/* Parserfehler (falsche Checksumme) */&lt;br /&gt;
					case PARSER_STATE_ERROR:&lt;br /&gt;
						uart_putc(&#039;#&#039;);&lt;br /&gt;
						break;&lt;br /&gt;
					default:&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			/* Programmzustand: UART Kommunikation */&lt;br /&gt;
			else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
			{&lt;br /&gt;
				switch((uint8_t)c)&lt;br /&gt;
				{&lt;br /&gt;
					case &#039;p&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
					uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie hier ein\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					case &#039;q&#039;:&lt;br /&gt;
					boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
					uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
					default:&lt;br /&gt;
   					uart_putc((unsigned char)c);&lt;br /&gt;
					uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
	&lt;br /&gt;
	uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
	_delay_ms(1000);&lt;br /&gt;
	&lt;br /&gt;
	/* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
	temp = MCUCR;&lt;br /&gt;
	MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
	MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
	&lt;br /&gt;
	/* Reset */&lt;br /&gt;
	start();&lt;br /&gt;
	&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85731</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85731"/>
		<updated>2014-11-13T14:06:46Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0,&lt;br /&gt;
                    /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
                    hex_addr_offset = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex_addr_offset;&lt;br /&gt;
                              hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   case 2: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 4: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          switch(hex_type)&lt;br /&gt;
                          {&lt;br /&gt;
                              case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
                                  if(hex_cnt == 2)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;.&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                      flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += flash_data[flash_cnt];&lt;br /&gt;
                                      flash_cnt++;&lt;br /&gt;
                                      hex_data_cnt++;&lt;br /&gt;
                                      if(hex_data_cnt == hex_size)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                          hex_data_cnt=0;&lt;br /&gt;
                                          hex_cnt = 0;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                                      if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                          _delay_ms(100);&lt;br /&gt;
                                          program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                          memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                          flash_cnt = 0;&lt;br /&gt;
                                          flash_page_flag = 1;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      uart_putc(XON);&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) * 16);&lt;br /&gt;
&lt;br /&gt;
                                      /* Addieren der empfangenen Werte für die Checksumme */&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer + 2, 2);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) &amp;lt;&amp;lt; 16;&lt;br /&gt;
&lt;br /&gt;
                                      /* Addieren der empfangenen Werte für die Checksumme */ &lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer + 2, 2);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              default: &lt;br /&gt;
                                  break;&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85727</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85727"/>
		<updated>2014-11-13T07:32:21Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0,&lt;br /&gt;
                    /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
                    hex_addr_offset = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex_addr_offset;&lt;br /&gt;
                              hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   case 2: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 4: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          switch(hex_type)&lt;br /&gt;
                          {&lt;br /&gt;
                              case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
                                  if(hex_cnt == 2)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;.&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                      flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += flash_data[flash_cnt];&lt;br /&gt;
                                      flash_cnt++;&lt;br /&gt;
                                      hex_data_cnt++;&lt;br /&gt;
                                      if(hex_data_cnt == hex_size)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                          hex_data_cnt=0;&lt;br /&gt;
                                          hex_cnt = 0;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                                      if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                          _delay_ms(100);&lt;br /&gt;
                                          program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                          memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                          flash_cnt = 0;&lt;br /&gt;
                                          flash_page_flag = 1;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      uart_putc(XON);&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) * 16);&lt;br /&gt;
&lt;br /&gt;
                                      /* Addieren der empfangenen Werte für die Checksumme */&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer + 2, 2);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) &amp;lt;&amp;lt; 16);&lt;br /&gt;
&lt;br /&gt;
                                      /* Addieren der empfangenen Werte für die Checksumme */ &lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex2num(hex_buffer + 2, 2);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              default: &lt;br /&gt;
                                  break;&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85726</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85726"/>
		<updated>2014-11-13T07:26:55Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0,&lt;br /&gt;
                    /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
                    hex_addr_offset = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex_addr_offset;&lt;br /&gt;
                              hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   case 2: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 4: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          switch(hex_type)&lt;br /&gt;
                          {&lt;br /&gt;
                              case 0:  /* Record Typ 00 - Data Record auswerten */&lt;br /&gt;
                                  if(hex_cnt == 2)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;.&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                      flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += flash_data[flash_cnt];&lt;br /&gt;
                                      flash_cnt++;&lt;br /&gt;
                                      hex_data_cnt++;&lt;br /&gt;
                                      if(hex_data_cnt == hex_size)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                          hex_data_cnt=0;&lt;br /&gt;
                                          hex_cnt = 0;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                                      if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                          _delay_ms(100);&lt;br /&gt;
                                          program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                          memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                          flash_cnt = 0;&lt;br /&gt;
                                          flash_page_flag = 1;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      uart_putc(XON);&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 2:   /* Record Typ 02 - Extended Segment Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) * 16);&lt;br /&gt;
                                      hex_check += (uint8_t) hex_addr_offset;&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 8);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 24);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              case 4:   /* Record Typ 04 - Extended Linear Address auswerten */&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) &amp;lt;&amp;lt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) hex_addr_offset;&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 8);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 24);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              default: &lt;br /&gt;
                                  break;&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85724</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85724"/>
		<updated>2014-11-12T16:06:29Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0,&lt;br /&gt;
                    /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
                    hex_addr_offset = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex_addr_offset;&lt;br /&gt;
                              hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   case 2: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          switch(hex_type)&lt;br /&gt;
                          {&lt;br /&gt;
                              case 0:  /* Daten in Puffer schreiben */&lt;br /&gt;
                              {&lt;br /&gt;
                                  if(hex_cnt == 2)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;.&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                      flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += flash_data[flash_cnt];&lt;br /&gt;
                                      flash_cnt++;&lt;br /&gt;
                                      hex_data_cnt++;&lt;br /&gt;
                                      if(hex_data_cnt == hex_size)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                          hex_data_cnt=0;&lt;br /&gt;
                                          hex_cnt = 0;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                                      if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                          _delay_ms(100);&lt;br /&gt;
                                          program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                          memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                          flash_cnt = 0;&lt;br /&gt;
                                          flash_page_flag = 1;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      uart_putc(XON);&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              }&lt;br /&gt;
                              case 2:   /* Segmentadresse auswerten */&lt;br /&gt;
                              {&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) * 16);&lt;br /&gt;
                                      hex_check += (uint8_t) hex_addr_offset;&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 8);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 24);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              }&lt;br /&gt;
                              default: break;&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85723</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85723"/>
		<updated>2014-11-12T15:43:25Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0,&lt;br /&gt;
                    /* Intel-HEX Zieladress-Offset */&lt;br /&gt;
                    hex_addr_offset = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex_addr_offset;&lt;br /&gt;
                              hex_addr += hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   case 2: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          switch(hex_type)&lt;br /&gt;
                          {&lt;br /&gt;
                              case 0:  /* Daten in Puffer schreiben */&lt;br /&gt;
                              {&lt;br /&gt;
                                  if(hex_cnt == 2)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;.&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                      flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                                      hex_check += flash_data[flash_cnt];&lt;br /&gt;
                                      flash_cnt++;&lt;br /&gt;
                                      hex_data_cnt++;&lt;br /&gt;
                                      if(hex_data_cnt == hex_size)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                          hex_data_cnt=0;&lt;br /&gt;
                                          hex_cnt = 0;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                                      if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                                      {&lt;br /&gt;
                                          uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                          _delay_ms(100);&lt;br /&gt;
                                          program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                          memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                          flash_cnt = 0;&lt;br /&gt;
                                          flash_page_flag = 1;&lt;br /&gt;
                                      }&lt;br /&gt;
                                      uart_putc(XON);&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              }&lt;br /&gt;
                              case 2:   /* Segmentadresse auswerten */&lt;br /&gt;
                              {&lt;br /&gt;
                                  if(hex_cnt == 4)&lt;br /&gt;
                                  {&lt;br /&gt;
                                      uart_putc(XOFF);&lt;br /&gt;
                                      uart_putc(&#039;J&#039;);&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
&lt;br /&gt;
                                      /* Schreibe angfangene Flash-Page vor Segment-Sprung */&lt;br /&gt;
                                      uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                      _delay_ms(100);&lt;br /&gt;
                                      program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                      memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                      flash_cnt = 0;&lt;br /&gt;
                                      flash_page_flag = 1;&lt;br /&gt;
&lt;br /&gt;
                                      /* Berechnen der Offsetadresse */&lt;br /&gt;
                                      hex_addr_offset = (uint32_t)(hex2num(hex_buffer, 4) &amp;lt;&amp;lt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) hex_addr_offset;&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 8);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 16);&lt;br /&gt;
                                      hex_check += (uint8_t) (hex_addr_offset &amp;gt;&amp;gt; 24);&lt;br /&gt;
&lt;br /&gt;
                                      parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                      hex_data_cnt=0;&lt;br /&gt;
                                      hex_cnt = 0;&lt;br /&gt;
                                  }&lt;br /&gt;
                                  break;&lt;br /&gt;
                              }&lt;br /&gt;
                              default: break;&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85722</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85722"/>
		<updated>2014-11-12T15:24:00Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
    uint32_t        hex_addr = 0;&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85721</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85721"/>
		<updated>2014-11-12T15:21:02Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.+&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
Das Adresse eines Type 0 - Records im HEX86 - Format ist auf 16 Bit und somit auf 65535 Adressen beschränkt. Um den Adressbereich zu erweitern wurden die Record - Typen 2 bis 5 definert, wobei für uns nur Typ 2 und 4 relevant sind. Im Record-Typ 2 wird eine Offset- bzw Segmentadresse definiert. Sie wird mit 16 multipliziert und bei allen folgenden Schreiboperationen zur Adresse hinzuaddiert. Typ 4 sind die oberen 16 Bit einer 32 Bitadresse, die unteren 16 Bit sind in diesem Fall die im Record-Typ 0 angegebenen Adresse. Die Zustandsmaschine muß nun entsprechend erweitert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85720</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85720"/>
		<updated>2014-11-12T15:11:50Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet. &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG:  Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85719</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85719"/>
		<updated>2014-11-12T15:10:35Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet. &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;ACHTUNG: &#039;&#039;&#039; Der Abschnitt befindet sich zur Zeit in Bearbeitung!! &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85718</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=85718"/>
		<updated>2014-11-12T15:08:40Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des Weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die [http://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library]  von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten.&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit ist zu beachten, wenn der verfügbare Flash-Speicher größer als 128kB ist. In der Regel wird der Bootloader im oberen Teil des Speichers liegen und daher ist ein einfacher Rücksprung mit normaler 2Byte Wordadressierung nicht möglich. Leider hat gcc hier einen Bug und verwendet das vom Controller zusätzlich herangezogene Register EIND nicht. Dieses Register muß daher explizit vor dem Rücksprung auf 0 gesetzt werden. Also &amp;lt;pre&amp;gt;EIND = 0;&lt;br /&gt;
start();&amp;lt;/pre&amp;gt;Ohne diesen Zusatz kann der Bootloader im Normalfall nicht mehr verlassen werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x0C00;  // Achtung Falle: Hier Word-Adresse&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer auf die &#039;&#039;&#039;Word&#039;&#039;&#039;-Adresse des Bootloaders. (Hier verhält sich der AVR-GCC leider etwas inkonsistent, da sonst bei Flash-Adressen mit Bytes gearbeitet wird. Gibt man hier versehentlich die Byteadresse an, kann es sein, dass der Sprung in den Bootloader klappt, es muss aber nicht funktionieren, da das Sprungziel undefiniert ist. Verwendet man stattdessen einen JMP befehl im Inline-Assembler, so ist die Byte-Adresse für das Sprungziel anzugeben.) &amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x0C00;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
Leider haben wir auch hier ein Problem, wenn der verfügbare Flash-Speicher größer als 128kB ist. Denn selbst wenn man dem Funktionspointer z.B. 0x1F000 (kleinste Bootloader Startadresse bei 256kB Devices) als Adresswert zuweist, werden nur die unteren zwei Bytes verwendet. Der Sprung in den Bootloader würde also nach 0xF000 gehen und man würde bei Applikationen, die größer sind als 120kB, irgendwo mitten im Code landen. Um sicher zu sein, daß an die korrekte Startadresse gesprungen wird, muß vor dem Sprung das EIND Register explizit auf 1 gesetzt werden. (Im Gegensatz zum Rücksprung mit EIND = 0) Also &lt;br /&gt;
&amp;lt;pre&amp;gt;EIND = 1;&lt;br /&gt;
bootloader();&amp;lt;/pre&amp;gt;&lt;br /&gt;
Das EIND Register wird vom Controller bei extended calls oder jumps als höchstwertigstes Adressbyte verwendet, aber leider vom gcc Compiler in der aktuellen Version nicht unterstützt. Siehe [http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/AVR-Options.html]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden (Flash-Speicher nicht automatisch löschen).&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Als erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben. Eine andere Möglichkeit ist, eine Verzögerung zu aktivieren. z.B. kann mit dem Programm CoolTerm einen &amp;quot;Transmit Character delay&amp;quot; eingestellt werden. Dies funktioniert dann ab 2ms, wenn die Flusssteuerung versagt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader für Programme &amp;gt; 64k =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader für AVRs mit mehr als 64K Flash-Speicher wird direkt Bootloader im vorherigen Kapitel abgeleitet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xYYYYY&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse) 0xXXXXX || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Wordadresse) 0xYYYYY&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800 || 0xC00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800 || 0x1C00&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000 || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000 || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000 || 0xF000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000 || 0x1F000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die Application Note [http://www.atmel.com/images/doc2591.pdf AVR068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164].&lt;br /&gt;
&lt;br /&gt;
Mit Atmel Studio 6 lässt sich das Vorgehen sehr leicht über die Post-Build Events automatisieren. Benötigt wird hierzu ein kleines Batch-Script, welches z.B. als &amp;quot;hexjoin.bat&amp;quot; im Solution Directory abgelegt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
type %1 | findstr /v :00000001FF &amp;gt; tmp.hex&lt;br /&gt;
type %2 &amp;gt;&amp;gt; tmp.hex&lt;br /&gt;
type tmp.hex &amp;gt; %1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter &amp;quot;Project-&amp;gt;Properties-&amp;gt;Build Events-&amp;gt;Post-build event command line&amp;quot; kann nun das Script nach jedem Build ausgeführt werden und erzeugt eine kombinierte HEX-Datei. Im folgenden Beispiel wird davon ausgegangen, dass sowohl die Datei mit dem Bootloader, als auch das Batch-Script im Solution Directory liegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;dos&amp;quot;&amp;gt;&lt;br /&gt;
$(SolutionDir)hexjoin.bat &amp;quot;$(OutputDirectory)\$(OutputFileName).hex&amp;quot; &amp;quot;$(SolutionDir)bootloader.hex&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen / Links =&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/arduino/Arduino/blob/master/hardware/arduino/bootloaders/atmega/ATmegaBOOT_168.c Arduino Bootloader (simuliert den AVR ISP)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger FastBoot von Peter Dannegger ]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/Bootloader allgemeines zu Bootloadern ]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Raspberry_Pi_Einf%C3%BChrung&amp;diff=78619</id>
		<title>Raspberry Pi Einführung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Raspberry_Pi_Einf%C3%BChrung&amp;diff=78619"/>
		<updated>2013-09-24T09:31:25Z</updated>

		<summary type="html">&lt;p&gt;Mario: Change: Datensicherung -&amp;gt; Win32DiskImager&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Robert Knauer&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig&#039;&#039;&#039;: &#039;&#039;Alle Informationen und Anleitungen in diesem Artikel beziehen sich auf das Model B mit erweitertem Arbeitsspeicher.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Raspberry_Pi.jpg|thumb|Der Raspberry Pi]]&lt;br /&gt;
Der &#039;&#039;&#039;Raspberry Pi&#039;&#039;&#039; ist ein Einplatinen-Computer im Kreditkarten-Format mit einem SoC (System-on-a-Chip) von Broadcom. Dieser verfügt über folgende Eigenschaften:&lt;br /&gt;
* eine 700MHz ARMv6-CPU&lt;br /&gt;
* eine Broadcom VideoCore IV GPU&lt;br /&gt;
* 512 MB Arbeitsspeicher&lt;br /&gt;
Außerdem kann der Raspberry Pi über folgende Schnittstellen mit der Außenwelt kommunizieren:&lt;br /&gt;
* 2 USB 2.0 Host-Anschlüsse&lt;br /&gt;
* einen Composite Video-Ausgang&lt;br /&gt;
* einen HDMI-Ausgang (Video+Audio)&lt;br /&gt;
* einen 3,5mm Klinke-Audio-Ausgang&lt;br /&gt;
* einen SD-/MMC-Karten-Slot (Unterstützung für SDIO und SDHC)&lt;br /&gt;
* einen Ethernet-Port (10/100 MBit)&lt;br /&gt;
* 21 GPIO-Pins, teilweise gedacht als [[UART]], [[SPI]] und [[I2C]]&lt;br /&gt;
Die Energieversorgung wird über einen microUSB-Anschluss gewährleistet. Man sollte jedoch darauf achten, dass der USB-Anschluss, über den man den Raspberry Pi mit Energie versorgt mindestens 700 mA liefern kann, da in der USB 2.0 Spezifikation nur 500 mA vorgesehen sind. In diesem Artikel wird noch darauf eingegangen, wie man sich selbst ein entsprechendes Netzteil bauen kann, alternativ funktioniert auch ein entsprechend starkes USB-Ladegerät in Verbindung mit einem USB-A auf microUSB-B Kabel. (siehe [[Raspberry Pi#Stromversorgung]])&lt;br /&gt;
&lt;br /&gt;
Um diese umfangreichen Möglichkeiten zu nutzen muss man mit einem anderen Computer ein Betriebssystem auf die SD-Karte laden, das dann vom Raspberry Pi gebootet wird. Am naheliegendsten ist hier natürlich Linux (Raspbian, Fedora, Debian und Arch Linux sind offiziell unterstützt), aber auch Plan 9 wurde schon auf dem kleinen Computer [http://www.heise.de/open/meldung/Plan-9-laeuft-auf-dem-Raspberry-1761392.html zum Laufen gebracht].&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel soll Schritt für Schritt der Weg vom Anschließen des Computers über das Einrichten von Arch Linux ARM bis zum Verwenden der verschiedenen Schnittstellen des Raspberry Pi beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Bezugsquellen &amp;amp; Zubehör ==&lt;br /&gt;
Der Raspberry Pi kann über verschiedene Quellen bezogen werden. Wichtig ist darauf zu achten, dass man das Model B mit 512 MB Arbeitsspeicher bestellt, da alle Anleitungen in diesem Artikel nur damit getestet sind und unter anderen Modellen eventuell nicht funktionieren. Ein in Deutschland ziemlich bekannter Elektronik-Versand, bei dem man den Raspberry Pi bestellen kann, ist Pollin, dort ist er zur Zeit für 38,95 € erhältlich. (Bestell-Nr.: 701 697, Stand: 17.06.2013)&lt;br /&gt;
&lt;br /&gt;
Doch neben dem eigentlichen Computer braucht man natürlich noch etwas Zubehör, wenn man es nicht eh schon zu Hause rumliegen hat:&lt;br /&gt;
* ein Netzteil, das 5V bei mindestens 700 mA auf einem microUSB-Stecker liefert (siehe [[Raspberry Pi#Stromversorgung]])&lt;br /&gt;
* eine SD(HC)-Speicherkarte&lt;br /&gt;
* einen SD(HC)-Cardreader an einem anderen Computer&lt;br /&gt;
* ein RJ45-Netzwerkkabel&lt;br /&gt;
* ein Ethernet-Netzwerk mit Zugang zum Internet&lt;br /&gt;
Mit diesem Zubehör kann man nur über SSH zugreifen, wenn man direkt am Raspberry Pi arbeiten will und später evtl. auch eine grafische Oberfläche einrichten will braucht man noch folgendes:&lt;br /&gt;
* ein HDMI-Kabel und einen Fernseher bzw. Monitor mit HDMI-Eingang &#039;&#039;&#039;oder&#039;&#039;&#039;&lt;br /&gt;
* ein Composite-Kabel und einen Fernseher mit entsprechendem Video-Eingang&lt;br /&gt;
* eine USB-Tastatur&lt;br /&gt;
* eine USB-Maus (für eine grafische Oberfläche)&lt;br /&gt;
* evtl. einen aktiven USB-Hub, wenn die Ports des Raspberry Pi nicht genug Leistung für Maus und Tastatur liefern können&lt;br /&gt;
&lt;br /&gt;
== Das Betriebssystem ==&lt;br /&gt;
Als Betriebssystem für die Anleitungen in diesem Artikel wird [http://archlinuxarm.org/ Arch Linux ARM] verwendet, das Meiste funktioniert jedoch auch unter anderen Linux-Distributionen und alles andere zumindest mit kleinen Anpassungen.&lt;br /&gt;
Es basiert auf den Prinzipien und teilweise auch der Software von [https://www.archlinux.org/ Arch Linux], hat aber vollständig unabhängige Paketquellen, in denen nur für ARM kompilierte Software verfügbar ist.&lt;br /&gt;
&lt;br /&gt;
Arch Linux ist ein gutes Betriebssystem für einen Computer wie den Raspberry Pi, da es am Anfang sehr leichtgewichtig ist und vorerst keine grafische Oberfläche installiert hat (was gut ist wenn man den Raspberry Pi als Server verwenden will). Andererseits kann aber auch problemlos eine grafische Oberfläche nachinstalliert werden, wenn man den Raspberry Pi als Desktop-Computer, HTPC oder ähnliches verwenden will.&lt;br /&gt;
&lt;br /&gt;
Man muss jedoch auch sagen, dass Arch Linux kein Betriebssystem für jemanden ist, der Linux noch nie verwendet hat. Man sollte schon etwas Erfahrung mit dem Arbeiten auf der Konsole haben, da grundlegende Dinge über Linux hier nicht erklärt werden.&lt;br /&gt;
Außerdem ist es teilweise notwendig, sich selbst weiterführende Informationen zu Arch Linux anzusehen, die in diesem Artikel verlinkt werden.&lt;br /&gt;
&lt;br /&gt;
== Anschluss &amp;amp; Installation ==&lt;br /&gt;
Nun muss man zuerst alles anschließen. Die Netzwerk-Buchse wird über das Netzwerk-Kabel mit dem Ethernet-Netzwerk verbunden. Hier ist wichtig, dass in dem Netzwerk ein DHCP-Server läuft (also IP-Adressen automatisch bezogen werden können) und Zugriff zum Internet möglich ist. Wenn man sich für die Variante mit eigenem Bildschirm entschieden hat muss man außerdem die HDMI-Buchse mit dem Fernseher oder Monitor bzw. den Composite-Ausgang mit dem Fernseher verbinden und Tastatur und Maus über USB anschließen.&lt;br /&gt;
Dann folgt die Installation von Arch Linux ARM auf die SD-Karte.&lt;br /&gt;
&lt;br /&gt;
Arch Linux ARM für Raspberry Pi kann man über folgende Links downloaden:&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-06-06/archlinux-hf-2013-06-06.zip.torrent Torrent-Download von Arch Linux ARM 2013-02-11]&lt;br /&gt;
* [http://downloads.raspberrypi.org/download.php?file=/images/archlinuxarm/archlinux-hf-2013-06-06/archlinux-hf-2013-06-06.zip HTTP-Download von Arch Linux ARM 2013-06-06]&lt;br /&gt;
Wenn der etwa 185 MB große Download abgeschlossen ist und man über HTTP gedownloadet hat sollte man zuerst die Checksum überprüfen.&lt;br /&gt;
Eine Anleitung dazu findet befindet sich auf der Downloadseite.&lt;br /&gt;
* SHA-1: 706037373bbb33ab6fb0af147a6e795bd04f1503&lt;br /&gt;
Nachdem man das getan oder falls man über Torrent gedownloadet hat (dann passiert das automatisch) kann man das zip-Archiv entpacken.&lt;br /&gt;
&lt;br /&gt;
Man erhält eine etwa 2,0 GB große Datei namens archlinux-hf-2013-06-06.img. Diese muss man nun auf die SD-Karte bringen. Unter Linux funktioniert das mit dem kleinen Programm dd, das in den GNU coreutils enthalten ist und damit auf fast jedem System vorinstalliert sein sollte. Dazu gibt man folgendes in die Konsole ein (als root, /dev/sdX durch den Pfad zur Gerätedatei der SD-Karte ersetzen, Pfad der img-Datei anpassen):&lt;br /&gt;
* dd if=/pfad/zur/archlinux-hf-2013-02-11.img bs=1M of=/dev/sdX&lt;br /&gt;
Unter Windows kann man dafür [https://launchpad.net/win32-image-writer Image Writer for Windows] benutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Danach entfernt man die Karte aus dem Cardreader des Computers und setzt sie in den Raspberry Pi ein. Dann verbindet man die Stromversorgung, indem man den microUSB-Stecker des Netzteils in die entsprechende Buchse auf der Platine steckt.&lt;br /&gt;
&lt;br /&gt;
== Der erste Start ==&lt;br /&gt;
Wenn man ein Display angeschlossen hat, erscheint nach ein paar Sekunden ein Login-Prompt auf dem Bildschirm. Als Username gibt man &#039;&#039;root&#039;&#039; ein, wenn man dann nach dem Passwort gefragt wird gibt man wieder &#039;&#039;root&#039;&#039; ein.&lt;br /&gt;
&lt;br /&gt;
Wenn man kein Display angeschlossen hat, muss man die IP-Adresse, die der Raspberry Pi sich vom DHCP-Server geholt hat, herausfinden. Wenn der DSL-Router diesen Dienst bereitstellt, kann man meistens auf der Web-Oberfläche eine Liste der verteilten IP-Adressen sehen. Alternativ kann man auch mit &#039;&#039;nmap&#039;&#039; einen Netzwerk-Scan machen, der die IP-Adressen von allen Computern im Netzwerk anzeigt. Das funktioniert unter Linux, indem man &#039;&#039;&#039;nmap -sn SUBNETZ&#039;&#039;&#039; im Terminal eingibt. Davor ersetzt man SUBNETZ durch das Subnetz, in dem auch der Raspberry Pi ist, in [https://de.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR-Notation]. Wenn man z.B. in einem Netz ist, in dem IP-Adressen von &#039;&#039;192.168.1.0&#039;&#039; bis &#039;&#039;192.168.1.255&#039;&#039; verteilt werden, muss man &lt;br /&gt;
&#039;&#039;192.168.1.0/24&#039;&#039; verwenden.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse herausgefunden hat, verbindet man sich per SSH zu ihr und loggt sich als root, Passwort root ein. Unter Linux funktioniert das mit&lt;br /&gt;
* ssh root@IP-ADRESSE&lt;br /&gt;
Unter Windows verwendet man am besten [http://www.putty.org/ PuTTY].&lt;br /&gt;
&lt;br /&gt;
Egal für welche Möglichkeit man sich entschieden hat, sitzt man jetzt vor einer root-Shell des Raspberry Pi, erkennbar am Shell-Prompt &#039;&#039;&#039;[root@alarmpi ~]#&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
Nun geht es an die Konfiguration von Arch Linux ARM, diese erfolgt vollständig über das Editieren von Dateien auf der Konsole.&lt;br /&gt;
&lt;br /&gt;
=== Lokalisierung ===&lt;br /&gt;
Man editiert die Datei /etc/locale.gen:&lt;br /&gt;
* nano /etc/locale.gen&lt;br /&gt;
und entfernt die Raute vor der Zeile mit der entsprechenden Sprache-Land-Kodierungs-Kombination, also z.B. vor &#039;&#039;&#039;de_DE.UTF-8 UTF-8&#039;&#039;&#039; für Deutschland.&lt;br /&gt;
Danach speichert man mit Strg+O -&amp;gt; Enter und Strg+X und erstellt die Lokalisierung neu:&lt;br /&gt;
* locale-gen&lt;br /&gt;
Danach editiert man die Datei /etc/locale.conf:&lt;br /&gt;
* nano /etc/locale.conf&lt;br /&gt;
Man ändert &#039;&#039;&#039;LANG=en_US.UTF-8&#039;&#039;&#039; zur entsprechenden Sprache, also z.B. &#039;&#039;&#039;LANG=de_DE.utf8&#039;&#039;&#039; und speichert mit Strg+O -&amp;gt; Enter und Strg+X.&lt;br /&gt;
&lt;br /&gt;
=== Tastatur-Layout ===&lt;br /&gt;
Um das Tastatur-Layout auf der Konsole anzupassen, muss man die Datei /etc/vconsole.conf ändern:&lt;br /&gt;
* nano /etc/vconsole.conf&lt;br /&gt;
Man ändert &#039;&#039;&#039;KEYMAP=us&#039;&#039;&#039; zu z.B. &#039;&#039;&#039;KEYMAP=de-latin1&#039;&#039;&#039; für QWERTZ  und speichert wieder mit Strg+O -&amp;gt; Enter und Strg+X.&lt;br /&gt;
&lt;br /&gt;
=== Zeitzone ===&lt;br /&gt;
Um die Zeitzone zu ändern muss man einen Symlink zu /etc/localtime erstellen:&lt;br /&gt;
* ln -sf /usr/share/zoneinfo/ZEITZONE /etc/localtime&lt;br /&gt;
Also für Deutschland (Europe/Berlin):&lt;br /&gt;
* ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime&lt;br /&gt;
&lt;br /&gt;
Um alle Änderungen zu übernehmen startet man den Raspberry Pi am besten neu:&lt;br /&gt;
* reboot&lt;br /&gt;
Wenn man per SSH verbunden war, wird die Verbindung geschlossen und man muss sich neu verbinden.&lt;br /&gt;
&lt;br /&gt;
== Aktualisierung ==&lt;br /&gt;
Da das Image, das man auf die SD-Karte geschrieben hat, schon etwas veraltet sein kann, sollte man das System über die Paketverwaltung aktualisieren:&lt;br /&gt;
* pacman -Syu&lt;br /&gt;
Dieses Kommando downloadet die Datenbanken und alle neuen Pakete und installiert diese anschließend.&lt;br /&gt;
&lt;br /&gt;
Dieses Kommando sollte man jede Woche mindestens einmal ausführen, um das System aktuell zu halten. Arch Linux ARM muss nie neu installiert werden, allein durch dieses Kommando wird es immer aktuell gehalten.&lt;br /&gt;
&lt;br /&gt;
== Dateisystem vergrößern ==&lt;br /&gt;
Da das Image darauf ausgelegt ist, dass es auch auf nur 2GB großen SD-Karten geschrieben werden kann, ist auch das Haupt-Dateisystem etwa 2GB groß, selbst wenn man Arch Linux ARM auf eine größere SD-Karte geschrieben hat.&lt;br /&gt;
&lt;br /&gt;
Doch das ist kein großes Problem. Um das Haupt-Dateisystem so zu vergrößern, dass es die gesamte SD-Karte ausfüllt, muss man den Raspberry Pi ausschalten und die SD-Karte in einen Cardreader an einem Linux-PC einsetzen.&lt;br /&gt;
&lt;br /&gt;
In der folgenden Anleitung wird angenommen, dass die SD-Karte unter /dev/sdb und das Haupt-Dateisystem der SD-Karte unter /dev/sdb2 ist. Wenn das nicht zutrifft muss man die Pfade entsprechend ändern.&lt;br /&gt;
&lt;br /&gt;
Zuerst muss man sicherstellen, dass keines der Dateisysteme gemountet ist:&lt;br /&gt;
* umount /dev/sdb1&lt;br /&gt;
* umount /dev/sdb2&lt;br /&gt;
Danach muss man einen Dateisystem-Test durchführen:&lt;br /&gt;
* fsck -n /dev/sdb2&lt;br /&gt;
... und das Journal entfernen:&lt;br /&gt;
* tune2fs -O &amp;quot;^has_journal&amp;quot; /dev/sdb2&lt;br /&gt;
Dann bearbeitet man die Partitionstabelle mit fdisk:&lt;br /&gt;
* fdisk /dev/sdb&lt;br /&gt;
Man kommt in eine Kommandozeile, in der man folgendes nacheinander eingibt:&lt;br /&gt;
* d&lt;br /&gt;
* 2&lt;br /&gt;
* n&lt;br /&gt;
* p&lt;br /&gt;
* 2&lt;br /&gt;
* &amp;lt;ohne Eingabe Enter&amp;gt;&lt;br /&gt;
* &amp;lt;ohne Eingabe Enter&amp;gt;&lt;br /&gt;
* w&lt;br /&gt;
Dadurch wird die zweite Partition gelöscht und neu angelegt, und zwar über den gesamten freien Bereich der SD-Karte.&lt;br /&gt;
Dann muss man wieder einen Dateisystem-Test durchführen:&lt;br /&gt;
* e2fsck -f /dev/sdb2&lt;br /&gt;
... und das Dateisystem vergrößern:&lt;br /&gt;
* resize2fs /dev/sdb2&lt;br /&gt;
Jetzt erfolgt erneut ein Dateisystem-Test:&lt;br /&gt;
* fsck -n /dev/sdb2&lt;br /&gt;
... und die Wiederherstellung des Journals:&lt;br /&gt;
* tune2fs -j /dev/sdb2&lt;br /&gt;
Jetzt kann man die SD-Karte aus dem Cardreader nehmen, in den Raspberry Pi einsetzen und ihn starten.&lt;br /&gt;
&lt;br /&gt;
Wenn alles glatt gelaufen ist bootet der Raspberry Pi ganz normal und man hat plötzlich mehr Speicherplatz zur Verfügung. Aus&lt;br /&gt;
&amp;lt;pre&amp;gt;# df -h /    &lt;br /&gt;
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf&lt;br /&gt;
/dev/root       1,8G    805M  869M   49% /&amp;lt;/pre&amp;gt;&lt;br /&gt;
wird bei einer 16GB SD-Karte zum Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;# df -h /    &lt;br /&gt;
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf&lt;br /&gt;
/dev/root        15G    905M   14G    7% /&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Dieser Abschnitt basiert auf einer [http://www.howtoforge.de/anleitung/wie-man-die-grose-von-ext3-partitionen-anpasst-ohne-daten-zu-verlieren/ Anleitung zum Verändern der Größe von ext3-Partitionen von HowtoForge].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Paketverwaltung ==&lt;br /&gt;
Arch Linux ARM setzt pacman als Paket-Manager ein. Das erste Kommando, das zur System-Aktualisierung, wurde schon in einem anderen Kapitel genannt:&lt;br /&gt;
* pacman -Syu&lt;br /&gt;
Dieses setzt sich aus zwei Teil-Kommandos zusammen:&lt;br /&gt;
* pacman -Sy&lt;br /&gt;
zum Downloaden der Paket-Datenbanken und&lt;br /&gt;
* pacman -Su&lt;br /&gt;
zum Downloaden und Installieren der neuen Pakete.&lt;br /&gt;
&lt;br /&gt;
Um ein neues Paket zu installieren verwendet man folgendes Kommando:&lt;br /&gt;
* pacman -S PAKETNAME&lt;br /&gt;
oder um davor noch die Datenbanken zu aktualisieren:&lt;br /&gt;
* pacman -Sy PAKETNAME&lt;br /&gt;
&lt;br /&gt;
Um ein Paket zu löschen verwendet man:&lt;br /&gt;
* pacman -R PAKETNAME&lt;br /&gt;
oder um alle danach nicht mehr benötigten Abhängigkeiten mit zu löschen:&lt;br /&gt;
* pacman -Rs PAKETNAME&lt;br /&gt;
&lt;br /&gt;
Es gibt noch viele weitere Kommandos, z.B. zum Suchen von Paketen, Anzeigen von Informationen, Auflisten von Dateien. Einen Überblick findet man im [https://wiki.archlinux.de/title/Pacman Arch Linux Wiki] oder eine Auflistung aller Optionen in der Manpage:&lt;br /&gt;
* man pacman&lt;br /&gt;
Nach der Installation sind standardmäßig alle fünf Paketquellen von Arch Linux ARM aktiviert, eine Liste aller enthaltenen Pakete findet man [http://archlinuxarm.org/packages auf der Website].&lt;br /&gt;
&lt;br /&gt;
== Datensicherung ==&lt;br /&gt;
Um neue Betriebssysteme auf dem Raspberry Pi auszuprobieren braucht man nicht zehn verschiedene SD-Karten, die man immer wechselt. Es reicht eine SD-Karte, von der man ein Backup macht, bevor man ein neues Betriebssystem aufspielt. Aber auch wenn man das nicht vorhat macht sich eine Datensicherung immer gut.&lt;br /&gt;
&lt;br /&gt;
Dafür gibt es unter Linux verschiedene Möglichkeiten, einmal natürlich das Tool dd, mit dem man den gesamten Inhalt der SD-Karte Byte für Byte auf ein anderes Medium, z.B. die Festplatte, kopieren kann. Hier werden aber auch eigentlich leere Teile der SD-Karte mit gesichert, wodurch bei einer 16GB SD-Karte auch (unkomprimiert) ein 16GB-Backup entsteht, egal wie viel Daten man nun eigentlich auf der Karte gespeichert hat. Abhilfe schafft partclone, das die einzelnen Partitionen sichert, und zwar auch nur die wirklich verwendeten Teile. Die Handhabung ist etwas komplexer als bei dd, eine gute Anleitung und ein ausführlicherer Vergleich der beiden Tools ist auf [http://www.bitblokes.de/2013/03/sd-karte-des-raspberry-pi-sichern-dd-oder-partclone/ bitblokes.de] zu finden.&lt;br /&gt;
&lt;br /&gt;
Unter Windows gibt es das Tool [http://sourceforge.net/projects/win32diskimager/ Win32DiskImager] welches komplette bitweise Images (von SD-Karten und USB-Medien) lesen und schreiben kann.&lt;br /&gt;
&lt;br /&gt;
== SoC-Konfiguration ==&lt;br /&gt;
Generelle Einstellungen für den SoC, die noch vor dem Starten des Betriebssystems geladen werden, kann man in der Datei &#039;&#039;&#039;/boot/config.txt&#039;&#039;&#039; ändern.&lt;br /&gt;
&lt;br /&gt;
Hier ist es z.B. möglich, Parameter für die Video-Ausgabe zu setzen, die Speicher-Aufteilung zwischen CPU und GPU zu ändern oder die CPU zu übertakten. Generell sollte man eher vorsichtig mit diesen Optionen umgehen, da sie bei falscher Verwendung auch mal dazu führen können, dass das Betriebssystem nicht mehr bootet oder das System instabil wird.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Auflistung der möglichen Konfigurations-Optionen findet man auf [http://elinux.org/RPiconfig eLinux.org].&lt;br /&gt;
&lt;br /&gt;
== Die GPIO-Schnittstelle ==&lt;br /&gt;
[[Datei:Raspberry_Pi_GPIO.jpg|thumb|Die GPIO-Schnittstelle (P1-Anschluss)]]&lt;br /&gt;
Für Bastler besonders interessant ist die GPIO-Schnittstelle, diese kann man über verschiedene Wege ansteuern.&lt;br /&gt;
=== Belegung der Schnittstelle ===&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Alle GPIO-Pins haben einen Pegel von 3,3V und sind &#039;&#039;&#039;nicht&#039;&#039;&#039; kompatibel zu 5V! Wenn man versucht, 5V anzuschließen, kann das zur Zerstörung des Raspberry Pi führen! Für Informationen zum Anschließen von 5V-Chips, siehe [[Pegelwandler]] oder [[Raspberry Pi#Erweiterungs-Boards]].&lt;br /&gt;
&amp;lt;gallery heights=&amp;quot;300&amp;quot; widths=&amp;quot;300&amp;quot;&amp;gt;&lt;br /&gt;
Datei:Raspberry_Pi_GPIO.svg|Der P1-Anschluss&lt;br /&gt;
Datei:Raspberry_Pi_GPIO_P2.svg|Der P2-Anschluss (eigentlich das JTAG-Interface der GPU, aber trotzdem mit ein paar nützlichen Pins)&lt;br /&gt;
Datei:Raspberry_Pi_GPIO_P5.svg|Der P5-Anschluss auf der Rückseite der Platine&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im oberen Bild ist die Belegung der Anschlüsse zu sehen, die man als GPIO oder zur Stromversorgung nutzen kann. Die Beschriftungen sind genau so wie auf der Platine angebracht und sollen eine Orientierung ermöglichen.&lt;br /&gt;
Die 3,3V Anschlüsse (P1-1, P1-17, P2-1 und P5-2) dürfen maximal mit 50 mA belastet werden. Bei den 5V Anschlüssen (P1-2, P1-4 und P5-1) gibt es keine feste Grenze, hier kommt es auf den aktuellen Stromverbrauch des restlichen Boards und der Leistung des Netzteils an. Die Pins GPIO2 (P1-3) und GPIO3 (P1-5) sind jeweils mit einem 1,8 kΩ Pull-Up-Widerstand ausgestattet.&lt;br /&gt;
&lt;br /&gt;
Der Anschluss P3 ist noch ein JTAG-Interface, der Anschluss P6 kann mit einem Reset-Knopf ausgestattet werden. Wenn man die beiden Pins verbindet verursacht das einen Soft Reset der CPU.&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht der Alternativ-Funktionen der Pins:&lt;br /&gt;
* P1-03 (GPIO2): I2C SDA (siehe [[Raspberry Pi#Der_I2C-Bus]])&lt;br /&gt;
* P1-05 (GPIO3): I2C SCL (siehe [[Raspberry Pi#Der_I2C-Bus]])&lt;br /&gt;
&lt;br /&gt;
* P1-07 (GPIO4): GPCLK0 (General Purpose Clock)&lt;br /&gt;
&lt;br /&gt;
* P1-08 (GPIO14): UART TXD (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
* P1-10 (GPIO15): UART RXD (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
* P1-11 (GPIO17): UART RTS (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
&lt;br /&gt;
* P1-12 (GPIO18): PWM ([[Pulsweitenmodulation]])&lt;br /&gt;
* P1-13 (GPIO27): PCM (Puls-Code-Modulation) oder GPCLK1 (General Purpose Clock)&lt;br /&gt;
&lt;br /&gt;
* P1-19 (GPIO10): SPI MOSI (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-21 (GPIO9): SPI MISO (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-23 (GPIO11): SPI SCLK (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-24 (GPIO8): SPI Chip Select 0 (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-26 (GPIO7): SPI Chip Select 1 (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per sysfs ===&lt;br /&gt;
Ganz nach den UNIX-Prinzipien gibt es die Möglichkeit, auf die Schnittstelle über Dateien im sysfs zuzugreifen. Dafür existiert das Verzeichnis &#039;&#039;/sys/class/gpio/&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Um einen GPIO-Pin zu verwenden muss man ihn zuerst aktivieren, indem man die Pin-Nummer in &#039;&#039;/sys/class/gpio/export&#039;&#039; schreibt. Wenn man über die Kommandozeile z.B. Pin 4 aktivieren will benutzt man folgendes Kommando:&lt;br /&gt;
* echo 4 &amp;gt; /sys/class/gpio/export&lt;br /&gt;
Dadurch wird automatisch ein Verzeichnis &#039;&#039;/sys/class/gpio/gpio4/&#039;&#039; erstellt. Nun kann man über die darin enthaltene Datei &#039;&#039;direction&#039;&#039; den Pin als Out- bzw. Input festlegen (standardmäßig sind alle Inputs):&lt;br /&gt;
* echo in &amp;gt; /sys/class/gpio/gpio4/direction&lt;br /&gt;
für einen Input und&lt;br /&gt;
* echo out &amp;gt; /sys/class/gpio/gpio4/direction&lt;br /&gt;
für einen Output. Jetzt kann man den Wert des Pins auslesen:&lt;br /&gt;
* cat /sys/class/gpio/gpio4/value&lt;br /&gt;
oder ihn setzen, wenn es ein Output-Pin ist:&lt;br /&gt;
* echo 1 &amp;gt; /sys/class/gpio/gpio4/value&lt;br /&gt;
* echo 0 &amp;gt; /sys/class/gpio/gpio4/value&lt;br /&gt;
Wenn man den Pin nicht mehr benutzen will sollte man ihn wieder deaktivieren:&lt;br /&gt;
* echo 4 &amp;gt; /sys/class/gpio/unexport&lt;br /&gt;
Dadurch verschwindet das Verzeichnis &#039;&#039;/sys/class/gpio/gpio4/&#039;&#039; wieder. All das kann man natürlich nicht nur auf der Shell verwenden, sondern auch in jeder Programmiersprache, indem man einfach in die entsprechenden Dateien schreibt bzw. aus ihnen liest.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Shell-Blinklicht ===&lt;br /&gt;
Dieses kleine Shellscript aktiviert den GPIO-Pin, den man als Argument übergeben hat, setzt ihn als Output und schaltet ihn alle 500ms um:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
## Check arguments:&lt;br /&gt;
if [[ ! $# -eq 1 ]]; then&lt;br /&gt;
        echo &amp;quot;Usage: $0 GPIO&amp;quot; &amp;gt; /dev/stderr&lt;br /&gt;
        exit 1&lt;br /&gt;
fi&lt;br /&gt;
## Activate GPIO:&lt;br /&gt;
echo &amp;quot;Activating GPIO$1&amp;quot;&lt;br /&gt;
echo $1 &amp;gt; /sys/class/gpio/export || exit 1&lt;br /&gt;
## Create trap:&lt;br /&gt;
trap &amp;quot;echo &#039;Deactivating GPIO$1&#039; &amp;amp;&amp;amp; echo in &amp;gt; /sys/class/gpio/gpio$1/direction &amp;amp;&amp;amp; echo $1 &amp;gt; /sys/class/gpio/unexport&amp;quot; EXIT&lt;br /&gt;
## Set as output:&lt;br /&gt;
echo &amp;quot;out&amp;quot; &amp;gt; /sys/class/gpio/gpio$1/direction || exit 1&lt;br /&gt;
## Reset value:&lt;br /&gt;
echo 0 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
## Blink loop:&lt;br /&gt;
while true; do&lt;br /&gt;
        echo &amp;quot;Switching GPIO$1 on&amp;quot;&lt;br /&gt;
        echo 1 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
        sleep 0.5&lt;br /&gt;
        echo &amp;quot;Switching GPIO$1 off&amp;quot;&lt;br /&gt;
        echo 0 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
        sleep 0.5&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn man an dem entsprechenden Pin eine LED anschließt eignet sich das Script gut zum Testen der GPIO-Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Python-Modul ===&lt;br /&gt;
Mit dem Modul [https://pypi.python.org/pypi/RPi.GPIO RPi.GPIO] kann man unter Python die GPIO-Pins steuern und auslesen. Auf der Projekt-Website gibt es auch ein Beispiel, mit dem man die Handhabung schnell lernt.&lt;br /&gt;
&lt;br /&gt;
Jedes Python-Script, das dieses Modul nutzen will, muss als root laufen.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit Java ===&lt;br /&gt;
Das [http://pi4j.com/ Pi4J-Projekt] ermöglicht unter Java die Verwendung der GPIO-Pins, dem UART, des I2C-Bus und der SPI-Schnittstelle. Eine ausführliche Anleitung gibt es auf der Website.&lt;br /&gt;
&lt;br /&gt;
Jedes Java-Programm, dass dieses Modul nutzen will, muss als root laufen.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit WiringPi ===&lt;br /&gt;
[https://projects.drogon.net/raspberry-pi/wiringpi/ WiringPi] bietet ein Kommandozeilen-Tool (&#039;&#039;&#039;gpio&#039;&#039;&#039;, Details siehe &#039;&#039;&#039;man gpio&#039;&#039;&#039;), das man auch als normaler Nutzer (ohne root-Rechte) verwenden kann und eine C-Bibliothek (Header-Datei &#039;&#039;wiringPi.h&#039;&#039; und Linker-Option &#039;&#039;-lwiringPi&#039;&#039;), mit der man die GPIO-Pins in C-Programmen verwenden kann.&lt;br /&gt;
&lt;br /&gt;
Auf der Website gibt es Beispiele und eine Installationsanleitung. Bei Arch Linux ARM ist es auch in den Paketquellen und kann per Paketverwaltung installiert werden:&lt;br /&gt;
* pacman -S wiringpi&lt;br /&gt;
&lt;br /&gt;
== Der I2C-Bus ==&lt;br /&gt;
Auch auf den I2C-Bus des Raspberry Pi kann man zugreifen. Die Pins dafür liegen an P1-03 (GPIO2, SDA) und P1-05 (GPIO3, SCL).&lt;br /&gt;
=== Ansteuerung per devfs ===&lt;br /&gt;
Den I2C-Bus kann man direkt über die Datei &#039;&#039;&#039;/dev/i2c-0&#039;&#039;&#039; erreichen.&lt;br /&gt;
=== Ansteuerung per i2c-tools ===&lt;br /&gt;
Komfortabler geht das aber über die i2c-tools. Dazu muss man erst einmal das Paket aus den Arch Linux ARM Paketquellen installieren:&lt;br /&gt;
* pacman -S i2c-tools&lt;br /&gt;
Nun muss man die nötigen Kernel-Module laden:&lt;br /&gt;
* modprobe i2c-bcm2708&lt;br /&gt;
* modprobe i2c-dev&lt;br /&gt;
Soll das beim Systemstart automatisch passieren, legt man eine *.conf-Datei im Verzeichnis &#039;&#039;&#039;/etc/modules-load.d/&#039;&#039;&#039; mit folgendem Inhalt an:&lt;br /&gt;
&amp;lt;pre&amp;gt;i2c-bcm2708&lt;br /&gt;
i2c-dev&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt sollten folgende Kommandozeilen-Tools funktionieren:&lt;br /&gt;
* &#039;&#039;&#039;i2cdetect&#039;&#039;&#039; zum Auflisten der I2C-Busse und zum Anzeigen der Eigenschaften und Geräte&lt;br /&gt;
* &#039;&#039;&#039;i2cdump&#039;&#039;&#039; zum Herausfinden von I2C-Registern&lt;br /&gt;
* &#039;&#039;&#039;i2cget&#039;&#039;&#039; zum Lesen von I2C-Registern&lt;br /&gt;
* &#039;&#039;&#039;i2cset&#039;&#039;&#039; zum Setzen von I2C-Registern&lt;br /&gt;
Die Manpages und Hilfe-Meldungen der einzelnen Tools geben Auskunft über die genaue Verwendung. Wichtig ist, dass man den I2C-Bus 0, &#039;&#039;&#039;nicht&#039;&#039;&#039; den I2C-Bus 1 verwendet. Ein kleines Beispiel gibt es auch in der Ausgabe 5 des MagPi im Artikel „Temperature Sensor“. Dort wird ein Temperatursensor per I2C ausgelesen.&lt;br /&gt;
&lt;br /&gt;
== Der UART ==&lt;br /&gt;
Die Pins für den UART liegen auch an P1. TXD an Pin 08 (GPIO14), RXD an Pin 10 (GPIO15) und RTS an Pin 11 (GPIO17).&lt;br /&gt;
&lt;br /&gt;
Zugreifen kann man über das devfs, genauer gesagt über die Datei &#039;&#039;&#039;/dev/ttyAMA0&#039;&#039;&#039;. Um zu testen, ob das funktioniert, kann man minicom benutzen:&lt;br /&gt;
* pacman -S minicom&lt;br /&gt;
* minicom -b 115200 -o -D /dev/ttyAMA0&lt;br /&gt;
&lt;br /&gt;
== Die SPI-Schnittstelle ==&lt;br /&gt;
Die Pins für die SPI-Schnittstelle liegen genau wie I2C und UART an P1. MOSI an Pin 19 (GPIO10), MISO an Pin 21 (GPIO9), SCLK an Pin 23 (GPIO11), CS0 an Pin 24 (GPIO8) und CS1 an Pin 26 (GPIO7).&lt;br /&gt;
&lt;br /&gt;
Der Zugriff erfolgt über die Dateien &#039;&#039;&#039;/dev/spidev0.0&#039;&#039;&#039; (Gerät an CS0) und &#039;&#039;&#039;/dev/spidev0.1&#039;&#039;&#039; (Gerät an CS1), in die man direkt schreiben und aus denen man auch direkt lesen kann.&lt;br /&gt;
&lt;br /&gt;
== Audio-Ausgabe ==&lt;br /&gt;
[[Datei:Raspberry_Pi_Audio.jpg|thumb|Der 3,5mm Klinke-Anschluss]]&lt;br /&gt;
Der Raspberry Pi verfügt über zwei Möglichkeiten der Audio-Ausgabe, einmal digital per HDMI und wenn kein Display angeschlossen ist wird automatisch auf die analoge Ausgabe per 3,5mm-Klinken-Anschluss umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Nach der Installation von Arch Linux ARM funktioniert jedoch vorerst keine der beiden Möglichkeiten, da das nötige Kernel-Modul nicht automatisch geladen wird.&lt;br /&gt;
&lt;br /&gt;
Manuell lässt es sich mit &#039;&#039;&#039;modprobe&#039;&#039;&#039; laden, beim nächsten Neustart ist das Problem dann jedoch wieder da:&lt;br /&gt;
* modprobe snd_bcm2835&lt;br /&gt;
Wenn man will, dass das Modul bei jedem Start automatisch geladen wird, muss man eine *.conf Datei im Verzeichnis /etc/modules-load.d/ erstellen und dort den Namen des Moduls hineinschreiben. Es bietet sich z.B. der Name sound.conf an, über die Kommandozeile ist die Datei schnell erstellt:&lt;br /&gt;
* echo &amp;quot;snd_bcm2835&amp;quot; &amp;gt; /etc/modules-load.d/sound.conf&lt;br /&gt;
Jetzt sollte die Audio-Ausgabe problemlos funktionieren. Testen kann man das beispielsweise per &#039;&#039;&#039;aplay&#039;&#039;&#039;, das im Paket &#039;&#039;&#039;alsa-utils&#039;&#039;&#039; enthalten ist:&lt;br /&gt;
* pacman -S alsa-utils&lt;br /&gt;
* aplay name_der_datei.wav&lt;br /&gt;
In diesem Paket ist auch &#039;&#039;&#039;alsamixer&#039;&#039;&#039; enthalten, ein Programm mit dem man über eine ncurses-Oberfläche die Ausgabe-Lautstärke regeln kann.&lt;br /&gt;
&lt;br /&gt;
Man muss beachten, dass nur &#039;&#039;&#039;root&#039;&#039;&#039; und Nutzer in der Gruppe &#039;&#039;&#039;audio&#039;&#039;&#039; auf die Soundkarte zugreifen können. Wenn man nicht als &#039;&#039;&#039;root&#039;&#039;&#039; arbeiten will sollte man seinen Benutzer der Gruppe hinzufügen:&lt;br /&gt;
* gpasswd -a USERNAME audio&lt;br /&gt;
&lt;br /&gt;
=== Standard-Ausgabegerät ===&lt;br /&gt;
Normalerweise wird der Sound automatisch über HDMI ausgegeben, wenn das angeschlossene Gerät das unterstützt, andernfalls über den Klinke-Anschluss. Will man das ändern, z.B. weil man einen Monitor per HDMI angeschlossen hat, den Sound aber trotzdem über die Anlage hören will, geht das ganz schnell über die Kommandozeile:&lt;br /&gt;
* amixer -c 0 cset numid=3 &amp;lt;nummer&amp;gt;&lt;br /&gt;
Dabei muss man &#039;&#039;&amp;lt;nummer&amp;gt;&#039;&#039; durch eine 0 für automatisches Wechseln, eine 1 für den Klinke-Anschluss oder eine 2 für den HDMI-Anschluss ersetzen. Soll das bei jedem Systemstart automatisch ausgeführt werden, erstellt man bei Arch Linux eine Service-Datei für systemd, z.B. so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Set Default Audio Output&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=oneshot&lt;br /&gt;
ExecStart=/usr/bin/amixer -c 0 cset numid=3 &amp;lt;nummer&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier ersetzt man wieder &#039;&#039;&amp;lt;nummer&amp;gt;&#039;&#039; durch das gewünschte Ziel, speichert die Datei unter &#039;&#039;/etc/systemd/system/default-audio.service&#039;&#039; ab und aktiviert den soeben erstellten Service noch:&lt;br /&gt;
* systemctl enable default-audio.service&lt;br /&gt;
Ab dem nächsten Systemstart wird dadurch das Ausgabegerät automatisch eingestellt.&lt;br /&gt;
&lt;br /&gt;
== Erweiterungs-Boards ==&lt;br /&gt;
Für die GPIO-Schnittstelle des Raspberry Pi gibt es verschiedene Erweiterungs-Boards, die zusätzliche Schnittstellen bereitstellen und 3,3V zu 5V Pegelwandler besitzen. Durch diese kann man den Raspberry Pi in bestehende, 5V-basierte Schaltungen und Projekte einbinden.&lt;br /&gt;
&lt;br /&gt;
=== Prototyping-Board für den Raspberry Pi ===&lt;br /&gt;
In der Ausgabe 16 des embedded projects journal wurde auf Seite 13 ein Prototyping-Board für den Raspberry Pi vorgestellt.&lt;br /&gt;
&lt;br /&gt;
Das Board besitzt eine Spannungsversorgung, an die man 7 bis 15V anschließen kann und die auch den Raspberry Pi mitversorgt. Dadurch wird ein per microUSB angeschlossenes Netzteil überflüssig. Außerdem gibt es einen Pegelwandler, der acht GPIO-Pins des Raspberry Pi als 5V-GPIO-Pins zur Verfügung stellt und einen I2C-Pegelwandler, durch den auch hier der Anschluss von 5V-Chips möglich wird.&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es im embedded projects journal und auf der [http://bastelstube.rocci.net/projects/MBS19_RasPi-GPIO/RasPi-GPIO.html Website des Projekts].&lt;br /&gt;
&lt;br /&gt;
=== Berrybush Pi ===&lt;br /&gt;
Ebenfalls in der Ausgabe 16 des embedded projects journal wurde auf Seite 28 der „Berrybush Pi“ vorgestellt, der ein paar mehr Schnittstellen als das Prototyping-Board bietet.&lt;br /&gt;
&lt;br /&gt;
Auch dieses Board bietet eine Spannungsversorgung für den Raspberry Pi und acht 5V-GPIO-Pins, diese sind jedoch mit Darlington-Transistoren und LEDs zur Status-Anzeige ausgestattet. Außerdem gibt es eine 3,3V (sic) SPI-Schnittstelle, die einfach zum Raspberry Pi durchgeschleift wird, einen 5V-I2C-Bus (mit Pegelwandler zum Raspberry Pi), einen RS485-Bus und eine RS232-Schnittstelle, die jedoch in den RS485-Bus gespeist wird.&lt;br /&gt;
&lt;br /&gt;
Als kleines Highlight gibt es noch die Möglichkeit, ein LCD anzuschließen. Dafür sind Portexpander und Potentiometer für Helligkeit und Kontrast vorhanden.&lt;br /&gt;
&lt;br /&gt;
Genauere Informationen, Schaltpläne und Bilder gibt es im embedded projects journal.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/epj-16-seite-28 Weitere Informationen]&lt;br /&gt;
&lt;br /&gt;
=== Arduino Shield Bridge ===&lt;br /&gt;
Mit diesem Board aus Spanien kann man Arduino-Aufsteckplatinen mit dem Raspberry Pi verwenden.&lt;br /&gt;
[http://www.cooking-hacks.com/index.php/raspberry-pi-to-arduino-shield-connection-bridge.html Cooking-hacks.com]&lt;br /&gt;
&lt;br /&gt;
Dazu existiert eine Library &amp;quot;arduPi&amp;quot;:&lt;br /&gt;
&amp;quot;In order to make complete the compatibility we have created the arduPi library which allows to use Raspberry with the same code used in Arduino. To do so, we have implemented conversion functions so that you can control in the same way as in Arduino all the I/O interfaces: i2C, SPI, UART, analog, digital, in Raspberry Pi.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== I/O Module seriell/USB ===&lt;br /&gt;
&lt;br /&gt;
Alternativ bieten sich Schnittstellen-Erweiterungen über seriellen Port oder USB an, zum Beispiel mit einem handelsüblichen AVR Mikrocontroller-Modul und der Firmware von Stefan Frings&lt;br /&gt;
[http://stefanfrings.de/avr_io/].&lt;br /&gt;
&lt;br /&gt;
=== Gertboard ===&lt;br /&gt;
Webseite: http://www.raspberrypi.org/archives/tag/gertboard&lt;br /&gt;
Eine I/O-Aufsteckplatine mit zusätzlichem Mikrocontroller (bisher PIC, jetzt ATMega)&lt;br /&gt;
&lt;br /&gt;
== Projekte ==&lt;br /&gt;
Um den Raspberry Pi hat sich inzwischen eine große Community gebildet, von der immer wieder neue Projekte mit und Erweiterungen für den Raspberry Pi entwickelt werden. Eine Quelle, über die man von den Projekten erfährt ist z.B. der offizielle [http://www.raspberrypi.org/ Raspberry Pi Blog], aber es gibt auch noch viele andere Möglichkeiten, sich zu informieren.&lt;br /&gt;
&lt;br /&gt;
Einige dieser Möglichkeiten und einige ausgewählte Projekte werden in diesem Artikel vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== Pideas ===&lt;br /&gt;
[http://pideas.co.uk/ Pideas] ist eine Seite, auf der Projekte für den Raspberry Pi gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
=== The MagPi ===&lt;br /&gt;
[http://www.themagpi.com/ The MagPi] ist ein freies Online-Magazin, das (normalerweise) monatlich erscheint und in dem interessante Projekte genauer vorgestellt werden. Außerdem gibt es Programmier-Tutorials, Anleitungen für Linux-Software und Interviews.&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 1 ====&lt;br /&gt;
Die Ausgabe 1 vom Mai 2012 behandelt folgende Themen:&lt;br /&gt;
* „The Pioneers“ - eine kleine Übersicht, wie es zur Entwicklung des Raspberry Pi kam&lt;br /&gt;
* „Skutter“ - ein Roboterarm für den Raspberry Pi&lt;br /&gt;
* „RacyPy“ - eine Linux Live-CD, basierend auf Puppy Linux und speziell angepasst für einen leichten Einstieg in die Programmierung&lt;br /&gt;
* eine Anleitung zum Installieren von Debian in einer virtuellen Maschine&lt;br /&gt;
* „Scratch“ - eine Programmiersprache und Entwicklungsumgebung zum kinderleichten Einstieg in die Programmierung&lt;br /&gt;
* „the python pit“ - ein Einstieg in die Programmierung mit Python&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 2 ====&lt;br /&gt;
Die Ausgabe 2 vom Juni 2012 behandelt folgende Themen:&lt;br /&gt;
* „PiSetup“ - eine Anleitung zum Einrichten des Raspberry Pi&lt;br /&gt;
* „In Control“ - ein Einstieg in die GPIO-Programmierung mit dem Raspberry Pi und Python&lt;br /&gt;
* „Pi Dissection“ - ein genauerer Blick auf die Einzelteile des Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Skutter“ aus Ausgabe 1&lt;br /&gt;
* „Command Line Clinic“ - eine Einführung in die Benutzung der Linux-Shell&lt;br /&gt;
* „Computer Music“ - eine Anleitung zur Installation von „Schism Tracker“, einem Sequencer-Programm&lt;br /&gt;
* Fortsetzung der Debian-Anleitung aus Ausgabe 1, diesmal mit einer Anleitung zum Einrichten und Verwenden von Python&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 3 ====&lt;br /&gt;
Die Ausgabe 3 vom Juli 2012 behandelt folgende Themen:&lt;br /&gt;
* eine Anleitung zum automatischen Starten der grafischen Oberfläche von Debian&lt;br /&gt;
* Fortsetzung von „In Control“ aus Ausgabe 2&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2&lt;br /&gt;
* Fortsetzung von „Skutter“ aus Ausgabe 1 und 2&lt;br /&gt;
* „To Protect and Serve your Raspberry Pi“ - eine einfache Anleitung, um die GPIO-Pins des Raspberry Pi vor Kurzschlüssen zu schützen&lt;br /&gt;
* „Meeting Pi“ - Vorschäge, wie man Kinder an den Raspberry Pi heranführen kann&lt;br /&gt;
* „The C Cave“ - ein C-Tutorial&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1 und 2&lt;br /&gt;
* „Programming Fundamentals“ - ein Artikel, der die Grundlagen (fast) aller Programmiersprachen auf einfachem Weg zeigen will&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 4 ====&lt;br /&gt;
Die Ausgabe 4 vom August 2012 behandelt folgende Themen:&lt;br /&gt;
* Fortsetzung von „In Control“ aus Ausgabe 2 und 3&lt;br /&gt;
* „3-Axis Accelerometer“ - eine Anleitung für einen Beschleunigungs-Sensor am Raspberry Pi&lt;br /&gt;
* „Kernow Pi Launch“ - ein Artikel darüber, wie der Raspberry Pi an Schulen gebracht wurde&lt;br /&gt;
* „MagPi Exclusive Interview“ - ein Interview mit Eben und Liz von der Raspberry Pi Foundation&lt;br /&gt;
* „How to customise your LXDE menu“ - eine Anleitung zum Anpassen von LXDE&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2 und 3&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1 und 3&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2 und 3&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 5 ====&lt;br /&gt;
Die Ausgabe 5 vom September 2012 behandelt folgende Themen:&lt;br /&gt;
* „Steady Hands“ - eine Anleitung zum Bau eines „Heißer Draht“-Spiels mit dem Raspberry Pi&lt;br /&gt;
* „Temperature Sensor“ - eine Anleitung zum Anschluss eines I2C-Temperatursensors an den Raspberry Pi&lt;br /&gt;
* „XBMC Media Center“ - eine Einführung in Rasbbmc, einer HTPC-Distribution für den Raspberry Pi&lt;br /&gt;
* „Squeezy or Wheezy?“ - ein Vergleich von Debian 6 „Squeeze“ und Debian 7 „Wheezy“&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2, 3 und 4&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3 und 4&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3 und 4&lt;br /&gt;
* „RasPiThon 2012“ - ein Bericht über den Rasbperry Pi Programmier-Marathon&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3 und 4&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 6 ====&lt;br /&gt;
Die Ausgabe 6 vom Oktober 2012 behandelt folgende Themen:&lt;br /&gt;
* „Skutter Returns“ - Fortsetzung von „Skutter“ aus Ausgabe 1, 2 und 3&lt;br /&gt;
* „Portable Power for your Pi“ - eine Anleitung für eine mobile Stromversorgung&lt;br /&gt;
* „Star Letter: An FET Buffer Stage for GPIO Access“ - eine Möglichkeit, wie man die GPIO-Pins des Raspberry Pi belastbarer machen kann&lt;br /&gt;
* „Pumpkin Pi“ - ein Kürbis mit Bewegungs-Sensor und RGB-LEDs&lt;br /&gt;
* „Camera Pi“ - ein Interview mit David Hunt, einem Bastler, der seine Kamera mit einem Raspberry Pi ausgestattet hat&lt;br /&gt;
* „Our Raspberry Pi Summer“ - ein Beispiel für ein kleines Spiel, das mit Scratch programmiert wurde&lt;br /&gt;
* „Baby steps in a Big World“ - ein Ada-Tutorial&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3, 4 und 5&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4 und 5&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4 und 5&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 7 ====&lt;br /&gt;
Die Ausgabe 7 vom November 2012 behandelt folgende Themen:&lt;br /&gt;
* „Raspberry Pi &amp;amp; Arduino“ - ein Artikel über die Kommunikation zwischen Arduino und Raspberry Pi mithilfe von Python&lt;br /&gt;
* „A little Ray of Sunshine“ - eine Solar-Stromversorgung für den Raspberry Pi und andere USB-Geräte&lt;br /&gt;
* „The Raspberry Ladder Board“ - ein Board zum Einstieg in die GPIO-Programmierung mit dem Raspberry Pi&lt;br /&gt;
* „Interrupts and Other Activities with GPIO Pins” - eine Anleitung für Interrupts und GPIO-Pins im Allgemeinen auf dem Raspberry Pi&lt;br /&gt;
* „Raspbian“ - ein Interview mit Mike Thompson, dem Erfinder von Raspbian&lt;br /&gt;
* „Turbopi“ - eine Anleitung zum Übertakten des Raspberry Pi&lt;br /&gt;
* „Pi-Evolution“ - eine Übersicht über die Entwicklung des Raspberry Pi&lt;br /&gt;
* „Make“ - eine Einführung in GNU Make und Makefiles&lt;br /&gt;
* „Welcome to the C++ Cache!“ - eine Einführung in C++&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5 und 6&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5 und 6&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 8 ====&lt;br /&gt;
Die Ausgabe 8 vom Dezember 2012 behandelt folgende Themen:&lt;br /&gt;
* „Skutter Returns“ - Fortsetzung von „Skutter“ aus Ausgabe 1, 2 und 3 bzw. „Skutter Returns“ aus Ausgabe 6&lt;br /&gt;
* „The Santa Trap“ - Heimautomation mit dem Raspberry Pi&lt;br /&gt;
* „Control your Arduino board with Raspberry Pi and Python“ - ein Python-Modul zum Steuern eines Arduino&lt;br /&gt;
* „PiGauge“ - eine Anleitung, wie man Servo-Motoren übers Internet mithilfe des Raspberry Pi steuern kann&lt;br /&gt;
* „Quick2Wire“ - Erweiterungs-Board für den Raspberry Pi&lt;br /&gt;
* „Kickstarter“ - eine Vorstellung der Kickstarter-Kampagne, um The MagPi auf Papier zu bannen&lt;br /&gt;
* „Programming the Raspberry Pi: Getting Started with Python“ - eine kurze Vorstellung des gleichnamigen Buches von Simon Monk&lt;br /&gt;
* „pibow interview“ - ein Interview mit den Erfindern des PiBow, einem Gehäuse für den Raspberry Pi&lt;br /&gt;
* „CESIL Pi“ - ein CESIL Programmier-Tutorial für den Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Welcome to the C++ Cache!“ aus Ausgabe 7&lt;br /&gt;
* Fortsetzung von „Baby steps in a Big World“ aus Ausgabe 6&lt;br /&gt;
* „MySQL Database Bootcamp“ - eine Einführung in MySQL&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6 und 7&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 9 ====&lt;br /&gt;
Die Ausgabe 9 vom Februar 2013 (Januar wurde ausgelassen) behandelt folgende Themen:&lt;br /&gt;
* „adafruit Interview“ - Interview mit der Gründerin von adafruit, einem Shop für DIY Elektronik&lt;br /&gt;
* „WebIOPi“ - eine REST-Schnittstelle, um die GPIO-Pins des Raspberry Pi per Javascript zu steuern&lt;br /&gt;
* „Backup your Raspberry Pi“ - eine Anleitung für die Datensicherung der SD-Karte&lt;br /&gt;
* „Pi Interface Board Review Quick2Wire“ - ein genauerer Blick auf das Erweiterungs-Board „Quick2Wire“, das in Ausgabe 8 schon kurz gezeigt wurde&lt;br /&gt;
* „RISC OS Pi“ - eine Anleitung zur Installation von RISC OS auf dem Raspberry Pi&lt;br /&gt;
* „Installing &amp;amp; Configuring ArchLinux“ - eine Anleitung zur Installation von Arch Linux auf dem Raspberry Pi, die auch die Installation von XFCE umfasst&lt;br /&gt;
* „Introducing Vala“ - ein kleines Vala Programmier-Tutorial für den Raspberry Pi und das LedBorg Erweiterungs-Board&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3, 4, 5 und 6&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6 und 7&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6, 7 und 8&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 10 ====&lt;br /&gt;
Die Ausgabe 10 vom März 2013 behandelt folgende Themen:&lt;br /&gt;
* Fortsetzung von „WebIOPi“ aus Ausgabe 9&lt;br /&gt;
* „Try a Cocktail of Projects“ - ein Artikel, der verschiedene Erweiterungs-Möglichkeiten für den Raspberry Pi zeigt&lt;br /&gt;
* „Backing up - part 2“ - Fortsetzung von „Backup your Raspberry Pi“ aus Ausgabe 9&lt;br /&gt;
* „Bash Gaffer Tape“ - ein Bash-Tutorial&lt;br /&gt;
* „Charm on the Raspberry Pi“ - ein Charm Programmier-Tutorial für den Raspberry Pi&lt;br /&gt;
* „C++ Cache”: Fortsetzung von „Welcome to the C++ Cache!“ aus Ausgabe 7 und 8&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6, 7 und 9&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6, 7, 8 und 9&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 11 ====&lt;br /&gt;
Die Ausgabe 11 vom April 2013 behandelt folgende Themen:&lt;br /&gt;
* Heizungssteuerung mithilfe des Raspberry Pi über ein Smartphone&lt;br /&gt;
* eine Vorstellung vom „Power and I/O Expansion Board“&lt;br /&gt;
* eine Anleitung zum Einrichten eines WLAN-Access-Points auf dem Raspberry Pi (per USB-WLAN-Dongle)&lt;br /&gt;
* Vorstellung von der Minecraft Pi-Edition&lt;br /&gt;
* „Printing with Cups“: Configuring Cups&lt;br /&gt;
* „Setting up a simple intranet“ mit einer Anleitung zum Einrichten vom Apache HTTPD&lt;br /&gt;
* eine Einführung in die Assembler-Programmierung auf dem Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Charm on the Raspberry Pi“ aus Ausgabe 10&lt;br /&gt;
* kurze Anleitung zum Verwenden von Farben auf der Kommandozeile&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6, 7, 9 und 10&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Pi multi-effects ===&lt;br /&gt;
Ein Musiker hat mit einem Raspberry Pi, einem Arduino Uno und einer USB-Soundkarte ein Gitarren-Effektboard gebaut und das Ganze in seinem Blog [https://guitarextended.wordpress.com/2013/01/31/raspberry-pi-multi-effects-overview-of-the-setup/ beschrieben]. Das alles sieht im [https://www.youtube.com/watch?v=NwJNeouLqgQ Video] noch etwas sperrig aus, es sollte aber kein Problem sein, die Soundkarte und den Raspberry Pi auch noch mit ins Effektboard zu integrieren.&lt;br /&gt;
&lt;br /&gt;
Als Software verwendet er Raspbian, also das Standard-Betriebssystem des Raspberry Pi, und [http://puredata.info/ Pure Data], eine Echtzeit-Umgebung für Audio-, Video- und Grafik-Verarbeitung. Diese ist jedoch auch in den Arch Linux ARM Paketquellen verfügbar, einem Versuch, das ganze auch mit Arch Linux auszuprobieren, steht also theoretisch nichts im Wege.&lt;br /&gt;
&lt;br /&gt;
Die Installation und Konfiguration von Pure Data hat er ebenfalls in seinem Blog [https://guitarextended.wordpress.com/2013/01/28/rpi-as-guitar-effects-processor-installing-and-configuring-pd/ beschrieben].&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Pi guitar tuner ===&lt;br /&gt;
Ein anderer Nutzer hat ein Gitarren-Stimmgerät gebaut. Dazu hat er einen Raspberry Pi, eine USB-Webcam zur Ton-Aufnahme, eine 7-Segment-Anzeige zur Anzeige des Tons und 15 LEDs zur Anzeige der Abweichung vom Ton verwendet.&lt;br /&gt;
&lt;br /&gt;
Ausgewertet und angesteuert wird das Ganze über ein selbstgeschriebenes Java-Programm, ein Video und ein paar zusätzliche Informationen gibt es auf [https://www.youtube.com/watch?v=oB4F5VhzQf8 YouTube].&lt;br /&gt;
&lt;br /&gt;
=== Circadian Lighting ===&lt;br /&gt;
Auch für Menschen, die sich mit dem Morgen manchmal schwer tun, gibt es ein Projekt mit dem Raspberry Pi, das im Blog [http://www.raspberrypi.org/archives/3267 vorgestellt wurde]. In Verbindung mit ein paar RGB-LEDs kann man den Raspbrry Pi zu einem Wecker machen, der einen nicht mit lautem, nervigen Piepsen aus dem Schlaf reißt, sondern langsam in Verbindung mit einem künstlichen Sonnenaufgang weckt. Fehlt nur noch ein Relais, über das die Kaffeemaschine angeschaltet wird, und der Morgen ist perfekt.&lt;br /&gt;
&lt;br /&gt;
Bei diesem Projekt gibt es sogar den [https://github.com/rasathus/circadianLighting Quellcode], eine [http://rasathus.blogspot.co.uk/2013/01/circadian-lighting-part-one.html Hardware-Liste] und ein [https://www.youtube.com/watch?v=Q9_tqoOkmCU Video], das einige der Funktionen zeigt. Und mit ein paar Python-Kenntnissen kann man alles seinen Bedürfnissen anpassen und erweitern.&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Leaf ===&lt;br /&gt;
Die einfachsten Ideen sind doch manchmal die Besten. „Dr. Monk” hat in seinem Blog eine Pinbelegung des Raspberry Pi [http://www.doctormonk.com/2013/02/raspberry-pi-and-breadboard-raspberry.html veröffentlicht], die (in der richtigen Größe ausgedruckt) genau auf den P1-GPIO-Anschluss des Raspberry Pi passt. So vergisst man nie die Pinbelegung, wenn man gerade mal wieder mit dem Raspberry Pi bastelt und auch ein falsches Anschließen, das zur Zerstörung des Mini-Computers oder der angeschlossenen Teile führen kann, passiert nicht mehr so leicht.&lt;br /&gt;
&lt;br /&gt;
=== raspberry-tools ===&lt;br /&gt;
Eine Tool-Sammlung für den Raspberry Pi, veröffentlicht unter der GNU General Public License Version 2 und gehostet auf [https://github.com/rookies/raspberry-tools github]. Momentan enthält diese Sammlung nur ein Programm, es folgen aber noch weitere nützliche Tools.&lt;br /&gt;
&lt;br /&gt;
==== scope ====&lt;br /&gt;
Scope ist Teil von raspberry-tools. Es zeichnet digitale Signale von einem oder mehreren GPIO-Pins auf und bereitet die Daten für eine Auswertung mit [http://xoscope.sourceforge.net/ xoscope] auf.&lt;br /&gt;
&lt;br /&gt;
Wenn man sich den Quellcode geholt hat, muss man erst einmal das Hauptprogramm für den Raspberry Pi kompilieren. Das geht entweder direkt auf dem Raspberry Pi oder per Cross-Compiler.&lt;br /&gt;
* g++ -o RasPi_logGPIO RasPi_logGPIO.cpp&lt;br /&gt;
Nachdem sowohl die entstandene Datei &#039;&#039;RasPi_logGPIO&#039;&#039; als auch das Script &#039;&#039;RasPi_logGPIO.sh&#039;&#039; im gleichen Ordner auf dem Raspberry Pi sind, kann man die Aufzeichnung starten:&lt;br /&gt;
* ./RasPi_logGPIO.sh [Liste mit GPIO-Pins] &amp;gt; ausgabedatei.txt&lt;br /&gt;
Für die Pins 14, 31 und 23 sieht das Kommando z.B. so aus:&lt;br /&gt;
* ./RasPi_logGPIO.sh 14 31 23 &amp;gt; ausgabedatei.txt&lt;br /&gt;
Die Aufzeichnung kann mit Strg+C gestoppt werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man die entstandene Datei (z.B. hier ausgabedatei.txt) normalisieren und ins xoscope-Format umwandeln, das funktioniert auch problemlos auf einem anderen Computer:&lt;br /&gt;
* ./RasPi_normalizeGPIO.py ausgabedatei.txt &amp;gt; ausgabedatei.dat&lt;br /&gt;
Nun entsteht die Datei &#039;&#039;ausgabedatei.dat&#039;&#039;, die man mit xoscope ansehen kann:&lt;br /&gt;
&amp;lt;gallery heights=&amp;quot;300&amp;quot; widths=&amp;quot;300&amp;quot;&amp;gt;&lt;br /&gt;
Datei:RasPiTools_scope_sample1.png|eine Beispiel-Aufnahme&lt;br /&gt;
Datei:RasPiTools_scope_sample2.png|die gleiche Aufnahme, vergrößert&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
Dazu muss man nur die Datei laden und die entsprechenden Memory-Einträge anzeigen lassen. In diesem Beispiel ist Pin 14 in Mem A, Pin 31 in Mem B und Pin 23 in Mem C, also genau in der Reihenfolge, in der man die Pin-Nummern dem Programm übergeben hat.&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
=== Stromversorgung ===&lt;br /&gt;
[[Datei:Raspberry_Pi_Power.jpg|thumb|Der microUSB-Anschluss zur Stromversorgung]]&lt;br /&gt;
Um den Raspberry Pi mit Strom zu versorgen gibt es verschiedene Möglichkeiten. Entscheidend ist nur, dass möglichst konstante 5V bei bis zu 700 mA auf einem microUSB-Stecker geliefert werden.&lt;br /&gt;
==== mit USB-Netzteil ====&lt;br /&gt;
Wenn man ein genügend starkes USB-Netzteil mit einem microUSB-Kabel verbindet hat man eine passende Stromversorgung für den Raspberry Pi.&lt;br /&gt;
Gut funktioniert zum Beispiel das iPhone-Ladegerät, aber auch andere, günstigere Netzteile sollten keine Probleme machen.&lt;br /&gt;
==== Netzteil selbst bauen ====&lt;br /&gt;
Wenn man kein USB-Netzteil zur Hand hat kann man auch selbst ein Steckernetzteil, das 5V Gleichspannung liefert, mit einem microUSB-Kabel zusammenlöten.&lt;br /&gt;
&lt;br /&gt;
Dafür braucht man erst einmal ein microUSB-Kabel und ein passendes Steckernetzteil. (siehe Abb. 1)&lt;br /&gt;
Man entfernt den USB-A-Stecker und befreit das Kabelende ein Stück von seinem Mantel, es kommen vier Adern zum Vorschein. (siehe Abb. 2)&lt;br /&gt;
Uns interessiert die grüne und die weiße Ader nicht, da wir keine Daten übertragen wollen.&lt;br /&gt;
Jetzt muss auch noch der Stecker vom Netzteil entfernt und die Adern abisoliert werden. (siehe Abb. 3)&lt;br /&gt;
Bei manchen Netzteilen ist eine Markierung für Plus- oder Minus-Pol angebracht. Wenn das nicht der Fall ist, muss man mit einem Multimeter nachmessen. (siehe Abb. 4)&lt;br /&gt;
Nun isoliert man auch noch die schwarze und rote Ader des USB-Kabels ab und verlötet die schwarze Ader mit dem Minus-Pol des Netzteils und die rote Ader mit dem Plus-Pol des Netzteils. Danach packt man um die Adern jeweils einen Schrumpfschlauch zur Isolation. Wenn man um die ganze Verbindung noch einen großen Schrumpfschlauch packt wird die Verbindung nochmal um einiges stabiler. &lt;br /&gt;
(siehe Abb. 5)&lt;br /&gt;
Es ist auch möglich den Raspberry Pi über jedes alte PC Netzteil zu betreiben, bei einem alten 300 Watt Netzteil hat man einen Verlust im Leerlauf von ca. 8 Watt.&lt;br /&gt;
Dafür kann man aber einige andere Komponenten (LEDs, usw.) mitbetreiben.&lt;br /&gt;
Hier ein Link zu einem deutschen Youtube Video:&lt;br /&gt;
http://www.youtube.com/watch?v=hqevlVn1Osk&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau1.jpg|Abb. 1: Die Ausgangsmaterialien&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau2.jpg|Abb. 2: Das USB-Kabel ohne Mantel mit der roten (+5V), schwarzen (GND), grünen (Data+) und weißen (Data-) Ader&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau3.jpg|Abb. 3: Das Netzteil mit abisolierten Kabelenden&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau4.jpg|Abb. 4: Das Multimeter zeigt eine positive Zahl an, daher ist das Kabel mit der roten Klemme der Plus-Pol, das Kabel mit der schwarzen Klemme der Minus-Pol.&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau5.jpg|Abb. 5: Das fertige Netzteil, die Verbindung ist mit einem Schrumpfschlauch geschützt.&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== USB-Geräte ===&lt;br /&gt;
[[Datei:Raspberry_Pi_USB.jpg|thumb|Die beiden USB Host-Anschlüsse]]&lt;br /&gt;
Generell kann man am Raspberry Pi alle USB-Geräte verwenden, die vom Linux-Kernel unterstützt werden und somit auch an anderen Linux-PCs funktionieren.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings zu beachten, dass die USB-Ports nur vergleichsweise wenig Strom liefern können (nicht die vollen 500 mA, die laut USB2.0 Standard maximal möglich sind), für größere Verbraucher wie z.B. 2,5-Zoll-Festplatten ohne externe Stromversorgung muss man daher einen externen USB-Hub mit Stromversorgung verwenden.&lt;br /&gt;
==== Maus &amp;amp; Tastatur ====&lt;br /&gt;
Da es für Mäuse und Tastaturen Standard-Profile gibt, die auch von allen (bis auf extrem wenige Ausnahmefälle) Geräten eingehalten werden, sollte es hier keine Probleme geben. Die Eingabegeräte sollten sofort verwendbar sein und auch Hot-Plugging stellt im Normalfall kein Problem dar.&lt;br /&gt;
==== USB-Sticks &amp;amp; USB-Festplatten ====&lt;br /&gt;
Wenn man seinem Raspberry Pi zusätzlichen Speicherplatz verpassen will bietet es sich an, USB-Sticks bzw. -Festplatten anzuschließen. Da es auch hier Standard-Profile gibt sollten diese sofort vom Linux-Kernel erkannt werden. Einhängen ([https://de.wikipedia.org/wiki/Mounten mounten]) muss man sie jedoch unter Arch Linux ARM generell selbst, wenn man nicht einen [https://de.wikipedia.org/wiki/Fstab fstab-Eintrag] erstellt hat oder eine Desktop-Oberfläche installiert hat, die das automatisch erledigt.&lt;br /&gt;
==== WLAN-Sticks ====&lt;br /&gt;
Wird der WLAN-Stick vom Kernel erkannt (erkennbar daran, dass ein entsprechendes Interface von &#039;&#039;&#039;ifconfig -a&#039;&#039;&#039; aufgelistet wird), kann man ein Netzwerk-Profil mit [https://wiki.archlinux.org/index.php/Netcfg netcfg] (Quasi-Standard unter ArchLinux, bei dem die Profile über Konfigurationsdateien erstellt werden), dem [https://wiki.archlinux.org/index.php/NetworkManager NetworkManager] oder [https://wiki.archlinux.org/index.php/Wicd Wicd] erstellen. Sollte die Karte nicht erkannt werden, hilft eventuell ein Blick auf die [https://wiki.archlinux.org/index.php/Wireless_Setup Wireless Setup] Seite im ArchLinux Wiki.&lt;br /&gt;
==== TV-Karten ====&lt;br /&gt;
Will man den Raspberry Pi als HTPC verwenden, kann es sinnvoll sein, eine TV-Karte per USB anzuschließen.&lt;br /&gt;
Hier gibt es generell 4 Möglichkeiten:&lt;br /&gt;
* &#039;&#039;&#039;DVB-T&#039;&#039;&#039;, also digitales Fernsehen, das per Funk ausgestrahlt wird - kostenlos, jedoch nicht überall verfügbar&lt;br /&gt;
* &#039;&#039;&#039;DVB-S&#039;&#039;&#039;, also digitales Fernsehen, das per Satellit ausgestrahlt wird - kostenlos und generell überall verfügbar, jedoch wird eine Satelliten-Schüssel benötigt&lt;br /&gt;
* &#039;&#039;&#039;DVB-C&#039;&#039;&#039;, also digitales Fernsehen, das über das Kabelnetz ausgestrahlt wird - Kosten für den Kabelanschluss fallen an, Sender-Angebot vom Kabelanbieter abhängig&lt;br /&gt;
* &#039;&#039;&#039;Analog-TV&#039;&#039;&#039;, also analoges Fernsehen, das über das Kabelnetz ausgestrahlt wird - Kosten für den Kabelanschluss fallen an, kein HD, Sender-Angebot vom Kabelanbieter abhängig&lt;br /&gt;
&lt;br /&gt;
Nicht alle Geräte werden von Linux unterstützt, eine Übersicht, die nach Übertragungstechnik (DVB-T/-S/-C oder Analog) und Anschluss-Art (beim Raspberry Pi immer USB) geordnet ist, findet man im [http://linuxtv.org/wiki/index.php/Hardware_Device_Information LinuxTVWiki], zu dem man natürlich auch selbst beitragen kann, wenn man ein Gerät getestet hat.&lt;br /&gt;
==== Soundkarten ====&lt;br /&gt;
Da der Klinke-Ausgang des Raspberry Pi nicht mit einem D/A-Wandler, sondern nur per PWM funktioniert, ist die Sound-Qualität nicht gerade überragend. Wenn man das verbessern will, oder wenn man Sound per Line-Eingang oder per Mikrofon aufnehmen will, bietet es sich an, eine USB-Soundkarte anzuschließen.&lt;br /&gt;
&lt;br /&gt;
Ist man unsicher, ob die Soundkarte unterstützt wird, hilft ein Blick ins [http://www.alsa-project.org/main/index.php/Matrix:Main Wiki vom ALSA-Projekt].&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-02-11/archlinux-hf-2013-02-11.zip.torrent Torrent-Download von Arch Linux ARM 2013-02-11]&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-02-11/archlinux-hf-2013-02-11.zip HTTP-Download von Arch Linux ARM 2013-02-11] (SHA-1: 1d2508908e7d8c899f4a5284e855cb27c17645dc)&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.raspberrypi.org/ offizielle Website des Raspberry Pi] (englisch)&lt;br /&gt;
* [https://de.wikipedia.org/wiki/Raspberry_Pi Wikipedia: Raspberry Pi] (deutsch)&lt;br /&gt;
* [http://www.themagpi.com/ The MagPi] (englisch)&lt;br /&gt;
* [http://archlinuxarm.org/ Arch Linux ARM] (englisch)&lt;br /&gt;
* [https://www.archlinux.org/ Arch Linux] (englisch)&lt;br /&gt;
* [https://de.wikipedia.org/wiki/Arch_Linux Wikipedia: Arch Linux] (deutsch)&lt;br /&gt;
* [http://www.kammerath.net/asterisk-virtuelle-telefonanlage.html Asterisk auf dem Raspberry Pi] (deutsch)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Raspberry Pi]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Raspberry_Pi_Einf%C3%BChrung&amp;diff=78618</id>
		<title>Raspberry Pi Einführung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Raspberry_Pi_Einf%C3%BChrung&amp;diff=78618"/>
		<updated>2013-09-24T09:30:03Z</updated>

		<summary type="html">&lt;p&gt;Mario: Add: Datensicherung SD-Karte -&amp;gt; Win32DiskImager&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Robert Knauer&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig&#039;&#039;&#039;: &#039;&#039;Alle Informationen und Anleitungen in diesem Artikel beziehen sich auf das Model B mit erweitertem Arbeitsspeicher.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Raspberry_Pi.jpg|thumb|Der Raspberry Pi]]&lt;br /&gt;
Der &#039;&#039;&#039;Raspberry Pi&#039;&#039;&#039; ist ein Einplatinen-Computer im Kreditkarten-Format mit einem SoC (System-on-a-Chip) von Broadcom. Dieser verfügt über folgende Eigenschaften:&lt;br /&gt;
* eine 700MHz ARMv6-CPU&lt;br /&gt;
* eine Broadcom VideoCore IV GPU&lt;br /&gt;
* 512 MB Arbeitsspeicher&lt;br /&gt;
Außerdem kann der Raspberry Pi über folgende Schnittstellen mit der Außenwelt kommunizieren:&lt;br /&gt;
* 2 USB 2.0 Host-Anschlüsse&lt;br /&gt;
* einen Composite Video-Ausgang&lt;br /&gt;
* einen HDMI-Ausgang (Video+Audio)&lt;br /&gt;
* einen 3,5mm Klinke-Audio-Ausgang&lt;br /&gt;
* einen SD-/MMC-Karten-Slot (Unterstützung für SDIO und SDHC)&lt;br /&gt;
* einen Ethernet-Port (10/100 MBit)&lt;br /&gt;
* 21 GPIO-Pins, teilweise gedacht als [[UART]], [[SPI]] und [[I2C]]&lt;br /&gt;
Die Energieversorgung wird über einen microUSB-Anschluss gewährleistet. Man sollte jedoch darauf achten, dass der USB-Anschluss, über den man den Raspberry Pi mit Energie versorgt mindestens 700 mA liefern kann, da in der USB 2.0 Spezifikation nur 500 mA vorgesehen sind. In diesem Artikel wird noch darauf eingegangen, wie man sich selbst ein entsprechendes Netzteil bauen kann, alternativ funktioniert auch ein entsprechend starkes USB-Ladegerät in Verbindung mit einem USB-A auf microUSB-B Kabel. (siehe [[Raspberry Pi#Stromversorgung]])&lt;br /&gt;
&lt;br /&gt;
Um diese umfangreichen Möglichkeiten zu nutzen muss man mit einem anderen Computer ein Betriebssystem auf die SD-Karte laden, das dann vom Raspberry Pi gebootet wird. Am naheliegendsten ist hier natürlich Linux (Raspbian, Fedora, Debian und Arch Linux sind offiziell unterstützt), aber auch Plan 9 wurde schon auf dem kleinen Computer [http://www.heise.de/open/meldung/Plan-9-laeuft-auf-dem-Raspberry-1761392.html zum Laufen gebracht].&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel soll Schritt für Schritt der Weg vom Anschließen des Computers über das Einrichten von Arch Linux ARM bis zum Verwenden der verschiedenen Schnittstellen des Raspberry Pi beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Bezugsquellen &amp;amp; Zubehör ==&lt;br /&gt;
Der Raspberry Pi kann über verschiedene Quellen bezogen werden. Wichtig ist darauf zu achten, dass man das Model B mit 512 MB Arbeitsspeicher bestellt, da alle Anleitungen in diesem Artikel nur damit getestet sind und unter anderen Modellen eventuell nicht funktionieren. Ein in Deutschland ziemlich bekannter Elektronik-Versand, bei dem man den Raspberry Pi bestellen kann, ist Pollin, dort ist er zur Zeit für 38,95 € erhältlich. (Bestell-Nr.: 701 697, Stand: 17.06.2013)&lt;br /&gt;
&lt;br /&gt;
Doch neben dem eigentlichen Computer braucht man natürlich noch etwas Zubehör, wenn man es nicht eh schon zu Hause rumliegen hat:&lt;br /&gt;
* ein Netzteil, das 5V bei mindestens 700 mA auf einem microUSB-Stecker liefert (siehe [[Raspberry Pi#Stromversorgung]])&lt;br /&gt;
* eine SD(HC)-Speicherkarte&lt;br /&gt;
* einen SD(HC)-Cardreader an einem anderen Computer&lt;br /&gt;
* ein RJ45-Netzwerkkabel&lt;br /&gt;
* ein Ethernet-Netzwerk mit Zugang zum Internet&lt;br /&gt;
Mit diesem Zubehör kann man nur über SSH zugreifen, wenn man direkt am Raspberry Pi arbeiten will und später evtl. auch eine grafische Oberfläche einrichten will braucht man noch folgendes:&lt;br /&gt;
* ein HDMI-Kabel und einen Fernseher bzw. Monitor mit HDMI-Eingang &#039;&#039;&#039;oder&#039;&#039;&#039;&lt;br /&gt;
* ein Composite-Kabel und einen Fernseher mit entsprechendem Video-Eingang&lt;br /&gt;
* eine USB-Tastatur&lt;br /&gt;
* eine USB-Maus (für eine grafische Oberfläche)&lt;br /&gt;
* evtl. einen aktiven USB-Hub, wenn die Ports des Raspberry Pi nicht genug Leistung für Maus und Tastatur liefern können&lt;br /&gt;
&lt;br /&gt;
== Das Betriebssystem ==&lt;br /&gt;
Als Betriebssystem für die Anleitungen in diesem Artikel wird [http://archlinuxarm.org/ Arch Linux ARM] verwendet, das Meiste funktioniert jedoch auch unter anderen Linux-Distributionen und alles andere zumindest mit kleinen Anpassungen.&lt;br /&gt;
Es basiert auf den Prinzipien und teilweise auch der Software von [https://www.archlinux.org/ Arch Linux], hat aber vollständig unabhängige Paketquellen, in denen nur für ARM kompilierte Software verfügbar ist.&lt;br /&gt;
&lt;br /&gt;
Arch Linux ist ein gutes Betriebssystem für einen Computer wie den Raspberry Pi, da es am Anfang sehr leichtgewichtig ist und vorerst keine grafische Oberfläche installiert hat (was gut ist wenn man den Raspberry Pi als Server verwenden will). Andererseits kann aber auch problemlos eine grafische Oberfläche nachinstalliert werden, wenn man den Raspberry Pi als Desktop-Computer, HTPC oder ähnliches verwenden will.&lt;br /&gt;
&lt;br /&gt;
Man muss jedoch auch sagen, dass Arch Linux kein Betriebssystem für jemanden ist, der Linux noch nie verwendet hat. Man sollte schon etwas Erfahrung mit dem Arbeiten auf der Konsole haben, da grundlegende Dinge über Linux hier nicht erklärt werden.&lt;br /&gt;
Außerdem ist es teilweise notwendig, sich selbst weiterführende Informationen zu Arch Linux anzusehen, die in diesem Artikel verlinkt werden.&lt;br /&gt;
&lt;br /&gt;
== Anschluss &amp;amp; Installation ==&lt;br /&gt;
Nun muss man zuerst alles anschließen. Die Netzwerk-Buchse wird über das Netzwerk-Kabel mit dem Ethernet-Netzwerk verbunden. Hier ist wichtig, dass in dem Netzwerk ein DHCP-Server läuft (also IP-Adressen automatisch bezogen werden können) und Zugriff zum Internet möglich ist. Wenn man sich für die Variante mit eigenem Bildschirm entschieden hat muss man außerdem die HDMI-Buchse mit dem Fernseher oder Monitor bzw. den Composite-Ausgang mit dem Fernseher verbinden und Tastatur und Maus über USB anschließen.&lt;br /&gt;
Dann folgt die Installation von Arch Linux ARM auf die SD-Karte.&lt;br /&gt;
&lt;br /&gt;
Arch Linux ARM für Raspberry Pi kann man über folgende Links downloaden:&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-06-06/archlinux-hf-2013-06-06.zip.torrent Torrent-Download von Arch Linux ARM 2013-02-11]&lt;br /&gt;
* [http://downloads.raspberrypi.org/download.php?file=/images/archlinuxarm/archlinux-hf-2013-06-06/archlinux-hf-2013-06-06.zip HTTP-Download von Arch Linux ARM 2013-06-06]&lt;br /&gt;
Wenn der etwa 185 MB große Download abgeschlossen ist und man über HTTP gedownloadet hat sollte man zuerst die Checksum überprüfen.&lt;br /&gt;
Eine Anleitung dazu findet befindet sich auf der Downloadseite.&lt;br /&gt;
* SHA-1: 706037373bbb33ab6fb0af147a6e795bd04f1503&lt;br /&gt;
Nachdem man das getan oder falls man über Torrent gedownloadet hat (dann passiert das automatisch) kann man das zip-Archiv entpacken.&lt;br /&gt;
&lt;br /&gt;
Man erhält eine etwa 2,0 GB große Datei namens archlinux-hf-2013-06-06.img. Diese muss man nun auf die SD-Karte bringen. Unter Linux funktioniert das mit dem kleinen Programm dd, das in den GNU coreutils enthalten ist und damit auf fast jedem System vorinstalliert sein sollte. Dazu gibt man folgendes in die Konsole ein (als root, /dev/sdX durch den Pfad zur Gerätedatei der SD-Karte ersetzen, Pfad der img-Datei anpassen):&lt;br /&gt;
* dd if=/pfad/zur/archlinux-hf-2013-02-11.img bs=1M of=/dev/sdX&lt;br /&gt;
Unter Windows kann man dafür [https://launchpad.net/win32-image-writer Image Writer for Windows] benutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Danach entfernt man die Karte aus dem Cardreader des Computers und setzt sie in den Raspberry Pi ein. Dann verbindet man die Stromversorgung, indem man den microUSB-Stecker des Netzteils in die entsprechende Buchse auf der Platine steckt.&lt;br /&gt;
&lt;br /&gt;
== Der erste Start ==&lt;br /&gt;
Wenn man ein Display angeschlossen hat, erscheint nach ein paar Sekunden ein Login-Prompt auf dem Bildschirm. Als Username gibt man &#039;&#039;root&#039;&#039; ein, wenn man dann nach dem Passwort gefragt wird gibt man wieder &#039;&#039;root&#039;&#039; ein.&lt;br /&gt;
&lt;br /&gt;
Wenn man kein Display angeschlossen hat, muss man die IP-Adresse, die der Raspberry Pi sich vom DHCP-Server geholt hat, herausfinden. Wenn der DSL-Router diesen Dienst bereitstellt, kann man meistens auf der Web-Oberfläche eine Liste der verteilten IP-Adressen sehen. Alternativ kann man auch mit &#039;&#039;nmap&#039;&#039; einen Netzwerk-Scan machen, der die IP-Adressen von allen Computern im Netzwerk anzeigt. Das funktioniert unter Linux, indem man &#039;&#039;&#039;nmap -sn SUBNETZ&#039;&#039;&#039; im Terminal eingibt. Davor ersetzt man SUBNETZ durch das Subnetz, in dem auch der Raspberry Pi ist, in [https://de.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR-Notation]. Wenn man z.B. in einem Netz ist, in dem IP-Adressen von &#039;&#039;192.168.1.0&#039;&#039; bis &#039;&#039;192.168.1.255&#039;&#039; verteilt werden, muss man &lt;br /&gt;
&#039;&#039;192.168.1.0/24&#039;&#039; verwenden.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse herausgefunden hat, verbindet man sich per SSH zu ihr und loggt sich als root, Passwort root ein. Unter Linux funktioniert das mit&lt;br /&gt;
* ssh root@IP-ADRESSE&lt;br /&gt;
Unter Windows verwendet man am besten [http://www.putty.org/ PuTTY].&lt;br /&gt;
&lt;br /&gt;
Egal für welche Möglichkeit man sich entschieden hat, sitzt man jetzt vor einer root-Shell des Raspberry Pi, erkennbar am Shell-Prompt &#039;&#039;&#039;[root@alarmpi ~]#&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
Nun geht es an die Konfiguration von Arch Linux ARM, diese erfolgt vollständig über das Editieren von Dateien auf der Konsole.&lt;br /&gt;
&lt;br /&gt;
=== Lokalisierung ===&lt;br /&gt;
Man editiert die Datei /etc/locale.gen:&lt;br /&gt;
* nano /etc/locale.gen&lt;br /&gt;
und entfernt die Raute vor der Zeile mit der entsprechenden Sprache-Land-Kodierungs-Kombination, also z.B. vor &#039;&#039;&#039;de_DE.UTF-8 UTF-8&#039;&#039;&#039; für Deutschland.&lt;br /&gt;
Danach speichert man mit Strg+O -&amp;gt; Enter und Strg+X und erstellt die Lokalisierung neu:&lt;br /&gt;
* locale-gen&lt;br /&gt;
Danach editiert man die Datei /etc/locale.conf:&lt;br /&gt;
* nano /etc/locale.conf&lt;br /&gt;
Man ändert &#039;&#039;&#039;LANG=en_US.UTF-8&#039;&#039;&#039; zur entsprechenden Sprache, also z.B. &#039;&#039;&#039;LANG=de_DE.utf8&#039;&#039;&#039; und speichert mit Strg+O -&amp;gt; Enter und Strg+X.&lt;br /&gt;
&lt;br /&gt;
=== Tastatur-Layout ===&lt;br /&gt;
Um das Tastatur-Layout auf der Konsole anzupassen, muss man die Datei /etc/vconsole.conf ändern:&lt;br /&gt;
* nano /etc/vconsole.conf&lt;br /&gt;
Man ändert &#039;&#039;&#039;KEYMAP=us&#039;&#039;&#039; zu z.B. &#039;&#039;&#039;KEYMAP=de-latin1&#039;&#039;&#039; für QWERTZ  und speichert wieder mit Strg+O -&amp;gt; Enter und Strg+X.&lt;br /&gt;
&lt;br /&gt;
=== Zeitzone ===&lt;br /&gt;
Um die Zeitzone zu ändern muss man einen Symlink zu /etc/localtime erstellen:&lt;br /&gt;
* ln -sf /usr/share/zoneinfo/ZEITZONE /etc/localtime&lt;br /&gt;
Also für Deutschland (Europe/Berlin):&lt;br /&gt;
* ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime&lt;br /&gt;
&lt;br /&gt;
Um alle Änderungen zu übernehmen startet man den Raspberry Pi am besten neu:&lt;br /&gt;
* reboot&lt;br /&gt;
Wenn man per SSH verbunden war, wird die Verbindung geschlossen und man muss sich neu verbinden.&lt;br /&gt;
&lt;br /&gt;
== Aktualisierung ==&lt;br /&gt;
Da das Image, das man auf die SD-Karte geschrieben hat, schon etwas veraltet sein kann, sollte man das System über die Paketverwaltung aktualisieren:&lt;br /&gt;
* pacman -Syu&lt;br /&gt;
Dieses Kommando downloadet die Datenbanken und alle neuen Pakete und installiert diese anschließend.&lt;br /&gt;
&lt;br /&gt;
Dieses Kommando sollte man jede Woche mindestens einmal ausführen, um das System aktuell zu halten. Arch Linux ARM muss nie neu installiert werden, allein durch dieses Kommando wird es immer aktuell gehalten.&lt;br /&gt;
&lt;br /&gt;
== Dateisystem vergrößern ==&lt;br /&gt;
Da das Image darauf ausgelegt ist, dass es auch auf nur 2GB großen SD-Karten geschrieben werden kann, ist auch das Haupt-Dateisystem etwa 2GB groß, selbst wenn man Arch Linux ARM auf eine größere SD-Karte geschrieben hat.&lt;br /&gt;
&lt;br /&gt;
Doch das ist kein großes Problem. Um das Haupt-Dateisystem so zu vergrößern, dass es die gesamte SD-Karte ausfüllt, muss man den Raspberry Pi ausschalten und die SD-Karte in einen Cardreader an einem Linux-PC einsetzen.&lt;br /&gt;
&lt;br /&gt;
In der folgenden Anleitung wird angenommen, dass die SD-Karte unter /dev/sdb und das Haupt-Dateisystem der SD-Karte unter /dev/sdb2 ist. Wenn das nicht zutrifft muss man die Pfade entsprechend ändern.&lt;br /&gt;
&lt;br /&gt;
Zuerst muss man sicherstellen, dass keines der Dateisysteme gemountet ist:&lt;br /&gt;
* umount /dev/sdb1&lt;br /&gt;
* umount /dev/sdb2&lt;br /&gt;
Danach muss man einen Dateisystem-Test durchführen:&lt;br /&gt;
* fsck -n /dev/sdb2&lt;br /&gt;
... und das Journal entfernen:&lt;br /&gt;
* tune2fs -O &amp;quot;^has_journal&amp;quot; /dev/sdb2&lt;br /&gt;
Dann bearbeitet man die Partitionstabelle mit fdisk:&lt;br /&gt;
* fdisk /dev/sdb&lt;br /&gt;
Man kommt in eine Kommandozeile, in der man folgendes nacheinander eingibt:&lt;br /&gt;
* d&lt;br /&gt;
* 2&lt;br /&gt;
* n&lt;br /&gt;
* p&lt;br /&gt;
* 2&lt;br /&gt;
* &amp;lt;ohne Eingabe Enter&amp;gt;&lt;br /&gt;
* &amp;lt;ohne Eingabe Enter&amp;gt;&lt;br /&gt;
* w&lt;br /&gt;
Dadurch wird die zweite Partition gelöscht und neu angelegt, und zwar über den gesamten freien Bereich der SD-Karte.&lt;br /&gt;
Dann muss man wieder einen Dateisystem-Test durchführen:&lt;br /&gt;
* e2fsck -f /dev/sdb2&lt;br /&gt;
... und das Dateisystem vergrößern:&lt;br /&gt;
* resize2fs /dev/sdb2&lt;br /&gt;
Jetzt erfolgt erneut ein Dateisystem-Test:&lt;br /&gt;
* fsck -n /dev/sdb2&lt;br /&gt;
... und die Wiederherstellung des Journals:&lt;br /&gt;
* tune2fs -j /dev/sdb2&lt;br /&gt;
Jetzt kann man die SD-Karte aus dem Cardreader nehmen, in den Raspberry Pi einsetzen und ihn starten.&lt;br /&gt;
&lt;br /&gt;
Wenn alles glatt gelaufen ist bootet der Raspberry Pi ganz normal und man hat plötzlich mehr Speicherplatz zur Verfügung. Aus&lt;br /&gt;
&amp;lt;pre&amp;gt;# df -h /    &lt;br /&gt;
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf&lt;br /&gt;
/dev/root       1,8G    805M  869M   49% /&amp;lt;/pre&amp;gt;&lt;br /&gt;
wird bei einer 16GB SD-Karte zum Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;# df -h /    &lt;br /&gt;
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf&lt;br /&gt;
/dev/root        15G    905M   14G    7% /&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Dieser Abschnitt basiert auf einer [http://www.howtoforge.de/anleitung/wie-man-die-grose-von-ext3-partitionen-anpasst-ohne-daten-zu-verlieren/ Anleitung zum Verändern der Größe von ext3-Partitionen von HowtoForge].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Paketverwaltung ==&lt;br /&gt;
Arch Linux ARM setzt pacman als Paket-Manager ein. Das erste Kommando, das zur System-Aktualisierung, wurde schon in einem anderen Kapitel genannt:&lt;br /&gt;
* pacman -Syu&lt;br /&gt;
Dieses setzt sich aus zwei Teil-Kommandos zusammen:&lt;br /&gt;
* pacman -Sy&lt;br /&gt;
zum Downloaden der Paket-Datenbanken und&lt;br /&gt;
* pacman -Su&lt;br /&gt;
zum Downloaden und Installieren der neuen Pakete.&lt;br /&gt;
&lt;br /&gt;
Um ein neues Paket zu installieren verwendet man folgendes Kommando:&lt;br /&gt;
* pacman -S PAKETNAME&lt;br /&gt;
oder um davor noch die Datenbanken zu aktualisieren:&lt;br /&gt;
* pacman -Sy PAKETNAME&lt;br /&gt;
&lt;br /&gt;
Um ein Paket zu löschen verwendet man:&lt;br /&gt;
* pacman -R PAKETNAME&lt;br /&gt;
oder um alle danach nicht mehr benötigten Abhängigkeiten mit zu löschen:&lt;br /&gt;
* pacman -Rs PAKETNAME&lt;br /&gt;
&lt;br /&gt;
Es gibt noch viele weitere Kommandos, z.B. zum Suchen von Paketen, Anzeigen von Informationen, Auflisten von Dateien. Einen Überblick findet man im [https://wiki.archlinux.de/title/Pacman Arch Linux Wiki] oder eine Auflistung aller Optionen in der Manpage:&lt;br /&gt;
* man pacman&lt;br /&gt;
Nach der Installation sind standardmäßig alle fünf Paketquellen von Arch Linux ARM aktiviert, eine Liste aller enthaltenen Pakete findet man [http://archlinuxarm.org/packages auf der Website].&lt;br /&gt;
&lt;br /&gt;
== Datensicherung ==&lt;br /&gt;
Um neue Betriebssysteme auf dem Raspberry Pi auszuprobieren braucht man nicht zehn verschiedene SD-Karten, die man immer wechselt. Es reicht eine SD-Karte, von der man ein Backup macht, bevor man ein neues Betriebssystem aufspielt. Aber auch wenn man das nicht vorhat macht sich eine Datensicherung immer gut.&lt;br /&gt;
&lt;br /&gt;
Dafür gibt es unter Linux verschiedene Möglichkeiten, einmal natürlich das Tool dd, mit dem man den gesamten Inhalt der SD-Karte Byte für Byte auf ein anderes Medium, z.B. die Festplatte, kopieren kann. Hier werden aber auch eigentlich leere Teile der SD-Karte mit gesichert, wodurch bei einer 16GB SD-Karte auch (unkomprimiert) ein 16GB-Backup entsteht, egal wie viel Daten man nun eigentlich auf der Karte gespeichert hat. Abhilfe schafft partclone, das die einzelnen Partitionen sichert, und zwar auch nur die wirklich verwendeten Teile. Die Handhabung ist etwas komplexer als bei dd, eine gute Anleitung und ein ausführlicherer Vergleich der beiden Tools ist auf [http://www.bitblokes.de/2013/03/sd-karte-des-raspberry-pi-sichern-dd-oder-partclone/ bitblokes.de] zu finden.&lt;br /&gt;
&lt;br /&gt;
Unter Windows gibt es das Tool [http://sourceforge.net/projects/win32diskimager/ Win32DiskImager], mit dem man komplette bitweise Images (von SD-Karten) lesen und schreiben kann.&lt;br /&gt;
&lt;br /&gt;
== SoC-Konfiguration ==&lt;br /&gt;
Generelle Einstellungen für den SoC, die noch vor dem Starten des Betriebssystems geladen werden, kann man in der Datei &#039;&#039;&#039;/boot/config.txt&#039;&#039;&#039; ändern.&lt;br /&gt;
&lt;br /&gt;
Hier ist es z.B. möglich, Parameter für die Video-Ausgabe zu setzen, die Speicher-Aufteilung zwischen CPU und GPU zu ändern oder die CPU zu übertakten. Generell sollte man eher vorsichtig mit diesen Optionen umgehen, da sie bei falscher Verwendung auch mal dazu führen können, dass das Betriebssystem nicht mehr bootet oder das System instabil wird.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Auflistung der möglichen Konfigurations-Optionen findet man auf [http://elinux.org/RPiconfig eLinux.org].&lt;br /&gt;
&lt;br /&gt;
== Die GPIO-Schnittstelle ==&lt;br /&gt;
[[Datei:Raspberry_Pi_GPIO.jpg|thumb|Die GPIO-Schnittstelle (P1-Anschluss)]]&lt;br /&gt;
Für Bastler besonders interessant ist die GPIO-Schnittstelle, diese kann man über verschiedene Wege ansteuern.&lt;br /&gt;
=== Belegung der Schnittstelle ===&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Alle GPIO-Pins haben einen Pegel von 3,3V und sind &#039;&#039;&#039;nicht&#039;&#039;&#039; kompatibel zu 5V! Wenn man versucht, 5V anzuschließen, kann das zur Zerstörung des Raspberry Pi führen! Für Informationen zum Anschließen von 5V-Chips, siehe [[Pegelwandler]] oder [[Raspberry Pi#Erweiterungs-Boards]].&lt;br /&gt;
&amp;lt;gallery heights=&amp;quot;300&amp;quot; widths=&amp;quot;300&amp;quot;&amp;gt;&lt;br /&gt;
Datei:Raspberry_Pi_GPIO.svg|Der P1-Anschluss&lt;br /&gt;
Datei:Raspberry_Pi_GPIO_P2.svg|Der P2-Anschluss (eigentlich das JTAG-Interface der GPU, aber trotzdem mit ein paar nützlichen Pins)&lt;br /&gt;
Datei:Raspberry_Pi_GPIO_P5.svg|Der P5-Anschluss auf der Rückseite der Platine&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im oberen Bild ist die Belegung der Anschlüsse zu sehen, die man als GPIO oder zur Stromversorgung nutzen kann. Die Beschriftungen sind genau so wie auf der Platine angebracht und sollen eine Orientierung ermöglichen.&lt;br /&gt;
Die 3,3V Anschlüsse (P1-1, P1-17, P2-1 und P5-2) dürfen maximal mit 50 mA belastet werden. Bei den 5V Anschlüssen (P1-2, P1-4 und P5-1) gibt es keine feste Grenze, hier kommt es auf den aktuellen Stromverbrauch des restlichen Boards und der Leistung des Netzteils an. Die Pins GPIO2 (P1-3) und GPIO3 (P1-5) sind jeweils mit einem 1,8 kΩ Pull-Up-Widerstand ausgestattet.&lt;br /&gt;
&lt;br /&gt;
Der Anschluss P3 ist noch ein JTAG-Interface, der Anschluss P6 kann mit einem Reset-Knopf ausgestattet werden. Wenn man die beiden Pins verbindet verursacht das einen Soft Reset der CPU.&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht der Alternativ-Funktionen der Pins:&lt;br /&gt;
* P1-03 (GPIO2): I2C SDA (siehe [[Raspberry Pi#Der_I2C-Bus]])&lt;br /&gt;
* P1-05 (GPIO3): I2C SCL (siehe [[Raspberry Pi#Der_I2C-Bus]])&lt;br /&gt;
&lt;br /&gt;
* P1-07 (GPIO4): GPCLK0 (General Purpose Clock)&lt;br /&gt;
&lt;br /&gt;
* P1-08 (GPIO14): UART TXD (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
* P1-10 (GPIO15): UART RXD (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
* P1-11 (GPIO17): UART RTS (siehe [[Raspberry Pi#Der_UART]])&lt;br /&gt;
&lt;br /&gt;
* P1-12 (GPIO18): PWM ([[Pulsweitenmodulation]])&lt;br /&gt;
* P1-13 (GPIO27): PCM (Puls-Code-Modulation) oder GPCLK1 (General Purpose Clock)&lt;br /&gt;
&lt;br /&gt;
* P1-19 (GPIO10): SPI MOSI (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-21 (GPIO9): SPI MISO (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-23 (GPIO11): SPI SCLK (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-24 (GPIO8): SPI Chip Select 0 (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
* P1-26 (GPIO7): SPI Chip Select 1 (siehe [[Raspberry Pi#Die_SPI-Schnittstelle]])&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per sysfs ===&lt;br /&gt;
Ganz nach den UNIX-Prinzipien gibt es die Möglichkeit, auf die Schnittstelle über Dateien im sysfs zuzugreifen. Dafür existiert das Verzeichnis &#039;&#039;/sys/class/gpio/&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Um einen GPIO-Pin zu verwenden muss man ihn zuerst aktivieren, indem man die Pin-Nummer in &#039;&#039;/sys/class/gpio/export&#039;&#039; schreibt. Wenn man über die Kommandozeile z.B. Pin 4 aktivieren will benutzt man folgendes Kommando:&lt;br /&gt;
* echo 4 &amp;gt; /sys/class/gpio/export&lt;br /&gt;
Dadurch wird automatisch ein Verzeichnis &#039;&#039;/sys/class/gpio/gpio4/&#039;&#039; erstellt. Nun kann man über die darin enthaltene Datei &#039;&#039;direction&#039;&#039; den Pin als Out- bzw. Input festlegen (standardmäßig sind alle Inputs):&lt;br /&gt;
* echo in &amp;gt; /sys/class/gpio/gpio4/direction&lt;br /&gt;
für einen Input und&lt;br /&gt;
* echo out &amp;gt; /sys/class/gpio/gpio4/direction&lt;br /&gt;
für einen Output. Jetzt kann man den Wert des Pins auslesen:&lt;br /&gt;
* cat /sys/class/gpio/gpio4/value&lt;br /&gt;
oder ihn setzen, wenn es ein Output-Pin ist:&lt;br /&gt;
* echo 1 &amp;gt; /sys/class/gpio/gpio4/value&lt;br /&gt;
* echo 0 &amp;gt; /sys/class/gpio/gpio4/value&lt;br /&gt;
Wenn man den Pin nicht mehr benutzen will sollte man ihn wieder deaktivieren:&lt;br /&gt;
* echo 4 &amp;gt; /sys/class/gpio/unexport&lt;br /&gt;
Dadurch verschwindet das Verzeichnis &#039;&#039;/sys/class/gpio/gpio4/&#039;&#039; wieder. All das kann man natürlich nicht nur auf der Shell verwenden, sondern auch in jeder Programmiersprache, indem man einfach in die entsprechenden Dateien schreibt bzw. aus ihnen liest.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Shell-Blinklicht ===&lt;br /&gt;
Dieses kleine Shellscript aktiviert den GPIO-Pin, den man als Argument übergeben hat, setzt ihn als Output und schaltet ihn alle 500ms um:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
## Check arguments:&lt;br /&gt;
if [[ ! $# -eq 1 ]]; then&lt;br /&gt;
        echo &amp;quot;Usage: $0 GPIO&amp;quot; &amp;gt; /dev/stderr&lt;br /&gt;
        exit 1&lt;br /&gt;
fi&lt;br /&gt;
## Activate GPIO:&lt;br /&gt;
echo &amp;quot;Activating GPIO$1&amp;quot;&lt;br /&gt;
echo $1 &amp;gt; /sys/class/gpio/export || exit 1&lt;br /&gt;
## Create trap:&lt;br /&gt;
trap &amp;quot;echo &#039;Deactivating GPIO$1&#039; &amp;amp;&amp;amp; echo in &amp;gt; /sys/class/gpio/gpio$1/direction &amp;amp;&amp;amp; echo $1 &amp;gt; /sys/class/gpio/unexport&amp;quot; EXIT&lt;br /&gt;
## Set as output:&lt;br /&gt;
echo &amp;quot;out&amp;quot; &amp;gt; /sys/class/gpio/gpio$1/direction || exit 1&lt;br /&gt;
## Reset value:&lt;br /&gt;
echo 0 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
## Blink loop:&lt;br /&gt;
while true; do&lt;br /&gt;
        echo &amp;quot;Switching GPIO$1 on&amp;quot;&lt;br /&gt;
        echo 1 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
        sleep 0.5&lt;br /&gt;
        echo &amp;quot;Switching GPIO$1 off&amp;quot;&lt;br /&gt;
        echo 0 &amp;gt; /sys/class/gpio/gpio$1/value || exit 1&lt;br /&gt;
        sleep 0.5&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wenn man an dem entsprechenden Pin eine LED anschließt eignet sich das Script gut zum Testen der GPIO-Schnittstelle.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Python-Modul ===&lt;br /&gt;
Mit dem Modul [https://pypi.python.org/pypi/RPi.GPIO RPi.GPIO] kann man unter Python die GPIO-Pins steuern und auslesen. Auf der Projekt-Website gibt es auch ein Beispiel, mit dem man die Handhabung schnell lernt.&lt;br /&gt;
&lt;br /&gt;
Jedes Python-Script, das dieses Modul nutzen will, muss als root laufen.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit Java ===&lt;br /&gt;
Das [http://pi4j.com/ Pi4J-Projekt] ermöglicht unter Java die Verwendung der GPIO-Pins, dem UART, des I2C-Bus und der SPI-Schnittstelle. Eine ausführliche Anleitung gibt es auf der Website.&lt;br /&gt;
&lt;br /&gt;
Jedes Java-Programm, dass dieses Modul nutzen will, muss als root laufen.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung mit WiringPi ===&lt;br /&gt;
[https://projects.drogon.net/raspberry-pi/wiringpi/ WiringPi] bietet ein Kommandozeilen-Tool (&#039;&#039;&#039;gpio&#039;&#039;&#039;, Details siehe &#039;&#039;&#039;man gpio&#039;&#039;&#039;), das man auch als normaler Nutzer (ohne root-Rechte) verwenden kann und eine C-Bibliothek (Header-Datei &#039;&#039;wiringPi.h&#039;&#039; und Linker-Option &#039;&#039;-lwiringPi&#039;&#039;), mit der man die GPIO-Pins in C-Programmen verwenden kann.&lt;br /&gt;
&lt;br /&gt;
Auf der Website gibt es Beispiele und eine Installationsanleitung. Bei Arch Linux ARM ist es auch in den Paketquellen und kann per Paketverwaltung installiert werden:&lt;br /&gt;
* pacman -S wiringpi&lt;br /&gt;
&lt;br /&gt;
== Der I2C-Bus ==&lt;br /&gt;
Auch auf den I2C-Bus des Raspberry Pi kann man zugreifen. Die Pins dafür liegen an P1-03 (GPIO2, SDA) und P1-05 (GPIO3, SCL).&lt;br /&gt;
=== Ansteuerung per devfs ===&lt;br /&gt;
Den I2C-Bus kann man direkt über die Datei &#039;&#039;&#039;/dev/i2c-0&#039;&#039;&#039; erreichen.&lt;br /&gt;
=== Ansteuerung per i2c-tools ===&lt;br /&gt;
Komfortabler geht das aber über die i2c-tools. Dazu muss man erst einmal das Paket aus den Arch Linux ARM Paketquellen installieren:&lt;br /&gt;
* pacman -S i2c-tools&lt;br /&gt;
Nun muss man die nötigen Kernel-Module laden:&lt;br /&gt;
* modprobe i2c-bcm2708&lt;br /&gt;
* modprobe i2c-dev&lt;br /&gt;
Soll das beim Systemstart automatisch passieren, legt man eine *.conf-Datei im Verzeichnis &#039;&#039;&#039;/etc/modules-load.d/&#039;&#039;&#039; mit folgendem Inhalt an:&lt;br /&gt;
&amp;lt;pre&amp;gt;i2c-bcm2708&lt;br /&gt;
i2c-dev&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt sollten folgende Kommandozeilen-Tools funktionieren:&lt;br /&gt;
* &#039;&#039;&#039;i2cdetect&#039;&#039;&#039; zum Auflisten der I2C-Busse und zum Anzeigen der Eigenschaften und Geräte&lt;br /&gt;
* &#039;&#039;&#039;i2cdump&#039;&#039;&#039; zum Herausfinden von I2C-Registern&lt;br /&gt;
* &#039;&#039;&#039;i2cget&#039;&#039;&#039; zum Lesen von I2C-Registern&lt;br /&gt;
* &#039;&#039;&#039;i2cset&#039;&#039;&#039; zum Setzen von I2C-Registern&lt;br /&gt;
Die Manpages und Hilfe-Meldungen der einzelnen Tools geben Auskunft über die genaue Verwendung. Wichtig ist, dass man den I2C-Bus 0, &#039;&#039;&#039;nicht&#039;&#039;&#039; den I2C-Bus 1 verwendet. Ein kleines Beispiel gibt es auch in der Ausgabe 5 des MagPi im Artikel „Temperature Sensor“. Dort wird ein Temperatursensor per I2C ausgelesen.&lt;br /&gt;
&lt;br /&gt;
== Der UART ==&lt;br /&gt;
Die Pins für den UART liegen auch an P1. TXD an Pin 08 (GPIO14), RXD an Pin 10 (GPIO15) und RTS an Pin 11 (GPIO17).&lt;br /&gt;
&lt;br /&gt;
Zugreifen kann man über das devfs, genauer gesagt über die Datei &#039;&#039;&#039;/dev/ttyAMA0&#039;&#039;&#039;. Um zu testen, ob das funktioniert, kann man minicom benutzen:&lt;br /&gt;
* pacman -S minicom&lt;br /&gt;
* minicom -b 115200 -o -D /dev/ttyAMA0&lt;br /&gt;
&lt;br /&gt;
== Die SPI-Schnittstelle ==&lt;br /&gt;
Die Pins für die SPI-Schnittstelle liegen genau wie I2C und UART an P1. MOSI an Pin 19 (GPIO10), MISO an Pin 21 (GPIO9), SCLK an Pin 23 (GPIO11), CS0 an Pin 24 (GPIO8) und CS1 an Pin 26 (GPIO7).&lt;br /&gt;
&lt;br /&gt;
Der Zugriff erfolgt über die Dateien &#039;&#039;&#039;/dev/spidev0.0&#039;&#039;&#039; (Gerät an CS0) und &#039;&#039;&#039;/dev/spidev0.1&#039;&#039;&#039; (Gerät an CS1), in die man direkt schreiben und aus denen man auch direkt lesen kann.&lt;br /&gt;
&lt;br /&gt;
== Audio-Ausgabe ==&lt;br /&gt;
[[Datei:Raspberry_Pi_Audio.jpg|thumb|Der 3,5mm Klinke-Anschluss]]&lt;br /&gt;
Der Raspberry Pi verfügt über zwei Möglichkeiten der Audio-Ausgabe, einmal digital per HDMI und wenn kein Display angeschlossen ist wird automatisch auf die analoge Ausgabe per 3,5mm-Klinken-Anschluss umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Nach der Installation von Arch Linux ARM funktioniert jedoch vorerst keine der beiden Möglichkeiten, da das nötige Kernel-Modul nicht automatisch geladen wird.&lt;br /&gt;
&lt;br /&gt;
Manuell lässt es sich mit &#039;&#039;&#039;modprobe&#039;&#039;&#039; laden, beim nächsten Neustart ist das Problem dann jedoch wieder da:&lt;br /&gt;
* modprobe snd_bcm2835&lt;br /&gt;
Wenn man will, dass das Modul bei jedem Start automatisch geladen wird, muss man eine *.conf Datei im Verzeichnis /etc/modules-load.d/ erstellen und dort den Namen des Moduls hineinschreiben. Es bietet sich z.B. der Name sound.conf an, über die Kommandozeile ist die Datei schnell erstellt:&lt;br /&gt;
* echo &amp;quot;snd_bcm2835&amp;quot; &amp;gt; /etc/modules-load.d/sound.conf&lt;br /&gt;
Jetzt sollte die Audio-Ausgabe problemlos funktionieren. Testen kann man das beispielsweise per &#039;&#039;&#039;aplay&#039;&#039;&#039;, das im Paket &#039;&#039;&#039;alsa-utils&#039;&#039;&#039; enthalten ist:&lt;br /&gt;
* pacman -S alsa-utils&lt;br /&gt;
* aplay name_der_datei.wav&lt;br /&gt;
In diesem Paket ist auch &#039;&#039;&#039;alsamixer&#039;&#039;&#039; enthalten, ein Programm mit dem man über eine ncurses-Oberfläche die Ausgabe-Lautstärke regeln kann.&lt;br /&gt;
&lt;br /&gt;
Man muss beachten, dass nur &#039;&#039;&#039;root&#039;&#039;&#039; und Nutzer in der Gruppe &#039;&#039;&#039;audio&#039;&#039;&#039; auf die Soundkarte zugreifen können. Wenn man nicht als &#039;&#039;&#039;root&#039;&#039;&#039; arbeiten will sollte man seinen Benutzer der Gruppe hinzufügen:&lt;br /&gt;
* gpasswd -a USERNAME audio&lt;br /&gt;
&lt;br /&gt;
=== Standard-Ausgabegerät ===&lt;br /&gt;
Normalerweise wird der Sound automatisch über HDMI ausgegeben, wenn das angeschlossene Gerät das unterstützt, andernfalls über den Klinke-Anschluss. Will man das ändern, z.B. weil man einen Monitor per HDMI angeschlossen hat, den Sound aber trotzdem über die Anlage hören will, geht das ganz schnell über die Kommandozeile:&lt;br /&gt;
* amixer -c 0 cset numid=3 &amp;lt;nummer&amp;gt;&lt;br /&gt;
Dabei muss man &#039;&#039;&amp;lt;nummer&amp;gt;&#039;&#039; durch eine 0 für automatisches Wechseln, eine 1 für den Klinke-Anschluss oder eine 2 für den HDMI-Anschluss ersetzen. Soll das bei jedem Systemstart automatisch ausgeführt werden, erstellt man bei Arch Linux eine Service-Datei für systemd, z.B. so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[Unit]&lt;br /&gt;
Description=Set Default Audio Output&lt;br /&gt;
&lt;br /&gt;
[Service]&lt;br /&gt;
Type=oneshot&lt;br /&gt;
ExecStart=/usr/bin/amixer -c 0 cset numid=3 &amp;lt;nummer&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[Install]&lt;br /&gt;
WantedBy=multi-user.target&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier ersetzt man wieder &#039;&#039;&amp;lt;nummer&amp;gt;&#039;&#039; durch das gewünschte Ziel, speichert die Datei unter &#039;&#039;/etc/systemd/system/default-audio.service&#039;&#039; ab und aktiviert den soeben erstellten Service noch:&lt;br /&gt;
* systemctl enable default-audio.service&lt;br /&gt;
Ab dem nächsten Systemstart wird dadurch das Ausgabegerät automatisch eingestellt.&lt;br /&gt;
&lt;br /&gt;
== Erweiterungs-Boards ==&lt;br /&gt;
Für die GPIO-Schnittstelle des Raspberry Pi gibt es verschiedene Erweiterungs-Boards, die zusätzliche Schnittstellen bereitstellen und 3,3V zu 5V Pegelwandler besitzen. Durch diese kann man den Raspberry Pi in bestehende, 5V-basierte Schaltungen und Projekte einbinden.&lt;br /&gt;
&lt;br /&gt;
=== Prototyping-Board für den Raspberry Pi ===&lt;br /&gt;
In der Ausgabe 16 des embedded projects journal wurde auf Seite 13 ein Prototyping-Board für den Raspberry Pi vorgestellt.&lt;br /&gt;
&lt;br /&gt;
Das Board besitzt eine Spannungsversorgung, an die man 7 bis 15V anschließen kann und die auch den Raspberry Pi mitversorgt. Dadurch wird ein per microUSB angeschlossenes Netzteil überflüssig. Außerdem gibt es einen Pegelwandler, der acht GPIO-Pins des Raspberry Pi als 5V-GPIO-Pins zur Verfügung stellt und einen I2C-Pegelwandler, durch den auch hier der Anschluss von 5V-Chips möglich wird.&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen gibt es im embedded projects journal und auf der [http://bastelstube.rocci.net/projects/MBS19_RasPi-GPIO/RasPi-GPIO.html Website des Projekts].&lt;br /&gt;
&lt;br /&gt;
=== Berrybush Pi ===&lt;br /&gt;
Ebenfalls in der Ausgabe 16 des embedded projects journal wurde auf Seite 28 der „Berrybush Pi“ vorgestellt, der ein paar mehr Schnittstellen als das Prototyping-Board bietet.&lt;br /&gt;
&lt;br /&gt;
Auch dieses Board bietet eine Spannungsversorgung für den Raspberry Pi und acht 5V-GPIO-Pins, diese sind jedoch mit Darlington-Transistoren und LEDs zur Status-Anzeige ausgestattet. Außerdem gibt es eine 3,3V (sic) SPI-Schnittstelle, die einfach zum Raspberry Pi durchgeschleift wird, einen 5V-I2C-Bus (mit Pegelwandler zum Raspberry Pi), einen RS485-Bus und eine RS232-Schnittstelle, die jedoch in den RS485-Bus gespeist wird.&lt;br /&gt;
&lt;br /&gt;
Als kleines Highlight gibt es noch die Möglichkeit, ein LCD anzuschließen. Dafür sind Portexpander und Potentiometer für Helligkeit und Kontrast vorhanden.&lt;br /&gt;
&lt;br /&gt;
Genauere Informationen, Schaltpläne und Bilder gibt es im embedded projects journal.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/topic/epj-16-seite-28 Weitere Informationen]&lt;br /&gt;
&lt;br /&gt;
=== Arduino Shield Bridge ===&lt;br /&gt;
Mit diesem Board aus Spanien kann man Arduino-Aufsteckplatinen mit dem Raspberry Pi verwenden.&lt;br /&gt;
[http://www.cooking-hacks.com/index.php/raspberry-pi-to-arduino-shield-connection-bridge.html Cooking-hacks.com]&lt;br /&gt;
&lt;br /&gt;
Dazu existiert eine Library &amp;quot;arduPi&amp;quot;:&lt;br /&gt;
&amp;quot;In order to make complete the compatibility we have created the arduPi library which allows to use Raspberry with the same code used in Arduino. To do so, we have implemented conversion functions so that you can control in the same way as in Arduino all the I/O interfaces: i2C, SPI, UART, analog, digital, in Raspberry Pi.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== I/O Module seriell/USB ===&lt;br /&gt;
&lt;br /&gt;
Alternativ bieten sich Schnittstellen-Erweiterungen über seriellen Port oder USB an, zum Beispiel mit einem handelsüblichen AVR Mikrocontroller-Modul und der Firmware von Stefan Frings&lt;br /&gt;
[http://stefanfrings.de/avr_io/].&lt;br /&gt;
&lt;br /&gt;
=== Gertboard ===&lt;br /&gt;
Webseite: http://www.raspberrypi.org/archives/tag/gertboard&lt;br /&gt;
Eine I/O-Aufsteckplatine mit zusätzlichem Mikrocontroller (bisher PIC, jetzt ATMega)&lt;br /&gt;
&lt;br /&gt;
== Projekte ==&lt;br /&gt;
Um den Raspberry Pi hat sich inzwischen eine große Community gebildet, von der immer wieder neue Projekte mit und Erweiterungen für den Raspberry Pi entwickelt werden. Eine Quelle, über die man von den Projekten erfährt ist z.B. der offizielle [http://www.raspberrypi.org/ Raspberry Pi Blog], aber es gibt auch noch viele andere Möglichkeiten, sich zu informieren.&lt;br /&gt;
&lt;br /&gt;
Einige dieser Möglichkeiten und einige ausgewählte Projekte werden in diesem Artikel vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== Pideas ===&lt;br /&gt;
[http://pideas.co.uk/ Pideas] ist eine Seite, auf der Projekte für den Raspberry Pi gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
=== The MagPi ===&lt;br /&gt;
[http://www.themagpi.com/ The MagPi] ist ein freies Online-Magazin, das (normalerweise) monatlich erscheint und in dem interessante Projekte genauer vorgestellt werden. Außerdem gibt es Programmier-Tutorials, Anleitungen für Linux-Software und Interviews.&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 1 ====&lt;br /&gt;
Die Ausgabe 1 vom Mai 2012 behandelt folgende Themen:&lt;br /&gt;
* „The Pioneers“ - eine kleine Übersicht, wie es zur Entwicklung des Raspberry Pi kam&lt;br /&gt;
* „Skutter“ - ein Roboterarm für den Raspberry Pi&lt;br /&gt;
* „RacyPy“ - eine Linux Live-CD, basierend auf Puppy Linux und speziell angepasst für einen leichten Einstieg in die Programmierung&lt;br /&gt;
* eine Anleitung zum Installieren von Debian in einer virtuellen Maschine&lt;br /&gt;
* „Scratch“ - eine Programmiersprache und Entwicklungsumgebung zum kinderleichten Einstieg in die Programmierung&lt;br /&gt;
* „the python pit“ - ein Einstieg in die Programmierung mit Python&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 2 ====&lt;br /&gt;
Die Ausgabe 2 vom Juni 2012 behandelt folgende Themen:&lt;br /&gt;
* „PiSetup“ - eine Anleitung zum Einrichten des Raspberry Pi&lt;br /&gt;
* „In Control“ - ein Einstieg in die GPIO-Programmierung mit dem Raspberry Pi und Python&lt;br /&gt;
* „Pi Dissection“ - ein genauerer Blick auf die Einzelteile des Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Skutter“ aus Ausgabe 1&lt;br /&gt;
* „Command Line Clinic“ - eine Einführung in die Benutzung der Linux-Shell&lt;br /&gt;
* „Computer Music“ - eine Anleitung zur Installation von „Schism Tracker“, einem Sequencer-Programm&lt;br /&gt;
* Fortsetzung der Debian-Anleitung aus Ausgabe 1, diesmal mit einer Anleitung zum Einrichten und Verwenden von Python&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 3 ====&lt;br /&gt;
Die Ausgabe 3 vom Juli 2012 behandelt folgende Themen:&lt;br /&gt;
* eine Anleitung zum automatischen Starten der grafischen Oberfläche von Debian&lt;br /&gt;
* Fortsetzung von „In Control“ aus Ausgabe 2&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2&lt;br /&gt;
* Fortsetzung von „Skutter“ aus Ausgabe 1 und 2&lt;br /&gt;
* „To Protect and Serve your Raspberry Pi“ - eine einfache Anleitung, um die GPIO-Pins des Raspberry Pi vor Kurzschlüssen zu schützen&lt;br /&gt;
* „Meeting Pi“ - Vorschäge, wie man Kinder an den Raspberry Pi heranführen kann&lt;br /&gt;
* „The C Cave“ - ein C-Tutorial&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1 und 2&lt;br /&gt;
* „Programming Fundamentals“ - ein Artikel, der die Grundlagen (fast) aller Programmiersprachen auf einfachem Weg zeigen will&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 4 ====&lt;br /&gt;
Die Ausgabe 4 vom August 2012 behandelt folgende Themen:&lt;br /&gt;
* Fortsetzung von „In Control“ aus Ausgabe 2 und 3&lt;br /&gt;
* „3-Axis Accelerometer“ - eine Anleitung für einen Beschleunigungs-Sensor am Raspberry Pi&lt;br /&gt;
* „Kernow Pi Launch“ - ein Artikel darüber, wie der Raspberry Pi an Schulen gebracht wurde&lt;br /&gt;
* „MagPi Exclusive Interview“ - ein Interview mit Eben und Liz von der Raspberry Pi Foundation&lt;br /&gt;
* „How to customise your LXDE menu“ - eine Anleitung zum Anpassen von LXDE&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2 und 3&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1 und 3&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2 und 3&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 5 ====&lt;br /&gt;
Die Ausgabe 5 vom September 2012 behandelt folgende Themen:&lt;br /&gt;
* „Steady Hands“ - eine Anleitung zum Bau eines „Heißer Draht“-Spiels mit dem Raspberry Pi&lt;br /&gt;
* „Temperature Sensor“ - eine Anleitung zum Anschluss eines I2C-Temperatursensors an den Raspberry Pi&lt;br /&gt;
* „XBMC Media Center“ - eine Einführung in Rasbbmc, einer HTPC-Distribution für den Raspberry Pi&lt;br /&gt;
* „Squeezy or Wheezy?“ - ein Vergleich von Debian 6 „Squeeze“ und Debian 7 „Wheezy“&lt;br /&gt;
* Fortsetzung von „Command Line Clinic“ aus Ausgabe 2, 3 und 4&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3 und 4&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3 und 4&lt;br /&gt;
* „RasPiThon 2012“ - ein Bericht über den Rasbperry Pi Programmier-Marathon&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3 und 4&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 6 ====&lt;br /&gt;
Die Ausgabe 6 vom Oktober 2012 behandelt folgende Themen:&lt;br /&gt;
* „Skutter Returns“ - Fortsetzung von „Skutter“ aus Ausgabe 1, 2 und 3&lt;br /&gt;
* „Portable Power for your Pi“ - eine Anleitung für eine mobile Stromversorgung&lt;br /&gt;
* „Star Letter: An FET Buffer Stage for GPIO Access“ - eine Möglichkeit, wie man die GPIO-Pins des Raspberry Pi belastbarer machen kann&lt;br /&gt;
* „Pumpkin Pi“ - ein Kürbis mit Bewegungs-Sensor und RGB-LEDs&lt;br /&gt;
* „Camera Pi“ - ein Interview mit David Hunt, einem Bastler, der seine Kamera mit einem Raspberry Pi ausgestattet hat&lt;br /&gt;
* „Our Raspberry Pi Summer“ - ein Beispiel für ein kleines Spiel, das mit Scratch programmiert wurde&lt;br /&gt;
* „Baby steps in a Big World“ - ein Ada-Tutorial&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3, 4 und 5&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4 und 5&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4 und 5&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 7 ====&lt;br /&gt;
Die Ausgabe 7 vom November 2012 behandelt folgende Themen:&lt;br /&gt;
* „Raspberry Pi &amp;amp; Arduino“ - ein Artikel über die Kommunikation zwischen Arduino und Raspberry Pi mithilfe von Python&lt;br /&gt;
* „A little Ray of Sunshine“ - eine Solar-Stromversorgung für den Raspberry Pi und andere USB-Geräte&lt;br /&gt;
* „The Raspberry Ladder Board“ - ein Board zum Einstieg in die GPIO-Programmierung mit dem Raspberry Pi&lt;br /&gt;
* „Interrupts and Other Activities with GPIO Pins” - eine Anleitung für Interrupts und GPIO-Pins im Allgemeinen auf dem Raspberry Pi&lt;br /&gt;
* „Raspbian“ - ein Interview mit Mike Thompson, dem Erfinder von Raspbian&lt;br /&gt;
* „Turbopi“ - eine Anleitung zum Übertakten des Raspberry Pi&lt;br /&gt;
* „Pi-Evolution“ - eine Übersicht über die Entwicklung des Raspberry Pi&lt;br /&gt;
* „Make“ - eine Einführung in GNU Make und Makefiles&lt;br /&gt;
* „Welcome to the C++ Cache!“ - eine Einführung in C++&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5 und 6&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5 und 6&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 8 ====&lt;br /&gt;
Die Ausgabe 8 vom Dezember 2012 behandelt folgende Themen:&lt;br /&gt;
* „Skutter Returns“ - Fortsetzung von „Skutter“ aus Ausgabe 1, 2 und 3 bzw. „Skutter Returns“ aus Ausgabe 6&lt;br /&gt;
* „The Santa Trap“ - Heimautomation mit dem Raspberry Pi&lt;br /&gt;
* „Control your Arduino board with Raspberry Pi and Python“ - ein Python-Modul zum Steuern eines Arduino&lt;br /&gt;
* „PiGauge“ - eine Anleitung, wie man Servo-Motoren übers Internet mithilfe des Raspberry Pi steuern kann&lt;br /&gt;
* „Quick2Wire“ - Erweiterungs-Board für den Raspberry Pi&lt;br /&gt;
* „Kickstarter“ - eine Vorstellung der Kickstarter-Kampagne, um The MagPi auf Papier zu bannen&lt;br /&gt;
* „Programming the Raspberry Pi: Getting Started with Python“ - eine kurze Vorstellung des gleichnamigen Buches von Simon Monk&lt;br /&gt;
* „pibow interview“ - ein Interview mit den Erfindern des PiBow, einem Gehäuse für den Raspberry Pi&lt;br /&gt;
* „CESIL Pi“ - ein CESIL Programmier-Tutorial für den Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Welcome to the C++ Cache!“ aus Ausgabe 7&lt;br /&gt;
* Fortsetzung von „Baby steps in a Big World“ aus Ausgabe 6&lt;br /&gt;
* „MySQL Database Bootcamp“ - eine Einführung in MySQL&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6 und 7&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 9 ====&lt;br /&gt;
Die Ausgabe 9 vom Februar 2013 (Januar wurde ausgelassen) behandelt folgende Themen:&lt;br /&gt;
* „adafruit Interview“ - Interview mit der Gründerin von adafruit, einem Shop für DIY Elektronik&lt;br /&gt;
* „WebIOPi“ - eine REST-Schnittstelle, um die GPIO-Pins des Raspberry Pi per Javascript zu steuern&lt;br /&gt;
* „Backup your Raspberry Pi“ - eine Anleitung für die Datensicherung der SD-Karte&lt;br /&gt;
* „Pi Interface Board Review Quick2Wire“ - ein genauerer Blick auf das Erweiterungs-Board „Quick2Wire“, das in Ausgabe 8 schon kurz gezeigt wurde&lt;br /&gt;
* „RISC OS Pi“ - eine Anleitung zur Installation von RISC OS auf dem Raspberry Pi&lt;br /&gt;
* „Installing &amp;amp; Configuring ArchLinux“ - eine Anleitung zur Installation von Arch Linux auf dem Raspberry Pi, die auch die Installation von XFCE umfasst&lt;br /&gt;
* „Introducing Vala“ - ein kleines Vala Programmier-Tutorial für den Raspberry Pi und das LedBorg Erweiterungs-Board&lt;br /&gt;
* Fortsetzung von „The C Cave“ aus Ausgabe 3, 4, 5 und 6&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6 und 7&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6, 7 und 8&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 10 ====&lt;br /&gt;
Die Ausgabe 10 vom März 2013 behandelt folgende Themen:&lt;br /&gt;
* Fortsetzung von „WebIOPi“ aus Ausgabe 9&lt;br /&gt;
* „Try a Cocktail of Projects“ - ein Artikel, der verschiedene Erweiterungs-Möglichkeiten für den Raspberry Pi zeigt&lt;br /&gt;
* „Backing up - part 2“ - Fortsetzung von „Backup your Raspberry Pi“ aus Ausgabe 9&lt;br /&gt;
* „Bash Gaffer Tape“ - ein Bash-Tutorial&lt;br /&gt;
* „Charm on the Raspberry Pi“ - ein Charm Programmier-Tutorial für den Raspberry Pi&lt;br /&gt;
* „C++ Cache”: Fortsetzung von „Welcome to the C++ Cache!“ aus Ausgabe 7 und 8&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6, 7 und 9&lt;br /&gt;
* Fortsetzung von „the python pit“ aus Ausgabe 1, 2, 3, 4, 5, 6, 7, 8 und 9&lt;br /&gt;
&lt;br /&gt;
==== Ausgabe 11 ====&lt;br /&gt;
Die Ausgabe 11 vom April 2013 behandelt folgende Themen:&lt;br /&gt;
* Heizungssteuerung mithilfe des Raspberry Pi über ein Smartphone&lt;br /&gt;
* eine Vorstellung vom „Power and I/O Expansion Board“&lt;br /&gt;
* eine Anleitung zum Einrichten eines WLAN-Access-Points auf dem Raspberry Pi (per USB-WLAN-Dongle)&lt;br /&gt;
* Vorstellung von der Minecraft Pi-Edition&lt;br /&gt;
* „Printing with Cups“: Configuring Cups&lt;br /&gt;
* „Setting up a simple intranet“ mit einer Anleitung zum Einrichten vom Apache HTTPD&lt;br /&gt;
* eine Einführung in die Assembler-Programmierung auf dem Raspberry Pi&lt;br /&gt;
* Fortsetzung von „Charm on the Raspberry Pi“ aus Ausgabe 10&lt;br /&gt;
* kurze Anleitung zum Verwenden von Farben auf der Kommandozeile&lt;br /&gt;
* Fortsetzung von „Scratch“ aus Ausgabe 1, 3, 4, 5, 6, 7, 9 und 10&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Pi multi-effects ===&lt;br /&gt;
Ein Musiker hat mit einem Raspberry Pi, einem Arduino Uno und einer USB-Soundkarte ein Gitarren-Effektboard gebaut und das Ganze in seinem Blog [https://guitarextended.wordpress.com/2013/01/31/raspberry-pi-multi-effects-overview-of-the-setup/ beschrieben]. Das alles sieht im [https://www.youtube.com/watch?v=NwJNeouLqgQ Video] noch etwas sperrig aus, es sollte aber kein Problem sein, die Soundkarte und den Raspberry Pi auch noch mit ins Effektboard zu integrieren.&lt;br /&gt;
&lt;br /&gt;
Als Software verwendet er Raspbian, also das Standard-Betriebssystem des Raspberry Pi, und [http://puredata.info/ Pure Data], eine Echtzeit-Umgebung für Audio-, Video- und Grafik-Verarbeitung. Diese ist jedoch auch in den Arch Linux ARM Paketquellen verfügbar, einem Versuch, das ganze auch mit Arch Linux auszuprobieren, steht also theoretisch nichts im Wege.&lt;br /&gt;
&lt;br /&gt;
Die Installation und Konfiguration von Pure Data hat er ebenfalls in seinem Blog [https://guitarextended.wordpress.com/2013/01/28/rpi-as-guitar-effects-processor-installing-and-configuring-pd/ beschrieben].&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Pi guitar tuner ===&lt;br /&gt;
Ein anderer Nutzer hat ein Gitarren-Stimmgerät gebaut. Dazu hat er einen Raspberry Pi, eine USB-Webcam zur Ton-Aufnahme, eine 7-Segment-Anzeige zur Anzeige des Tons und 15 LEDs zur Anzeige der Abweichung vom Ton verwendet.&lt;br /&gt;
&lt;br /&gt;
Ausgewertet und angesteuert wird das Ganze über ein selbstgeschriebenes Java-Programm, ein Video und ein paar zusätzliche Informationen gibt es auf [https://www.youtube.com/watch?v=oB4F5VhzQf8 YouTube].&lt;br /&gt;
&lt;br /&gt;
=== Circadian Lighting ===&lt;br /&gt;
Auch für Menschen, die sich mit dem Morgen manchmal schwer tun, gibt es ein Projekt mit dem Raspberry Pi, das im Blog [http://www.raspberrypi.org/archives/3267 vorgestellt wurde]. In Verbindung mit ein paar RGB-LEDs kann man den Raspbrry Pi zu einem Wecker machen, der einen nicht mit lautem, nervigen Piepsen aus dem Schlaf reißt, sondern langsam in Verbindung mit einem künstlichen Sonnenaufgang weckt. Fehlt nur noch ein Relais, über das die Kaffeemaschine angeschaltet wird, und der Morgen ist perfekt.&lt;br /&gt;
&lt;br /&gt;
Bei diesem Projekt gibt es sogar den [https://github.com/rasathus/circadianLighting Quellcode], eine [http://rasathus.blogspot.co.uk/2013/01/circadian-lighting-part-one.html Hardware-Liste] und ein [https://www.youtube.com/watch?v=Q9_tqoOkmCU Video], das einige der Funktionen zeigt. Und mit ein paar Python-Kenntnissen kann man alles seinen Bedürfnissen anpassen und erweitern.&lt;br /&gt;
&lt;br /&gt;
=== Raspberry Leaf ===&lt;br /&gt;
Die einfachsten Ideen sind doch manchmal die Besten. „Dr. Monk” hat in seinem Blog eine Pinbelegung des Raspberry Pi [http://www.doctormonk.com/2013/02/raspberry-pi-and-breadboard-raspberry.html veröffentlicht], die (in der richtigen Größe ausgedruckt) genau auf den P1-GPIO-Anschluss des Raspberry Pi passt. So vergisst man nie die Pinbelegung, wenn man gerade mal wieder mit dem Raspberry Pi bastelt und auch ein falsches Anschließen, das zur Zerstörung des Mini-Computers oder der angeschlossenen Teile führen kann, passiert nicht mehr so leicht.&lt;br /&gt;
&lt;br /&gt;
=== raspberry-tools ===&lt;br /&gt;
Eine Tool-Sammlung für den Raspberry Pi, veröffentlicht unter der GNU General Public License Version 2 und gehostet auf [https://github.com/rookies/raspberry-tools github]. Momentan enthält diese Sammlung nur ein Programm, es folgen aber noch weitere nützliche Tools.&lt;br /&gt;
&lt;br /&gt;
==== scope ====&lt;br /&gt;
Scope ist Teil von raspberry-tools. Es zeichnet digitale Signale von einem oder mehreren GPIO-Pins auf und bereitet die Daten für eine Auswertung mit [http://xoscope.sourceforge.net/ xoscope] auf.&lt;br /&gt;
&lt;br /&gt;
Wenn man sich den Quellcode geholt hat, muss man erst einmal das Hauptprogramm für den Raspberry Pi kompilieren. Das geht entweder direkt auf dem Raspberry Pi oder per Cross-Compiler.&lt;br /&gt;
* g++ -o RasPi_logGPIO RasPi_logGPIO.cpp&lt;br /&gt;
Nachdem sowohl die entstandene Datei &#039;&#039;RasPi_logGPIO&#039;&#039; als auch das Script &#039;&#039;RasPi_logGPIO.sh&#039;&#039; im gleichen Ordner auf dem Raspberry Pi sind, kann man die Aufzeichnung starten:&lt;br /&gt;
* ./RasPi_logGPIO.sh [Liste mit GPIO-Pins] &amp;gt; ausgabedatei.txt&lt;br /&gt;
Für die Pins 14, 31 und 23 sieht das Kommando z.B. so aus:&lt;br /&gt;
* ./RasPi_logGPIO.sh 14 31 23 &amp;gt; ausgabedatei.txt&lt;br /&gt;
Die Aufzeichnung kann mit Strg+C gestoppt werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man die entstandene Datei (z.B. hier ausgabedatei.txt) normalisieren und ins xoscope-Format umwandeln, das funktioniert auch problemlos auf einem anderen Computer:&lt;br /&gt;
* ./RasPi_normalizeGPIO.py ausgabedatei.txt &amp;gt; ausgabedatei.dat&lt;br /&gt;
Nun entsteht die Datei &#039;&#039;ausgabedatei.dat&#039;&#039;, die man mit xoscope ansehen kann:&lt;br /&gt;
&amp;lt;gallery heights=&amp;quot;300&amp;quot; widths=&amp;quot;300&amp;quot;&amp;gt;&lt;br /&gt;
Datei:RasPiTools_scope_sample1.png|eine Beispiel-Aufnahme&lt;br /&gt;
Datei:RasPiTools_scope_sample2.png|die gleiche Aufnahme, vergrößert&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
Dazu muss man nur die Datei laden und die entsprechenden Memory-Einträge anzeigen lassen. In diesem Beispiel ist Pin 14 in Mem A, Pin 31 in Mem B und Pin 23 in Mem C, also genau in der Reihenfolge, in der man die Pin-Nummern dem Programm übergeben hat.&lt;br /&gt;
&lt;br /&gt;
== Anhang ==&lt;br /&gt;
=== Stromversorgung ===&lt;br /&gt;
[[Datei:Raspberry_Pi_Power.jpg|thumb|Der microUSB-Anschluss zur Stromversorgung]]&lt;br /&gt;
Um den Raspberry Pi mit Strom zu versorgen gibt es verschiedene Möglichkeiten. Entscheidend ist nur, dass möglichst konstante 5V bei bis zu 700 mA auf einem microUSB-Stecker geliefert werden.&lt;br /&gt;
==== mit USB-Netzteil ====&lt;br /&gt;
Wenn man ein genügend starkes USB-Netzteil mit einem microUSB-Kabel verbindet hat man eine passende Stromversorgung für den Raspberry Pi.&lt;br /&gt;
Gut funktioniert zum Beispiel das iPhone-Ladegerät, aber auch andere, günstigere Netzteile sollten keine Probleme machen.&lt;br /&gt;
==== Netzteil selbst bauen ====&lt;br /&gt;
Wenn man kein USB-Netzteil zur Hand hat kann man auch selbst ein Steckernetzteil, das 5V Gleichspannung liefert, mit einem microUSB-Kabel zusammenlöten.&lt;br /&gt;
&lt;br /&gt;
Dafür braucht man erst einmal ein microUSB-Kabel und ein passendes Steckernetzteil. (siehe Abb. 1)&lt;br /&gt;
Man entfernt den USB-A-Stecker und befreit das Kabelende ein Stück von seinem Mantel, es kommen vier Adern zum Vorschein. (siehe Abb. 2)&lt;br /&gt;
Uns interessiert die grüne und die weiße Ader nicht, da wir keine Daten übertragen wollen.&lt;br /&gt;
Jetzt muss auch noch der Stecker vom Netzteil entfernt und die Adern abisoliert werden. (siehe Abb. 3)&lt;br /&gt;
Bei manchen Netzteilen ist eine Markierung für Plus- oder Minus-Pol angebracht. Wenn das nicht der Fall ist, muss man mit einem Multimeter nachmessen. (siehe Abb. 4)&lt;br /&gt;
Nun isoliert man auch noch die schwarze und rote Ader des USB-Kabels ab und verlötet die schwarze Ader mit dem Minus-Pol des Netzteils und die rote Ader mit dem Plus-Pol des Netzteils. Danach packt man um die Adern jeweils einen Schrumpfschlauch zur Isolation. Wenn man um die ganze Verbindung noch einen großen Schrumpfschlauch packt wird die Verbindung nochmal um einiges stabiler. &lt;br /&gt;
(siehe Abb. 5)&lt;br /&gt;
Es ist auch möglich den Raspberry Pi über jedes alte PC Netzteil zu betreiben, bei einem alten 300 Watt Netzteil hat man einen Verlust im Leerlauf von ca. 8 Watt.&lt;br /&gt;
Dafür kann man aber einige andere Komponenten (LEDs, usw.) mitbetreiben.&lt;br /&gt;
Hier ein Link zu einem deutschen Youtube Video:&lt;br /&gt;
http://www.youtube.com/watch?v=hqevlVn1Osk&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau1.jpg|Abb. 1: Die Ausgangsmaterialien&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau2.jpg|Abb. 2: Das USB-Kabel ohne Mantel mit der roten (+5V), schwarzen (GND), grünen (Data+) und weißen (Data-) Ader&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau3.jpg|Abb. 3: Das Netzteil mit abisolierten Kabelenden&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau4.jpg|Abb. 4: Das Multimeter zeigt eine positive Zahl an, daher ist das Kabel mit der roten Klemme der Plus-Pol, das Kabel mit der schwarzen Klemme der Minus-Pol.&lt;br /&gt;
Datei:RasPi_Netzteil_Selbstbau5.jpg|Abb. 5: Das fertige Netzteil, die Verbindung ist mit einem Schrumpfschlauch geschützt.&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== USB-Geräte ===&lt;br /&gt;
[[Datei:Raspberry_Pi_USB.jpg|thumb|Die beiden USB Host-Anschlüsse]]&lt;br /&gt;
Generell kann man am Raspberry Pi alle USB-Geräte verwenden, die vom Linux-Kernel unterstützt werden und somit auch an anderen Linux-PCs funktionieren.&lt;br /&gt;
&lt;br /&gt;
Es ist allerdings zu beachten, dass die USB-Ports nur vergleichsweise wenig Strom liefern können (nicht die vollen 500 mA, die laut USB2.0 Standard maximal möglich sind), für größere Verbraucher wie z.B. 2,5-Zoll-Festplatten ohne externe Stromversorgung muss man daher einen externen USB-Hub mit Stromversorgung verwenden.&lt;br /&gt;
==== Maus &amp;amp; Tastatur ====&lt;br /&gt;
Da es für Mäuse und Tastaturen Standard-Profile gibt, die auch von allen (bis auf extrem wenige Ausnahmefälle) Geräten eingehalten werden, sollte es hier keine Probleme geben. Die Eingabegeräte sollten sofort verwendbar sein und auch Hot-Plugging stellt im Normalfall kein Problem dar.&lt;br /&gt;
==== USB-Sticks &amp;amp; USB-Festplatten ====&lt;br /&gt;
Wenn man seinem Raspberry Pi zusätzlichen Speicherplatz verpassen will bietet es sich an, USB-Sticks bzw. -Festplatten anzuschließen. Da es auch hier Standard-Profile gibt sollten diese sofort vom Linux-Kernel erkannt werden. Einhängen ([https://de.wikipedia.org/wiki/Mounten mounten]) muss man sie jedoch unter Arch Linux ARM generell selbst, wenn man nicht einen [https://de.wikipedia.org/wiki/Fstab fstab-Eintrag] erstellt hat oder eine Desktop-Oberfläche installiert hat, die das automatisch erledigt.&lt;br /&gt;
==== WLAN-Sticks ====&lt;br /&gt;
Wird der WLAN-Stick vom Kernel erkannt (erkennbar daran, dass ein entsprechendes Interface von &#039;&#039;&#039;ifconfig -a&#039;&#039;&#039; aufgelistet wird), kann man ein Netzwerk-Profil mit [https://wiki.archlinux.org/index.php/Netcfg netcfg] (Quasi-Standard unter ArchLinux, bei dem die Profile über Konfigurationsdateien erstellt werden), dem [https://wiki.archlinux.org/index.php/NetworkManager NetworkManager] oder [https://wiki.archlinux.org/index.php/Wicd Wicd] erstellen. Sollte die Karte nicht erkannt werden, hilft eventuell ein Blick auf die [https://wiki.archlinux.org/index.php/Wireless_Setup Wireless Setup] Seite im ArchLinux Wiki.&lt;br /&gt;
==== TV-Karten ====&lt;br /&gt;
Will man den Raspberry Pi als HTPC verwenden, kann es sinnvoll sein, eine TV-Karte per USB anzuschließen.&lt;br /&gt;
Hier gibt es generell 4 Möglichkeiten:&lt;br /&gt;
* &#039;&#039;&#039;DVB-T&#039;&#039;&#039;, also digitales Fernsehen, das per Funk ausgestrahlt wird - kostenlos, jedoch nicht überall verfügbar&lt;br /&gt;
* &#039;&#039;&#039;DVB-S&#039;&#039;&#039;, also digitales Fernsehen, das per Satellit ausgestrahlt wird - kostenlos und generell überall verfügbar, jedoch wird eine Satelliten-Schüssel benötigt&lt;br /&gt;
* &#039;&#039;&#039;DVB-C&#039;&#039;&#039;, also digitales Fernsehen, das über das Kabelnetz ausgestrahlt wird - Kosten für den Kabelanschluss fallen an, Sender-Angebot vom Kabelanbieter abhängig&lt;br /&gt;
* &#039;&#039;&#039;Analog-TV&#039;&#039;&#039;, also analoges Fernsehen, das über das Kabelnetz ausgestrahlt wird - Kosten für den Kabelanschluss fallen an, kein HD, Sender-Angebot vom Kabelanbieter abhängig&lt;br /&gt;
&lt;br /&gt;
Nicht alle Geräte werden von Linux unterstützt, eine Übersicht, die nach Übertragungstechnik (DVB-T/-S/-C oder Analog) und Anschluss-Art (beim Raspberry Pi immer USB) geordnet ist, findet man im [http://linuxtv.org/wiki/index.php/Hardware_Device_Information LinuxTVWiki], zu dem man natürlich auch selbst beitragen kann, wenn man ein Gerät getestet hat.&lt;br /&gt;
==== Soundkarten ====&lt;br /&gt;
Da der Klinke-Ausgang des Raspberry Pi nicht mit einem D/A-Wandler, sondern nur per PWM funktioniert, ist die Sound-Qualität nicht gerade überragend. Wenn man das verbessern will, oder wenn man Sound per Line-Eingang oder per Mikrofon aufnehmen will, bietet es sich an, eine USB-Soundkarte anzuschließen.&lt;br /&gt;
&lt;br /&gt;
Ist man unsicher, ob die Soundkarte unterstützt wird, hilft ein Blick ins [http://www.alsa-project.org/main/index.php/Matrix:Main Wiki vom ALSA-Projekt].&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-02-11/archlinux-hf-2013-02-11.zip.torrent Torrent-Download von Arch Linux ARM 2013-02-11]&lt;br /&gt;
* [http://downloads.raspberrypi.org/images/archlinuxarm/archlinux-hf-2013-02-11/archlinux-hf-2013-02-11.zip HTTP-Download von Arch Linux ARM 2013-02-11] (SHA-1: 1d2508908e7d8c899f4a5284e855cb27c17645dc)&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.raspberrypi.org/ offizielle Website des Raspberry Pi] (englisch)&lt;br /&gt;
* [https://de.wikipedia.org/wiki/Raspberry_Pi Wikipedia: Raspberry Pi] (deutsch)&lt;br /&gt;
* [http://www.themagpi.com/ The MagPi] (englisch)&lt;br /&gt;
* [http://archlinuxarm.org/ Arch Linux ARM] (englisch)&lt;br /&gt;
* [https://www.archlinux.org/ Arch Linux] (englisch)&lt;br /&gt;
* [https://de.wikipedia.org/wiki/Arch_Linux Wikipedia: Arch Linux] (deutsch)&lt;br /&gt;
* [http://www.kammerath.net/asterisk-virtuelle-telefonanlage.html Asterisk auf dem Raspberry Pi] (deutsch)&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Raspberry Pi]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75372</id>
		<title>Linux Boards</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75372"/>
		<updated>2013-04-23T13:15:05Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier sollen Links auf Linux Boards gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
* [http://pandaboard.org/content/platform Pandabaord ES]: ARM Cortex-A9 DualCore (OMAP4460), 1200 Mhz, 1GB DDR2-RAM, HDMI, 3.5&amp;quot; Audio, SD/MMC Card, USB2.0 Host+OTG, Ethernet, WiFi, UART, LCD, Expansion Header (SPI, I2C, GPMC, ...)&lt;br /&gt;
* [[Raspberry Pi]]: [[ARM]]11, 700MHz&lt;br /&gt;
* [[Bifferboard]]: 486SX-kompatibel, 150 MHz&lt;br /&gt;
* [[Mini2440]],[[Micro2440]]: [[ARM]]920T, 400Mhz&lt;br /&gt;
* [[ATNGW100]]: [[AVR32]], AT32AP7000&lt;br /&gt;
* [http://beagleboard.org/ Beagle Board]: ARM Cortex-A8, OMAP3530&lt;br /&gt;
* [http://beagleboard.org/bone BeagleBone]: 700 MHz CortexTM-A8, 256MB, Ethernet, USB-Host, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://beagleboard.org/Products/BeagleBone%20Black BeagleBone Black]: 1 GHz CortexTM-A8, 512MB, Ethernet, USB-Host, Micro-HDMI, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://www.propox.com/products/t_232.html MMnet1002]&lt;br /&gt;
* [http://www.propox.com/products/t_231.html MMnet1001]&lt;br /&gt;
* [https://www.olimex.com/Products/ARM/Atmel/SAM9-L9261 Olimex SAM9-L9261]&lt;br /&gt;
* [http://www.igep-platform.com/index.php?option=com_content&amp;amp;view=article&amp;amp;id=46&amp;amp;Itemid=55IGEPv2 Board]: TI OMAP35x&lt;br /&gt;
* [http://wandboard.org Wandboard]: Freescale i.MX6, ARM Cortex-A9, 2x1GHz, 1GB&lt;br /&gt;
&lt;br /&gt;
* [http://www.gnublin.org/ Gnublin] [[ARM]]9 180 MHz (LPC3131), 32MB RAM&lt;br /&gt;
* [http://www.8devices.com/product/3/wi-fi-4-things Carambola] [http://www.tracermcc.ru/foto/bender/RT3050_5x_V2.0_081408_0902.pdf RT3050] 320Mhz MIPS Soc 8 MB Flash / 32 MB RAM, Wifi, USB, 2*Eth, 2*ser, IIC, SPI, GPIO, Openwrt, 35x45mm, Stiftleisten zum aufsetzen auf andere Boards.&lt;br /&gt;
* [http://www.hardkernel.com/renewal_2011/products/prdt_info.php?g_code=G133999328931 ODROID-X] Samsung Exynos4412 Cortex-A9 Quad Core 1.4Ghz &lt;br /&gt;
* [http://www.arndaleboard.org/wiki/index.php/Main_Page Arndale1] [http://www.howchip.com/shop/content.php?co_id=ArndaleBoard_en Arndale2] Samsung Exynos 5 Cortex-A15 Dual Core 1.7Ghz &lt;br /&gt;
* [http://www.acmesystems.it/aria Aria G25] ARM9 (AT91SAM9G25, 400Mhz), 128/256 MB RAM , Ethernet, USB, SPI, GPIO, Lötpads zum Aufsetzen auf Motherboard, Debian verfügbar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] Prozessor basierte Boards&lt;br /&gt;
&lt;br /&gt;
* [http://www.adafruit.com/blog/2010/08/30/new-product-chumby-hacker-board-beta/ Chumby Hacker Board]: Freescale iMX.233, 454 MHZ, 64MB RAM&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/iMX233/ Olimex Board] iMX2233-OLinuXino-Mini/Micro/Maxi&lt;br /&gt;
** basiert auf [http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] ARM926J 454Mhz Prozessor &lt;br /&gt;
**[https://www.olimex.com/wiki/Category:IMX233 Wiki zum Board]&lt;br /&gt;
**[https://www.youtube.com/watch?v=dZiw--D-Vts&amp;amp;list=PL6-W3FoUyb4-hwrYcNDHPrU9uykcuAgLm&amp;amp;index=5&amp;amp;feature=plpp_video Video zum Board]&lt;br /&gt;
**erhältlich auch bei [http://www.watterott.com/de/OLinuXino?xb966c=91b2d11066939e0e27ee6227ee55dda9 watterott.com ] und [http://thinkembedded.ch/advanced_search_result.php?keywords=imx233&amp;amp;x=0&amp;amp;y=0 thinkembedded.ch] &lt;br /&gt;
&lt;br /&gt;
AllWinner Prozessor basierte Boards:&lt;br /&gt;
 [http://linux-sunxi.org/Main_Page Linux Images zu Allwinner Boards]&lt;br /&gt;
 [https://github.com/OLIMEX/OLINUXINO Github von Olimex mit Schema, Layout und Software]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino/ OLinuXino A13 Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi optional erhältlich (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/Linux/A13-OLinuXino::156.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-WIFI/ OLinuXino A13 Wifi Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/A13-OLinuXino-WIFI::158.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-MICRO/ A13-OLinuXino-MICRO Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 1 USB Host,VGA, Audio-OUT, Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** erhältlich, [https://www.olimex.com/wiki/A13-OLinuXino-MICRO Preliminary Debian Image]&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/30/a10-olinuxino-first-prototypes-are-assembling/ A10-OLinuXino Board]&lt;br /&gt;
** A10 Cortex A8 processor @ 1Ghz&lt;br /&gt;
** 1GB DDR3 memory,4GB NAND Flash, micro-SDcard,normale SD/MMC card&lt;br /&gt;
** HDMI,VGA,LCD &lt;br /&gt;
** RS232 UART,100Mbit Ethernet,2 USB hosts,1 USB OTG,SATA,JTAG&lt;br /&gt;
** Total 132 GPIOs,console debug UART port&lt;br /&gt;
** Audio Output/Input, Line Input,7 Android, RESET and Power-Down Buttons&lt;br /&gt;
** 6-16VDC input power supply,LiPo battery power option &lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/26/new-exciting-processors-from-allwinner-a10s-cortex-a8-with-hdmi-and-a4x-cortex-a7-4-cores/ A10s-OLinuXino Board]&lt;br /&gt;
** A10S Cortex-A8 1GHz&lt;br /&gt;
** 512MB RAM DDR3&lt;br /&gt;
** micro SD-card, SD-MMC for SDMMC WIFI card/second MMC for storage&lt;br /&gt;
** Ethernet 100MBit,HDMI output,USB host, USB-OTG,UEXT&lt;br /&gt;
** LCD connector,GPIO connectors,JTAG &lt;br /&gt;
** Audio Line-IN, Audio-Out&lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [http://cubieboard.org Cubieboard] &lt;br /&gt;
** basiert auf einem Allwinner A10 Prozessor (hat zusätzlich zu A13 Prozessor Sata und HDMI)&lt;br /&gt;
** 1G ARM Cortex-A8 Prozessor, Mali400, OpenGL ES GPU&lt;br /&gt;
** 512M/1GB DDR3 @480MHz, 4GB Nand Flash, HDMI 1080p Output&lt;br /&gt;
** 10/100M Ethernet, 2 USB Host, 1 micro SD slot, 1 SATA, 1 Ir&lt;br /&gt;
** 96 extend pin including I2C, SPI, RGB/LVDS, CSI/TS, FM-IN, ADC, CVBS, VGA, SPDIF-OUT&lt;br /&gt;
** Android, Ubuntu und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** [https://www.youtube.com/watch?feature=player_embedded&amp;amp;v=s2xGu6urECA Video zum Board]&lt;br /&gt;
&lt;br /&gt;
* [http://gooseberry.atspace.co.uk/ Gooseberry]&lt;br /&gt;
&lt;br /&gt;
* [https://www.miniand.com/products/Hackberry%20A10%20Developer%20Board HackBerry ]&lt;br /&gt;
** AllWinner A10, 1 GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
** 10/100Mbit Ethernet, Wifi – 802.11 b/g/n&lt;br /&gt;
** Video Output: 1 x HDMI + 1x Composite, Audio: Headphone + microphone, IR sensor, zwei USB 2.0 ports&lt;br /&gt;
&lt;br /&gt;
* [http://www.pcduino.com pcDuino]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1GHz, 1GB RAM, 2GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
* [http://www.marsboard.com Marsboard]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1,2GHz, 1GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Linux-Boards]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75371</id>
		<title>Linux Boards</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75371"/>
		<updated>2013-04-23T13:11:00Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier sollen Links auf Linux Boards gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
* [http://pandaboard.org/content/platform Pandabaord ES]: ARM Cortex-A9 DualCore (OMAP4460), 1200 Mhz, 1GB DDR2-RAM&lt;br /&gt;
* [[Raspberry Pi]]: [[ARM]]11, 700MHz&lt;br /&gt;
* [[Bifferboard]]: 486SX-kompatibel, 150 MHz&lt;br /&gt;
* [[Mini2440]],[[Micro2440]]: [[ARM]]920T, 400Mhz&lt;br /&gt;
* [[ATNGW100]]: [[AVR32]], AT32AP7000&lt;br /&gt;
* [http://beagleboard.org/ Beagle Board]: ARM Cortex-A8, OMAP3530&lt;br /&gt;
* [http://beagleboard.org/bone BeagleBone]: 700 MHz CortexTM-A8, 256MB, Ethernet, USB-Host, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://beagleboard.org/Products/BeagleBone%20Black BeagleBone Black]: 1 GHz CortexTM-A8, 512MB, Ethernet, USB-Host, Micro-HDMI, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://www.propox.com/products/t_232.html MMnet1002]&lt;br /&gt;
* [http://www.propox.com/products/t_231.html MMnet1001]&lt;br /&gt;
* [https://www.olimex.com/Products/ARM/Atmel/SAM9-L9261 Olimex SAM9-L9261]&lt;br /&gt;
* [http://www.igep-platform.com/index.php?option=com_content&amp;amp;view=article&amp;amp;id=46&amp;amp;Itemid=55IGEPv2 Board]: TI OMAP35x&lt;br /&gt;
* [http://wandboard.org Wandboard]: Freescale i.MX6, ARM Cortex-A9, 2x1GHz, 1GB&lt;br /&gt;
&lt;br /&gt;
* [http://www.gnublin.org/ Gnublin] [[ARM]]9 180 MHz (LPC3131), 32MB RAM&lt;br /&gt;
* [http://www.8devices.com/product/3/wi-fi-4-things Carambola] [http://www.tracermcc.ru/foto/bender/RT3050_5x_V2.0_081408_0902.pdf RT3050] 320Mhz MIPS Soc 8 MB Flash / 32 MB RAM, Wifi, USB, 2*Eth, 2*ser, IIC, SPI, GPIO, Openwrt, 35x45mm, Stiftleisten zum aufsetzen auf andere Boards.&lt;br /&gt;
* [http://www.hardkernel.com/renewal_2011/products/prdt_info.php?g_code=G133999328931 ODROID-X] Samsung Exynos4412 Cortex-A9 Quad Core 1.4Ghz &lt;br /&gt;
* [http://www.arndaleboard.org/wiki/index.php/Main_Page Arndale1] [http://www.howchip.com/shop/content.php?co_id=ArndaleBoard_en Arndale2] Samsung Exynos 5 Cortex-A15 Dual Core 1.7Ghz &lt;br /&gt;
* [http://www.acmesystems.it/aria Aria G25] ARM9 (AT91SAM9G25, 400Mhz), 128/256 MB RAM , Ethernet, USB, SPI, GPIO, Lötpads zum Aufsetzen auf Motherboard, Debian verfügbar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] Prozessor basierte Boards&lt;br /&gt;
&lt;br /&gt;
* [http://www.adafruit.com/blog/2010/08/30/new-product-chumby-hacker-board-beta/ Chumby Hacker Board]: Freescale iMX.233, 454 MHZ, 64MB RAM&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/iMX233/ Olimex Board] iMX2233-OLinuXino-Mini/Micro/Maxi&lt;br /&gt;
** basiert auf [http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] ARM926J 454Mhz Prozessor &lt;br /&gt;
**[https://www.olimex.com/wiki/Category:IMX233 Wiki zum Board]&lt;br /&gt;
**[https://www.youtube.com/watch?v=dZiw--D-Vts&amp;amp;list=PL6-W3FoUyb4-hwrYcNDHPrU9uykcuAgLm&amp;amp;index=5&amp;amp;feature=plpp_video Video zum Board]&lt;br /&gt;
**erhältlich auch bei [http://www.watterott.com/de/OLinuXino?xb966c=91b2d11066939e0e27ee6227ee55dda9 watterott.com ] und [http://thinkembedded.ch/advanced_search_result.php?keywords=imx233&amp;amp;x=0&amp;amp;y=0 thinkembedded.ch] &lt;br /&gt;
&lt;br /&gt;
AllWinner Prozessor basierte Boards:&lt;br /&gt;
 [http://linux-sunxi.org/Main_Page Linux Images zu Allwinner Boards]&lt;br /&gt;
 [https://github.com/OLIMEX/OLINUXINO Github von Olimex mit Schema, Layout und Software]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino/ OLinuXino A13 Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi optional erhältlich (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/Linux/A13-OLinuXino::156.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-WIFI/ OLinuXino A13 Wifi Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/A13-OLinuXino-WIFI::158.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-MICRO/ A13-OLinuXino-MICRO Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 1 USB Host,VGA, Audio-OUT, Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** erhältlich, [https://www.olimex.com/wiki/A13-OLinuXino-MICRO Preliminary Debian Image]&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/30/a10-olinuxino-first-prototypes-are-assembling/ A10-OLinuXino Board]&lt;br /&gt;
** A10 Cortex A8 processor @ 1Ghz&lt;br /&gt;
** 1GB DDR3 memory,4GB NAND Flash, micro-SDcard,normale SD/MMC card&lt;br /&gt;
** HDMI,VGA,LCD &lt;br /&gt;
** RS232 UART,100Mbit Ethernet,2 USB hosts,1 USB OTG,SATA,JTAG&lt;br /&gt;
** Total 132 GPIOs,console debug UART port&lt;br /&gt;
** Audio Output/Input, Line Input,7 Android, RESET and Power-Down Buttons&lt;br /&gt;
** 6-16VDC input power supply,LiPo battery power option &lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/26/new-exciting-processors-from-allwinner-a10s-cortex-a8-with-hdmi-and-a4x-cortex-a7-4-cores/ A10s-OLinuXino Board]&lt;br /&gt;
** A10S Cortex-A8 1GHz&lt;br /&gt;
** 512MB RAM DDR3&lt;br /&gt;
** micro SD-card, SD-MMC for SDMMC WIFI card/second MMC for storage&lt;br /&gt;
** Ethernet 100MBit,HDMI output,USB host, USB-OTG,UEXT&lt;br /&gt;
** LCD connector,GPIO connectors,JTAG &lt;br /&gt;
** Audio Line-IN, Audio-Out&lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [http://cubieboard.org Cubieboard] &lt;br /&gt;
** basiert auf einem Allwinner A10 Prozessor (hat zusätzlich zu A13 Prozessor Sata und HDMI)&lt;br /&gt;
** 1G ARM Cortex-A8 Prozessor, Mali400, OpenGL ES GPU&lt;br /&gt;
** 512M/1GB DDR3 @480MHz, 4GB Nand Flash, HDMI 1080p Output&lt;br /&gt;
** 10/100M Ethernet, 2 USB Host, 1 micro SD slot, 1 SATA, 1 Ir&lt;br /&gt;
** 96 extend pin including I2C, SPI, RGB/LVDS, CSI/TS, FM-IN, ADC, CVBS, VGA, SPDIF-OUT&lt;br /&gt;
** Android, Ubuntu und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** [https://www.youtube.com/watch?feature=player_embedded&amp;amp;v=s2xGu6urECA Video zum Board]&lt;br /&gt;
&lt;br /&gt;
* [http://gooseberry.atspace.co.uk/ Gooseberry]&lt;br /&gt;
&lt;br /&gt;
* [https://www.miniand.com/products/Hackberry%20A10%20Developer%20Board HackBerry ]&lt;br /&gt;
** AllWinner A10, 1 GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
** 10/100Mbit Ethernet, Wifi – 802.11 b/g/n&lt;br /&gt;
** Video Output: 1 x HDMI + 1x Composite, Audio: Headphone + microphone, IR sensor, zwei USB 2.0 ports&lt;br /&gt;
&lt;br /&gt;
* [http://www.pcduino.com pcDuino]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1GHz, 1GB RAM, 2GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
* [http://www.marsboard.com Marsboard]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1,2GHz, 1GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Linux-Boards]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75370</id>
		<title>Linux Boards</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75370"/>
		<updated>2013-04-23T13:10:00Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier sollen Links auf Linux Boards gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
* [http://pandaboard.org/content/platform Pandabaord ES]: ARM Cortex-A9 DualCore, 1200 Mhz, 1GB DDR2-RAM&lt;br /&gt;
* [[Raspberry Pi]]: [[ARM]]11, 700MHz&lt;br /&gt;
* [[Bifferboard]]: 486SX-kompatibel, 150 MHz&lt;br /&gt;
* [[Mini2440]],[[Micro2440]]: [[ARM]]920T, 400Mhz&lt;br /&gt;
* [[ATNGW100]]: [[AVR32]], AT32AP7000&lt;br /&gt;
* [http://beagleboard.org/ Beagle Board]: ARM Cortex-A8, OMAP3530&lt;br /&gt;
* [http://beagleboard.org/bone BeagleBone]: 700 MHz CortexTM-A8, 256MB, Ethernet, USB-Host, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://beagleboard.org/Products/BeagleBone%20Black BeagleBone Black]: 1 GHz CortexTM-A8, 512MB, Ethernet, USB-Host, Micro-HDMI, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://www.propox.com/products/t_232.html MMnet1002]&lt;br /&gt;
* [http://www.propox.com/products/t_231.html MMnet1001]&lt;br /&gt;
* [https://www.olimex.com/Products/ARM/Atmel/SAM9-L9261 Olimex SAM9-L9261]&lt;br /&gt;
* [http://www.igep-platform.com/index.php?option=com_content&amp;amp;view=article&amp;amp;id=46&amp;amp;Itemid=55IGEPv2 Board]: TI OMAP35x&lt;br /&gt;
* [http://wandboard.org Wandboard]: Freescale i.MX6, ARM Cortex-A9, 2x1GHz, 1GB&lt;br /&gt;
&lt;br /&gt;
* [http://www.gnublin.org/ Gnublin] [[ARM]]9 180 MHz (LPC3131), 32MB RAM&lt;br /&gt;
* [http://www.8devices.com/product/3/wi-fi-4-things Carambola] [http://www.tracermcc.ru/foto/bender/RT3050_5x_V2.0_081408_0902.pdf RT3050] 320Mhz MIPS Soc 8 MB Flash / 32 MB RAM, Wifi, USB, 2*Eth, 2*ser, IIC, SPI, GPIO, Openwrt, 35x45mm, Stiftleisten zum aufsetzen auf andere Boards.&lt;br /&gt;
* [http://www.hardkernel.com/renewal_2011/products/prdt_info.php?g_code=G133999328931 ODROID-X] Samsung Exynos4412 Cortex-A9 Quad Core 1.4Ghz &lt;br /&gt;
* [http://www.arndaleboard.org/wiki/index.php/Main_Page Arndale1] [http://www.howchip.com/shop/content.php?co_id=ArndaleBoard_en Arndale2] Samsung Exynos 5 Cortex-A15 Dual Core 1.7Ghz &lt;br /&gt;
* [http://www.acmesystems.it/aria Aria G25] ARM9 (AT91SAM9G25, 400Mhz), 128/256 MB RAM , Ethernet, USB, SPI, GPIO, Lötpads zum Aufsetzen auf Motherboard, Debian verfügbar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] Prozessor basierte Boards&lt;br /&gt;
&lt;br /&gt;
* [http://www.adafruit.com/blog/2010/08/30/new-product-chumby-hacker-board-beta/ Chumby Hacker Board]: Freescale iMX.233, 454 MHZ, 64MB RAM&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/iMX233/ Olimex Board] iMX2233-OLinuXino-Mini/Micro/Maxi&lt;br /&gt;
** basiert auf [http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] ARM926J 454Mhz Prozessor &lt;br /&gt;
**[https://www.olimex.com/wiki/Category:IMX233 Wiki zum Board]&lt;br /&gt;
**[https://www.youtube.com/watch?v=dZiw--D-Vts&amp;amp;list=PL6-W3FoUyb4-hwrYcNDHPrU9uykcuAgLm&amp;amp;index=5&amp;amp;feature=plpp_video Video zum Board]&lt;br /&gt;
**erhältlich auch bei [http://www.watterott.com/de/OLinuXino?xb966c=91b2d11066939e0e27ee6227ee55dda9 watterott.com ] und [http://thinkembedded.ch/advanced_search_result.php?keywords=imx233&amp;amp;x=0&amp;amp;y=0 thinkembedded.ch] &lt;br /&gt;
&lt;br /&gt;
AllWinner Prozessor basierte Boards:&lt;br /&gt;
 [http://linux-sunxi.org/Main_Page Linux Images zu Allwinner Boards]&lt;br /&gt;
 [https://github.com/OLIMEX/OLINUXINO Github von Olimex mit Schema, Layout und Software]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino/ OLinuXino A13 Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi optional erhältlich (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/Linux/A13-OLinuXino::156.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-WIFI/ OLinuXino A13 Wifi Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/A13-OLinuXino-WIFI::158.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-MICRO/ A13-OLinuXino-MICRO Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 1 USB Host,VGA, Audio-OUT, Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** erhältlich, [https://www.olimex.com/wiki/A13-OLinuXino-MICRO Preliminary Debian Image]&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/30/a10-olinuxino-first-prototypes-are-assembling/ A10-OLinuXino Board]&lt;br /&gt;
** A10 Cortex A8 processor @ 1Ghz&lt;br /&gt;
** 1GB DDR3 memory,4GB NAND Flash, micro-SDcard,normale SD/MMC card&lt;br /&gt;
** HDMI,VGA,LCD &lt;br /&gt;
** RS232 UART,100Mbit Ethernet,2 USB hosts,1 USB OTG,SATA,JTAG&lt;br /&gt;
** Total 132 GPIOs,console debug UART port&lt;br /&gt;
** Audio Output/Input, Line Input,7 Android, RESET and Power-Down Buttons&lt;br /&gt;
** 6-16VDC input power supply,LiPo battery power option &lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/26/new-exciting-processors-from-allwinner-a10s-cortex-a8-with-hdmi-and-a4x-cortex-a7-4-cores/ A10s-OLinuXino Board]&lt;br /&gt;
** A10S Cortex-A8 1GHz&lt;br /&gt;
** 512MB RAM DDR3&lt;br /&gt;
** micro SD-card, SD-MMC for SDMMC WIFI card/second MMC for storage&lt;br /&gt;
** Ethernet 100MBit,HDMI output,USB host, USB-OTG,UEXT&lt;br /&gt;
** LCD connector,GPIO connectors,JTAG &lt;br /&gt;
** Audio Line-IN, Audio-Out&lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [http://cubieboard.org Cubieboard] &lt;br /&gt;
** basiert auf einem Allwinner A10 Prozessor (hat zusätzlich zu A13 Prozessor Sata und HDMI)&lt;br /&gt;
** 1G ARM Cortex-A8 Prozessor, Mali400, OpenGL ES GPU&lt;br /&gt;
** 512M/1GB DDR3 @480MHz, 4GB Nand Flash, HDMI 1080p Output&lt;br /&gt;
** 10/100M Ethernet, 2 USB Host, 1 micro SD slot, 1 SATA, 1 Ir&lt;br /&gt;
** 96 extend pin including I2C, SPI, RGB/LVDS, CSI/TS, FM-IN, ADC, CVBS, VGA, SPDIF-OUT&lt;br /&gt;
** Android, Ubuntu und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** [https://www.youtube.com/watch?feature=player_embedded&amp;amp;v=s2xGu6urECA Video zum Board]&lt;br /&gt;
&lt;br /&gt;
* [http://gooseberry.atspace.co.uk/ Gooseberry]&lt;br /&gt;
&lt;br /&gt;
* [https://www.miniand.com/products/Hackberry%20A10%20Developer%20Board HackBerry ]&lt;br /&gt;
** AllWinner A10, 1 GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
** 10/100Mbit Ethernet, Wifi – 802.11 b/g/n&lt;br /&gt;
** Video Output: 1 x HDMI + 1x Composite, Audio: Headphone + microphone, IR sensor, zwei USB 2.0 ports&lt;br /&gt;
&lt;br /&gt;
* [http://www.pcduino.com pcDuino]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1GHz, 1GB RAM, 2GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
* [http://www.marsboard.com Marsboard]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1,2GHz, 1GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Linux-Boards]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75369</id>
		<title>Linux Boards</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Linux_Boards&amp;diff=75369"/>
		<updated>2013-04-23T13:09:36Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier sollen Links auf Linux Boards gesammelt werden.&lt;br /&gt;
&lt;br /&gt;
* [http://pandaboard.org/content/platform Pandabaord]: ARM Cortex-A9 DualCore, 1200 Mhz, 1GB DDR2-RAM&lt;br /&gt;
* [[Raspberry Pi]]: [[ARM]]11, 700MHz&lt;br /&gt;
* [[Bifferboard]]: 486SX-kompatibel, 150 MHz&lt;br /&gt;
* [[Mini2440]],[[Micro2440]]: [[ARM]]920T, 400Mhz&lt;br /&gt;
* [[ATNGW100]]: [[AVR32]], AT32AP7000&lt;br /&gt;
* [http://beagleboard.org/ Beagle Board]: ARM Cortex-A8, OMAP3530&lt;br /&gt;
* [http://beagleboard.org/bone BeagleBone]: 700 MHz CortexTM-A8, 256MB, Ethernet, USB-Host, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://beagleboard.org/Products/BeagleBone%20Black BeagleBone Black]: 1 GHz CortexTM-A8, 512MB, Ethernet, USB-Host, Micro-HDMI, mit Stiftbuchsen ähnlich Ardunio.&lt;br /&gt;
* [http://www.propox.com/products/t_232.html MMnet1002]&lt;br /&gt;
* [http://www.propox.com/products/t_231.html MMnet1001]&lt;br /&gt;
* [https://www.olimex.com/Products/ARM/Atmel/SAM9-L9261 Olimex SAM9-L9261]&lt;br /&gt;
* [http://www.igep-platform.com/index.php?option=com_content&amp;amp;view=article&amp;amp;id=46&amp;amp;Itemid=55IGEPv2 Board]: TI OMAP35x&lt;br /&gt;
* [http://wandboard.org Wandboard]: Freescale i.MX6, ARM Cortex-A9, 2x1GHz, 1GB&lt;br /&gt;
&lt;br /&gt;
* [http://www.gnublin.org/ Gnublin] [[ARM]]9 180 MHz (LPC3131), 32MB RAM&lt;br /&gt;
* [http://www.8devices.com/product/3/wi-fi-4-things Carambola] [http://www.tracermcc.ru/foto/bender/RT3050_5x_V2.0_081408_0902.pdf RT3050] 320Mhz MIPS Soc 8 MB Flash / 32 MB RAM, Wifi, USB, 2*Eth, 2*ser, IIC, SPI, GPIO, Openwrt, 35x45mm, Stiftleisten zum aufsetzen auf andere Boards.&lt;br /&gt;
* [http://www.hardkernel.com/renewal_2011/products/prdt_info.php?g_code=G133999328931 ODROID-X] Samsung Exynos4412 Cortex-A9 Quad Core 1.4Ghz &lt;br /&gt;
* [http://www.arndaleboard.org/wiki/index.php/Main_Page Arndale1] [http://www.howchip.com/shop/content.php?co_id=ArndaleBoard_en Arndale2] Samsung Exynos 5 Cortex-A15 Dual Core 1.7Ghz &lt;br /&gt;
* [http://www.acmesystems.it/aria Aria G25] ARM9 (AT91SAM9G25, 400Mhz), 128/256 MB RAM , Ethernet, USB, SPI, GPIO, Lötpads zum Aufsetzen auf Motherboard, Debian verfügbar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] Prozessor basierte Boards&lt;br /&gt;
&lt;br /&gt;
* [http://www.adafruit.com/blog/2010/08/30/new-product-chumby-hacker-board-beta/ Chumby Hacker Board]: Freescale iMX.233, 454 MHZ, 64MB RAM&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/iMX233/ Olimex Board] iMX2233-OLinuXino-Mini/Micro/Maxi&lt;br /&gt;
** basiert auf [http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=i.MX233 Freescale iMX233] ARM926J 454Mhz Prozessor &lt;br /&gt;
**[https://www.olimex.com/wiki/Category:IMX233 Wiki zum Board]&lt;br /&gt;
**[https://www.youtube.com/watch?v=dZiw--D-Vts&amp;amp;list=PL6-W3FoUyb4-hwrYcNDHPrU9uykcuAgLm&amp;amp;index=5&amp;amp;feature=plpp_video Video zum Board]&lt;br /&gt;
**erhältlich auch bei [http://www.watterott.com/de/OLinuXino?xb966c=91b2d11066939e0e27ee6227ee55dda9 watterott.com ] und [http://thinkembedded.ch/advanced_search_result.php?keywords=imx233&amp;amp;x=0&amp;amp;y=0 thinkembedded.ch] &lt;br /&gt;
&lt;br /&gt;
AllWinner Prozessor basierte Boards:&lt;br /&gt;
 [http://linux-sunxi.org/Main_Page Linux Images zu Allwinner Boards]&lt;br /&gt;
 [https://github.com/OLIMEX/OLINUXINO Github von Olimex mit Schema, Layout und Software]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino/ OLinuXino A13 Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi optional erhältlich (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/Linux/A13-OLinuXino::156.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-WIFI/ OLinuXino A13 Wifi Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 3 USB Host,VGA, Audio-IN-OUT, 5 Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** On Board Wifi (kein RJ45 onboard),[http://thinkembedded.ch/Linux/USB-ETHERNET-AX88772B::174.html optionaler USB-Ethernet Stick]&lt;br /&gt;
** Optional 7&#039; Touch Display [http://thinkembedded.ch/advanced_search_result.php?keywords=a13-lcd&amp;amp;x=0&amp;amp;y=0 erhältlich]&lt;br /&gt;
** [https://www.olimex.com/wiki/A13-OLinuXino Wiki zum Board]&lt;br /&gt;
** [https://www.olimex.com/wiki/Prebuilt_SD_card_images_running_debian fertige Linux Debian Images]&lt;br /&gt;
** [https://www.youtube.com/playlist?list=PL6-W3FoUyb4-Vkxe2e5_z9k-uOR41XX8B&amp;amp;feature=view_all Videos zum Board]&lt;br /&gt;
** [https://vintagegamer.wordpress.com/2012/10/18/diy-android-game-console/ Board als Android Gaming Console nutzen]&lt;br /&gt;
** [http://thinkembedded.ch/A13-OLinuXino-WIFI::158.html erhältlich auch hier]&lt;br /&gt;
&lt;br /&gt;
* [https://www.olimex.com/Products/OLinuXino/A13/A13-OLinuXino-MICRO/ A13-OLinuXino-MICRO Board] &lt;br /&gt;
** ein Cortex A8 basiertes Board (eLQFP176 package) mit dem A13 Prozessor von Allwinner Technology Inc. 1GHz, 3D Mali400 GPU&lt;br /&gt;
** 1 USB Host,VGA, Audio-OUT, Buttons, 1 USB-OTG welcher als Speisung benutzt wird, IC2, USART. &lt;br /&gt;
** Android 4.0.3, und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** erhältlich, [https://www.olimex.com/wiki/A13-OLinuXino-MICRO Preliminary Debian Image]&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/30/a10-olinuxino-first-prototypes-are-assembling/ A10-OLinuXino Board]&lt;br /&gt;
** A10 Cortex A8 processor @ 1Ghz&lt;br /&gt;
** 1GB DDR3 memory,4GB NAND Flash, micro-SDcard,normale SD/MMC card&lt;br /&gt;
** HDMI,VGA,LCD &lt;br /&gt;
** RS232 UART,100Mbit Ethernet,2 USB hosts,1 USB OTG,SATA,JTAG&lt;br /&gt;
** Total 132 GPIOs,console debug UART port&lt;br /&gt;
** Audio Output/Input, Line Input,7 Android, RESET and Power-Down Buttons&lt;br /&gt;
** 6-16VDC input power supply,LiPo battery power option &lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [https://olimex.wordpress.com/2012/11/26/new-exciting-processors-from-allwinner-a10s-cortex-a8-with-hdmi-and-a4x-cortex-a7-4-cores/ A10s-OLinuXino Board]&lt;br /&gt;
** A10S Cortex-A8 1GHz&lt;br /&gt;
** 512MB RAM DDR3&lt;br /&gt;
** micro SD-card, SD-MMC for SDMMC WIFI card/second MMC for storage&lt;br /&gt;
** Ethernet 100MBit,HDMI output,USB host, USB-OTG,UEXT&lt;br /&gt;
** LCD connector,GPIO connectors,JTAG &lt;br /&gt;
** Audio Line-IN, Audio-Out&lt;br /&gt;
** noch nicht erhältlich&lt;br /&gt;
&lt;br /&gt;
* [http://cubieboard.org Cubieboard] &lt;br /&gt;
** basiert auf einem Allwinner A10 Prozessor (hat zusätzlich zu A13 Prozessor Sata und HDMI)&lt;br /&gt;
** 1G ARM Cortex-A8 Prozessor, Mali400, OpenGL ES GPU&lt;br /&gt;
** 512M/1GB DDR3 @480MHz, 4GB Nand Flash, HDMI 1080p Output&lt;br /&gt;
** 10/100M Ethernet, 2 USB Host, 1 micro SD slot, 1 SATA, 1 Ir&lt;br /&gt;
** 96 extend pin including I2C, SPI, RGB/LVDS, CSI/TS, FM-IN, ADC, CVBS, VGA, SPDIF-OUT&lt;br /&gt;
** Android, Ubuntu und div. Linux ab int. Flash oder ext. SD-Card.&lt;br /&gt;
** [https://www.youtube.com/watch?feature=player_embedded&amp;amp;v=s2xGu6urECA Video zum Board]&lt;br /&gt;
&lt;br /&gt;
* [http://gooseberry.atspace.co.uk/ Gooseberry]&lt;br /&gt;
&lt;br /&gt;
* [https://www.miniand.com/products/Hackberry%20A10%20Developer%20Board HackBerry ]&lt;br /&gt;
** AllWinner A10, 1 GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
** 10/100Mbit Ethernet, Wifi – 802.11 b/g/n&lt;br /&gt;
** Video Output: 1 x HDMI + 1x Composite, Audio: Headphone + microphone, IR sensor, zwei USB 2.0 ports&lt;br /&gt;
&lt;br /&gt;
* [http://www.pcduino.com pcDuino]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1GHz, 1GB RAM, 2GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
* [http://www.marsboard.com Marsboard]&lt;br /&gt;
** AllWinner A10 Cortex-A8 @ 1,2GHz, 1GB RAM, 4GB Flash + SDHC slot&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Linux-Boards]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=61509</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=61509"/>
		<updated>2011-11-08T07:28:48Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden.&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige interpretieren der Daten (Parsen). Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=61508</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=61508"/>
		<updated>2011-11-08T07:27:42Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Es muss darauf geachtet werden, dass beim flashen der Anwendung nicht der Bootloader überschrieben wird.&amp;lt;br&amp;gt;Bei Verwendung von avrdude muss dazu die Option &amp;quot;-D&amp;quot; angegeben werden.&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir uns der Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wenn das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommende ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=59357</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=59357"/>
		<updated>2011-08-09T13:43:23Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 3 - Programmieren des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;WICHTIGER HINWEIS: &#039;&#039;&#039;&#039;&#039; Es hat sich gezeigt, das die Flußkontrolle durch XON/XOFF nicht immer funktioniert, da es eine Software-Flußsteuerung ist und u.U. der (UART-Sende-)Interrupt zu spät (oder gar nicht) ausgeführt wird. Bei Problemen beim Übertragen des HEX-Files sollte man als erstes versuchen die Baudrate zu senken (oder die Taktrate des Controllers erhöhen) um dem Controller mehr Zeit zum Verarbeiten und schnellerem Reagieren auf Interrupts zu geben.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=59343</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=59343"/>
		<updated>2011-08-09T11:37:39Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 3 - Programmieren des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspeicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem_sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
&lt;br /&gt;
    char sregtemp = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    SREG = sregtemp;&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
&lt;br /&gt;
    /* vor Rücksprung eventuell benutzte Hardware deaktivieren&lt;br /&gt;
       und Interrupts global deaktivieren, da kein &amp;quot;echter&amp;quot; Reset erfolgt */&lt;br /&gt;
&lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    cli();&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht. Um versehentliches verstellen der Interrupttabelle zu vermeiden muss das setzen von &#039;&#039;&#039;IVCE&#039;&#039;&#039; und &#039;&#039;&#039;IVSEL&#039;&#039;&#039; innerhalb von 4 Taktzyklen erfolgen. Um dies zu gewährleisten müssen alle interrupts während des Setzens deaktiviert sein. ACHTUNG Stolperfalle: Die Variable temp wird benötigt, da beim Setzen von &#039;&#039;&#039;IVSEL&#039;&#039;&#039; gleichzeitig &#039;&#039;&#039;IVCE&#039;&#039;&#039; gelöscht werden muss:&lt;br /&gt;
&lt;br /&gt;
bei Atmega8/16:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVCE);   // noch richtig. IVCE wird gesetzt&lt;br /&gt;
GICR |= (1&amp;lt;&amp;lt;IVSEL);  // falsch! IVSEL wird zwar gesetzt, &lt;br /&gt;
                        IVCE bleibt jedoch in diesem Prozessortakt gesetzt.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wir &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54700</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54700"/>
		<updated>2011-02-02T08:08:08Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden. Es sind wieder die korrekte Taktfrequenz und die Verschiebung der Sektion &#039;&#039;&#039;.text&#039;&#039;&#039; auf die Bootresetadresse einzustellen.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54699</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54699"/>
		<updated>2011-02-02T08:07:12Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 4 - Ausprobieren der Anwendung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloaders widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54698</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54698"/>
		<updated>2011-02-02T08:05:17Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 3 - Programmieren des Bootloaders */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weiter sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54697</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54697"/>
		<updated>2011-02-02T08:02:35Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 1 - Konfiguration der Projekteinstellungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein (Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz). Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54696</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54696"/>
		<updated>2011-02-02T08:01:38Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Der &amp;quot;Hallo Welt&amp;quot; - Bootloader */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie oben erwähnt, wird für die Erstellung des Codes die kostenlose IDE von Atmel - das AVRStudio - benutzt. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminalprogramm benötigt. Im Tutorial wird PuTTY verwendet und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn wird ein neues AVRStudio-Projekt erstellt. Danach werden folgende Schritte abgearbeitet:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Von dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 MHz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54126</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54126"/>
		<updated>2011-01-10T13:47:44Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt beim AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54125</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54125"/>
		<updated>2011-01-10T13:46:27Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adressen sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54124</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54124"/>
		<updated>2011-01-10T13:44:48Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegennimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54123</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54123"/>
		<updated>2011-01-10T13:44:30Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne Weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegen nimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54122</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54122"/>
		<updated>2011-01-10T13:44:05Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping und kleine Anwendungen hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegen nimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54121</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54121"/>
		<updated>2011-01-10T13:43:45Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Dies ist für Prototyping ein kleine Anwendung hinnehmbar. Ist der Controller allerdings in einem größeren System oder in größerer räumlicher Entfernung verbaut und die ISP-Schnittstelle nicht mehr zugänglich, ist ein Update der Firmware nicht mehr ohne weiteres möglich oder sehr teuer und aufwendig. Hier kann ein Bootloader Abhilfe schaffen, in dem er das Anwendungsprogramm auf einer definierten Schnittstelle entgegen nimmt (UART, I2C, Wireless) und ins Flash transferiert. Ein Bootloader ist also in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54120</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54120"/>
		<updated>2011-01-10T13:38:08Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise kann während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Ein Bootloader hingegen ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54119</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54119"/>
		<updated>2011-01-10T13:37:47Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalerweise während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Ein Bootloader hingegen ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54118</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=54118"/>
		<updated>2011-01-10T13:37:32Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Normalwerweise während der Ausführung des Programms nicht auf den Flashspreicher geschrieben werden. Dies ist auch einleuchtend da sich das Programm ja sonst selbst überschreiben oder löschen könnte. Das Beschreiben des Flashs erfolgt bei AVR üblicherweise über die ISP-Schnittstelle, dabei befindet sich der Controller im Reset und es wird kein Programm ausgeführt. Ein Bootloader hingegen ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;Linker Options&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Dieses Beispiel soll weitestgehend übernommen werden, da es die nötige Funktionalität beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einen Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53170</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53170"/>
		<updated>2010-12-01T13:52:27Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 1 - Konfiguration der Projekteinstellungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speichers sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53169</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53169"/>
		<updated>2010-12-01T13:51:03Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53168</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53168"/>
		<updated>2010-12-01T13:50:47Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die Multiplikation der &#039;&#039;Page&#039;&#039;-Größe mit der Anzahl der &#039;&#039;Pages&#039;&#039; ergibt die Speichergröße. Die Größe einer &#039;&#039;Page&#039;&#039; steht im Datenblatt und ist in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53167</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53167"/>
		<updated>2010-12-01T13:48:13Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53166</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53166"/>
		<updated>2010-12-01T13:47:59Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Grundlagen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf Teile des Flashs - der sogenannten zu &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; - zu schreiben. Die eigentliche Anwendung wird ausschließlich in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039; ausgeführt. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53165</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53165"/>
		<updated>2010-12-01T13:45:52Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige meinen dass es nicht möglich ist das Thema ohne tieferen Einblick in die Hardware und die AVR-Register zu beleuchten, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf den Flash (in dem er auch selbst steht) zu schreiben. Die eigentliche Anwendung steht dann in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039;. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53164</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=53164"/>
		<updated>2010-12-01T13:44:30Z</updated>

		<summary type="html">&lt;p&gt;Mario: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige sagen, dass es nicht möglich ist, das Thema zu beleuchten, ohne tief in die Hardware und AVR-Register einzusteigen, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloadern arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele wurden für den ATmega88 kompiliert und getestet.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf den Flash (in dem er auch selbst steht) zu schreiben. Die eigentliche Anwendung steht dann in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039;. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039; (ACHTUNG: beim Atmega8 &#039;&#039;&#039;GICR&#039;&#039;&#039;), also&lt;br /&gt;
&lt;br /&gt;
beim Atmega88:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. beim Atmega8:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = GICR;&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
GICR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039; - 10 = 65 -10 = 55 abgezogen, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Autor]&lt;br /&gt;
&lt;br /&gt;
= FAQ =&lt;br /&gt;
&lt;br /&gt;
== Wie kann man Bootloader und Anwendungsprogramm gemeinsam flashen? ==&lt;br /&gt;
&lt;br /&gt;
Es ist in C ohne weiteres nicht möglich ein gemeinsames Hexfile aus Bootloader und Anwendungsprogramm zu kompilieren. Aber man kann das getrennt erstellte Hexfile des Bootloaders und das getrennt erstellte Hexfile des Anwendungsprogramms zusammenfügen und so gemeinsam mit ISP flashen. Dazu die letzte Zeile des 1. Hexfiles entfernen und dahinter das 2. Hexfile anfügen [http://www.mikrocontroller.net/topic/199241#1955092], [http://www.mikrocontroller.net/topic/198428#1949164]. &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52696</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52696"/>
		<updated>2010-11-11T10:08:44Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Schritt 1 - Konfiguration der Projekteinstellungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige sagen, dass es nicht möglich ist, das Thema zu beleuchten, ohne tief in die Hardware und AVR-Register einzusteigen, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloader arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele laufen auf einem ATmega88.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf den Flash (in dem er auch selbst steht) zu schreiben. Die eigentliche Anwendung steht dann in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039;. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen &#039;&#039;linkt&#039;&#039; der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;, also so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039;=65 und 10 hinzugezählt, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Author]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52695</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52695"/>
		<updated>2010-11-11T10:08:08Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Der &amp;quot;Hallo Welt&amp;quot; - Bootloader */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige sagen, dass es nicht möglich ist, das Thema zu beleuchten, ohne tief in die Hardware und AVR-Register einzusteigen, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloader arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele laufen auf einem ATmega88.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf die beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf den Flash (in dem er auch selbst steht) zu schreiben. Die eigentliche Anwendung steht dann in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039;. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVRStudio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen linkt der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbehalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt es verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereichs haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben behandelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;, also so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das Linken der Standardbibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden werden wir uns eine einfache eigene Funktion schreiben, um die Zeichenfolgen umzuwandeln. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039;=65 und 10 hinzugezählt, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum Beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Author]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-taste.png&amp;diff=52694</id>
		<title>Datei:PuTTY-Bootloader-taste.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-taste.png&amp;diff=52694"/>
		<updated>2010-11-11T10:01:27Z</updated>

		<summary type="html">&lt;p&gt;Mario: hat eine neue Version von „Datei:PuTTY-Bootloader-taste.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-start.png&amp;diff=52693</id>
		<title>Datei:PuTTY-Bootloader-start.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-start.png&amp;diff=52693"/>
		<updated>2010-11-11T10:00:28Z</updated>

		<summary type="html">&lt;p&gt;Mario: hat eine neue Version von „Datei:PuTTY-Bootloader-start.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-restart.png&amp;diff=52692</id>
		<title>Datei:PuTTY-Bootloader-restart.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:PuTTY-Bootloader-restart.png&amp;diff=52692"/>
		<updated>2010-11-11T09:58:19Z</updated>

		<summary type="html">&lt;p&gt;Mario: hat eine neue Version von „Datei:PuTTY-Bootloader-restart.png“ hochgeladen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52466</id>
		<title>AVR Bootloader in C - eine einfache Anleitung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=52466"/>
		<updated>2010-11-02T07:58:01Z</updated>

		<summary type="html">&lt;p&gt;Mario: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser Artikel soll dazu dienen, das Thema [[Bootloader]] im AVR etwas zu demystifizieren.&lt;br /&gt;
&lt;br /&gt;
Es gibt schon einige Artikel und Codebeispiele für verschiedene Bootloader in Assembler oder C (bzw. gemischt), aber kein Artikel beleuchtet das Thema von einer einfachen Seite aus Anwendungssicht. In diese Lücke zielt dieses Tutorial.&lt;br /&gt;
Es soll anhand von Beispielen einen möglichst einfachen, verständlichen und nachvollziehbaren Weg zeigen, sich mit Hilfe der Hochsprache C in das Thema einzuarbeiten (dabei soll weder Assembler noch Inline-Assembler verwendet werden). &lt;br /&gt;
&lt;br /&gt;
Vielleicht werden einige sagen, dass es nicht möglich ist, das Thema zu beleuchten, ohne tief in die Hardware und AVR-Register einzusteigen, ich möchte es aber trotzdem versuchen.&lt;br /&gt;
&lt;br /&gt;
Der Artikel wird sich auf das notwendige Wissen beschränken, um mit Booloader arbeiten zu können. Es wird ein genereller Weg gezeigt, der sich leicht auf andere AVR-Devices (mit Bootloader Sektion) übertragen lässt.&lt;br /&gt;
Die Codebeispiele laufen auf einem ATmega88.&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Zu Beginn soll das notwendige Wissen über die Bootloaderunterstützung im AVR vermittelt werden, um eine Arbeitsgrundlage zu schaffen.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf des Artikels werden insgesamt drei Anwendungen programmiert: Zuerst ein einfacher Bootloader, welcher in der Bootloadersektion des Flashs ausgeführt wird, aber noch keine eigentliche Bootloader-Funktion hat, sozusagen ein &amp;quot;Hallo Welt&amp;quot;-Bootloader. Danach soll eine kleine Applikation programmiert werden, welche der spätere &#039;&#039;echte&#039;&#039; Bootloader ins Flash programmieren soll. Als großes Finale soll dann ein Bootloader entstehen, welcher in der Lage ist, Intel-HEX-Dateien über die serielle Schnittstelle zu laden, ins Flash zu programmieren und zu starten.&lt;br /&gt;
&lt;br /&gt;
Der Leser sollte bereits Erfahrungen im Umgang mit dem AVR Studio und der Programmiersprache C gemacht haben und schon Anwendungen geschrieben haben. Für absolute Einsteiger ist der Artikel ungeeignet.&lt;br /&gt;
&lt;br /&gt;
Den Thread zum Artikel gibt es hier: http://www.mikrocontroller.net/topic/195102&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel werden folgende Software-Pakete benötigt:&lt;br /&gt;
&lt;br /&gt;
* aktuelles [http://http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 AVR Studio] (hier verwendet: AVR Studio v4.18) &lt;br /&gt;
* aktuelles [http://sourceforge.net/projects/winavr/files/ WinAVR] (hier verwendet: WinAVR20100110)&lt;br /&gt;
* [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] als serielle Konsole (Version v0.6)&lt;br /&gt;
&lt;br /&gt;
Des weiteren wurde auf der AVR-Seite für die serielle Kommunikation mit dem PC auf den beliebte UART-Library von [http://www.jump.to/fleury Peter Fleury] zurückgegriffen, damit wir uns nicht um die gepufferte UART-Kommunikation kümmern müssen.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Für den Artikel wurde eine kleine Hardware bestehend aus einem [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302 Atmega88] und einem [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232] als USB-Seriell-Wandler erstellt. Dies soll als Basis für die Experimente dienen. Der USB-Seriell-Wandler ist nicht zwingend notwendig und kann auch durch den üblichen Pegelwandler vom Typ [http://www.maxim-ic.com/datasheet/index.mvp/id/1798/ln/en MAX232] ersetzt werden, wenn der PC noch eine serielle Schnittstelle besitzt. Entscheidend ist nur die Möglichkeit der seriellen Kommunikation mit dem Rechner.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schaltplan-ATmega88-USB.png|800px|Schaltplan]]&lt;br /&gt;
&lt;br /&gt;
Für die ISP-Programmierung wurde der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3808 AVRISPmkII-In-System-Programmer] von Atmel verwendet. Es kann natürlich auch ein anderer Programmer (z.B. [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 STK500]) verwendet werden, welcher mit dem AVR Studio zusammenarbeitet. Prinzipiell kann natürlich auch ein selbstgebastelter Parallel-Programmer verwendet werden, dann kann aber nicht via AVR Studio programmiert werden, sondern mit [http://www.nongnu.org/avrdude/ AVRDude] oder [http://www.lancos.com/prog.html PonyProg] o.ä. Der Artikel beschränkt sich auf die Verwendung vom AVR Studio.&lt;br /&gt;
&lt;br /&gt;
Für das Verständis der Hardware und der seriellen Kommunikation sind folgende Artikel empfehlenswert:&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: UART]]&lt;br /&gt;
* [[RS-232]]&lt;br /&gt;
&lt;br /&gt;
= Grundlagen =&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-Memory.png|400px|thumb|Flash Speicher Aufteilung]]&lt;br /&gt;
&lt;br /&gt;
Was ist eigentlich ein Bootloader und was macht er? Wofür sollte ich so etwas brauchen? Ist das nicht viel zu kompliziert? Ich bin eingefleischter AVR Studio-Benutzer, muss ich mich jetzt mit makefiles beschäftigen? Kann man im AVR Studio mit C überhaupt einen Bootloader schreiben? Vielleicht hat sich der eine oder andere schon einmal diese oder ähnliche Fragen gestellt. &lt;br /&gt;
&lt;br /&gt;
Nun, der Programmcode des AVR steht in seinem Flashspeicher und wird von dort ausgeführt. Ein Bootloader ist in erster Linie ein kleines Programm, welches in einem besonderem Teil des Flash steht - der &#039;&#039;&#039;Boot Loader Section&#039;&#039;&#039;.  Durch die Lokalisierung des Bootloader-Programms in dieser besonderen Sektion des AVR ist es dem Programm möglich, auf den Flash (in dem er auch selbst steht) zu schreiben. Die eigentliche Anwendung steht dann in der &#039;&#039;&#039;Application Flash Section&#039;&#039;&#039;. Wenn man so will, können im Flash des AVR also zwei unabhängige Programme stehen. Der Flash ist in zwei Bereiche mit unterschiedlichen Merkmalen aufgeteilt (siehe Bild). Auf die RWW bzw. NRWW-Sektion möchte ich an dieser Stelle (noch) nicht eingehen.&lt;br /&gt;
&lt;br /&gt;
Wie man unschwer erkennen kann, liegt der Bootloader-Bereich am Ende des Flash-Speichers. Normalerweise startet der Controller die Abarbeitung seiner Programmierung an der Stelle 0x0000. Ein Bootloader soll ja aber &#039;&#039;&#039;vor&#039;&#039;&#039; der Abarbeitung der eigentlichen Applikation ausgeführt werden. Woher weiß also der AVR-Controller nach dem Reset, dass er nicht von Adresse 0x0000 sondern einer anderen Adresse starten soll? Diese Konfiguration ist wie alle wichtigen und grundlegenden Konfigurationen über die Fuses des AVRs geregelt. Wir beginnen mit der folgende Tabelle, welche die Speicheraufteilung des Programmspeichers veranschaulicht.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Boot Size Konfiguration, Tabelle 26-6 im Atmega88-Datenblatt S.280&lt;br /&gt;
|- &lt;br /&gt;
! BOOTSZ1 || BOOTSZ0 || Boot&amp;lt;br&amp;gt;Size || Pages || Application&amp;lt;br&amp;gt;Flash Section || Boot Loader&amp;lt;br&amp;gt;Flash Section || End&amp;lt;br&amp;gt;Application&amp;lt;br&amp;gt;Section || Boot Reset&amp;lt;br&amp;gt;(Start Boot&amp;lt;br&amp;gt;Loader Section)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 128 words || 4 || 0x000 - 0xF7F || 0xF80 - 0xFFF || 0xF7F || 0xF80&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 256 words || 8 || 0x000 - 0xEFF || 0xF00 - 0xFFF || 0xEFF || 0xF00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 512 words || 16 || 0x000 - 0xDFF || 0xE00 - 0xFFF || 0xDFF || 0xE00&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || 1024 words || 32 || 0x000 - 0xBFF || 0xC00 - 0xFFF || 0xBFF || 0xC00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Um die Tabelle zu verstehen, muss man wissen, dass der Flash-Speicher intern in sogenannten &#039;&#039;Pages&#039;&#039; (Seiten) organisiert ist. Die einzelnen &#039;&#039;Pages&#039;&#039; haben eine bestimmte Größe, welche im Datenblatt in &#039;&#039;Words&#039;&#039; - also Datenworte - angegeben ist. Ein Datenwort entspricht zwei Bytes. &#039;&#039;&#039;&#039;&#039;Hier offenbart sich eine Tücke des Datenblatts: Alle Speicherbezüge und Adresse sind in Datenworten  (also immer 2 Bytes) angegeben!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
Aus der Tabelle erfahren wir auch, dass man mit den beiden Fuses &#039;&#039;&#039;BOOTSZ0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1&#039;&#039;&#039; die Größe des Bootloaderbereichs einstellen kann. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über die Aufteilung der Pages und die Anzahl der Datenworte einer Page.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ No.of Words in a Page and No.of Pages in Flash, Tabelle 27-9 im Atmega88-Datenblatt S.288&lt;br /&gt;
|- &lt;br /&gt;
!  Device || Flash Size || Page Size || PCWORD || No. of Pages || PCPAGE || PCMSB&lt;br /&gt;
|-&lt;br /&gt;
| Atmega88 || 4K words (8 Kbytes) || 32 words || PC[4:0] || 128 || PC[11:5] || 11&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aus der Tabelle ergibt sich, dass die Größe einer &#039;&#039;Page&#039;&#039; des verwendeten Atmega88 32 &#039;&#039;Words&#039;&#039; - also 64 Byte sind. Insgesamt gibt es 128 &#039;&#039;Pages&#039;&#039;, damit ergibt sich nach Adam Riese 128 * 64 = 8192 Byte, also 8 Kbytes. In unserem späteren Codebeispiel soll die Größe des Bootloaderbereichs auf 1024 &#039;&#039;words&#039;&#039; - also 2048 Bytes gestellt werden (&#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;). Nun können wir ausrechnen, in welcher Flash-&#039;&#039;Page&#039;&#039; bzw. an welcher Flash-Adresse der Bootloaderbereich beginnt: Er beginnt in der 96 &#039;&#039;Page&#039;&#039; (128 - 32) an &#039;&#039;Word&#039;&#039;-Adresse 0xC00, also Byteadresse 0xC00 * 2 = 0x1800. Dies ist die exakte Startadresse unseres Bootloaderbereiches.&lt;br /&gt;
&lt;br /&gt;
Weiter oben wurde die Frage gestellt, woher der AVR weiß, an welcher Stelle (entweder 0x0000 oder in unserem Fall 0x1800) er nach dem Reset starten soll. Um dem AVR dies mitzuteilen, ist eine weitere Fuse nötig - die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse. Eine weitere Tabelle aus dem Atmega88-Datenblatt gibt Auskunft über diese Fuse.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Reset and Interrupt Vectors Placement in Atmega88, Tabelle 11-3 im Atmega88-Datenblatt S.58&lt;br /&gt;
|- &lt;br /&gt;
!  BOOTRST|| IVSEL || Reset Adress || Interrupt Vectors Start Adress&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || 0x000 || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || 0x000 || Boot Reset Address + 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Boot Reset Address || 0x001 &lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Boot Reset Address || Boot Reset Address + 0x001 &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Mit der &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse wird festgelegt, dass der AVR nach dem Reset an die Startadresse der Bootloader Sektion im Flash springt. Auf das &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bit (keine Fuse) möchte ich erst an späterer Stelle - wenn es um Interrupts geht - zurückkommen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Noch ein wichtiger Hinweis für die Werte der Fuses im Datenblatt:&#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;Der Wert &amp;quot;0&amp;quot; bedeutet, dass die Fuse programmiert ist, es entspicht dem Häkchen im AVR Studio!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;Hallo Welt&amp;quot; - Bootloader =&lt;br /&gt;
&lt;br /&gt;
Wie eingangs erwähnt, werden wir für die Erstellung des Codes die freie IDE von Atmel - das AVRStudio - benutzen. Ergänzt wird es durch C-Compiler und Tools des WinAVR Projektes. Des weiteren wird zur seriellen Kommunikation das Terminal Programm PuTTY benötigt und sollte installiert sein. Die Hardware ist aufgebaut und via AVRISPmkII-Programmer an den PC angeschlossen - nun kann es losgehen!&lt;br /&gt;
&lt;br /&gt;
Zu Beginn erstellen wir uns, wie üblich, ein neues AVR Studio-Projekt. Danach arbeiten wir folgende Schritte ab:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Konfiguration der Projekteinstellungen ==&lt;br /&gt;
[[Bild:Bootloader-Projekt.png|200px|thumb|Erstellen des Projekts]]&lt;br /&gt;
[[Bild:Bootloader-Config-General.PNG|200px|thumb|Generelle Optionen - setzten der Taktfrequenz]]&lt;br /&gt;
[[Bild:Bootloader-Config-Linker.png|200px|thumb|Linker Option eingeben]]&lt;br /&gt;
&lt;br /&gt;
Als erstes öffnen wir die Projekteinstellungen (Menü &#039;&#039;Project/Configuration Options&#039;&#039;) und tragen die richtige Taktfrequenz ein. Im Beispiel nutzen wir den internen Oszillator mit 8 Mhz. Danach gehen wir zum Reiter &#039;&#039;&#039;Custom Options&#039;&#039;&#039;. Dort klicken wir auf &#039;&#039;&#039;[Linker Options]&#039;&#039;&#039; und geben dann im Textfeld daneben &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039; ein. Danach drücken wir auf &#039;&#039;&#039;Add&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Was bewirkt dieser Linker-Parameter? Dafür muß wieder etwas weiter ausgeholt werden. Nach dem Kompilieren der Programmquellen linkt der Linker den Programmcode an bestimmte Stellen in den drei verschiedenen Speichern Flash, EEPROM und SRAM des AVR. In der vom Compiler verwendeten [http://www.nongnu.org/avr-libc/user-manual/mem__sections.html AVR Libc] ist der Speicher in verschiedene Sektionen aufgeteilt. Dem Linker muss mitgeteilt werden, in welche Speicher er den Programmcode linken soll. Die Lokalisierung des Speicher sind die Sektionen. Die Sektion &#039;&#039;.text&#039;&#039; ist dem ausführbaren Programmcode - also den Befehlen - vorbeihalten und liegt im Flash des AVR, des weiteren gibt es auch noch die Sektionen &#039;&#039;.data&#039;&#039; und &#039;&#039;.bss&#039;&#039; für die statischen und dynamischen Variablen im SRAM und eine Sektion &#039;&#039;.eeprom&#039;&#039; für den EEPROM und noch ein paar spezielle (Flash-)Sektionen.&lt;br /&gt;
&lt;br /&gt;
Nun gibt verschiedene Methoden, dem Linker mitzuteilen, dass man den Programmcode an die Stelle des Bootloaderbereich haben möchte. Eine sehr einfache Methode ist &#039;&#039;&#039;die Verschiebung der Sektion &#039;&#039;&#039; &#039;&#039;&#039;&#039;&#039;.text&#039;&#039;&#039;&#039;&#039;, welche normalerweise ab Adresse 0x0000 beginnt. Eben dies geschieht mit dem Linker Parameter &#039;&#039;&#039;-Ttext=0x1800&#039;&#039;&#039;. Die Adresse des Beginns der &#039;&#039;.text&#039;&#039; Sektion wird auf die (Byte-)Adresse 0x1800 gesetzt.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library von Peter Fleury ==&lt;br /&gt;
&lt;br /&gt;
Wie bereits erwähnt, wird für die serielle Kommunikation auf der AVR-Seite die UART-Library von [http://www.jump.to/fleury Peter Fleury] verwendet. Nach dem Download werden die uart.c und uart.h in das Projekt eingebunden (für die uart.c im AVR Studio rechte Maustaste auf &#039;&#039;Source Files&#039;&#039; und dann &#039;&#039;Add Existing Source File(s)...&#039;&#039; und für die uart.h die rechte Maustaste auf &#039;&#039;Header Files&#039;&#039; und dann &#039;&#039;Add Existing Header File(s)...&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-Config-Linker-ok.PNG|200px|thumb|Linker Option nach Add]]&lt;br /&gt;
[[Bild:Bootloader-AVRStudio.PNG|200px|thumb|Kompilieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Nun soll eine kleine Applikation geschrieben werden. Keine Angst, unser erstes Ziel ist es, eine kleine Anwendung in dem Bootloaderbereich zu positionieren, welche serielle Ein-und Ausgaben handelt. Die eigentliche Bootloaderfunktionalität kommt später dazu. Also schreiben wir die Datei &#039;&#039;main.c&#039;&#039; wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    unsigned int 	c=0;               /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    unsigned char	temp,              /* Variable */&lt;br /&gt;
                        flag=1,            /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
			p_mode=0;	   /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
    void (*start)( void ) = 0x0000;        /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
            switch((unsigned char)c)&lt;br /&gt;
            {&lt;br /&gt;
                 case &#039;q&#039;: &lt;br /&gt;
		     flag=0;&lt;br /&gt;
                     uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
                  default:&lt;br /&gt;
                     uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                     uart_putc((unsigned char)c);&lt;br /&gt;
                     uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                     break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(flag);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Springe zur Adresse 0x0000!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Rücksprung zur Adresse 0x0000 */&lt;br /&gt;
    start(); &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Beginnen wir mit den Defines: Die Baudrate erklärt sich von selbst. Die Defines XON und XOFF werden später, wenn die Bootloader-Funktionalität dazukommt, zur Flusssteuerung gebraucht. Wir werden also die XON/XOFF-Flussteuerung nutzen (merken für die Einstellung von PuTTY). &lt;br /&gt;
&lt;br /&gt;
Bei den Variablen ist nur eine interessant: Der Funktionspointer &amp;lt;pre&amp;gt;void (*start)( void ) = 0x0000;&amp;lt;/pre&amp;gt; ist ein einfacher Trick, um mit dem Programmcounter (PC) zur Adresse 0x0000 zu springen. Wir definieren einfach eine (fiktive) Funktion an der Stelle 0x0000. Beim Aufruf der Funktion mit &amp;lt;pre&amp;gt;start();&amp;lt;/pre&amp;gt; springt der Programmcounter und damit das Programm an Adresse 0x0000 und das Anwendungsprogramm - wenn es eins gibt - kann starten. &lt;br /&gt;
&lt;br /&gt;
Nun folgt ein sehr wichtiger Teil, auf den ich noch eingehen muss - die Interrupt-Vektoren. Interrupt-Vektoren sind Einsprungpunkte der Interrupts, welche normalerweise fest ab Adresse 0x0001 im Flash liegen. Wird ein Interrupt ausgelöst, springt der AVR automatisch zu der festen Flash-Adresse. Vor dort aus - wenn eine ISR programmiert ist - springt der Controller zur ISR (&#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine). Nun haben wir folgendes Problem: Wenn wir den Bootloadercode ab der Adresse 0x1800 ausführen, nützt es uns gar nichts, wenn der AVR nach Auslösen eines Interrupts an die Stelle 0x0001 + X springt, da dieser Speicherbereich ja im Zweifelsfalle sogar von uns überschrieben wird. Unser Code soll nur ab Adresse 0x1800 stehen! Wir müssen also die Sprungtabelle &amp;quot;verbiegen&amp;quot;, d.h. den AVR veranlassen, bei Auslösung eines Interrupts an Adresse 0x1801 + X zu springen und dann zur ISR. Das Verbiegen der Sprungtabelle passiert mit dem Setzen des &#039;&#039;&#039;IVSEL&#039;&#039;&#039;-Bits im &#039;&#039;&#039;MCUCR&#039;&#039;&#039;, also so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
temp = MCUCR;&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;IVCE&#039;&#039;&#039;-Bit wird nur benötigt, um dem Mikrocontroller zu sagen, dass wir als nächstes den Parameter &#039;&#039;&#039;IVSEL&#039;&#039;&#039; setzen wollen, das Bit wird nachher vom Controller wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
In der Hauptschleife wird lediglich gepollt, ob ein neues Zeichen von der Konsole kommt. Nach dem Empfang eines Zeichens wird es ausgewertet (switch). Nach dem Drücken von &#039;&#039;&amp;quot;q&amp;quot;&#039;&#039; verlässt der Bootloader die Hauptschleife, setzt die Interrupt-Vektoren wieder zurück und startet die Hauptanwendung - wenn eine da ist.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren sagt uns der Linker, dass 754 Byte Programmspeicher und 265 Byte Datenspeicher verbraucht wurde. 754 Byte ist weit unter den 2048 Byte, welche uns ab der Adresse 0x1800 zur Verfügung stehen, wir haben also alles richtig gemacht.&lt;br /&gt;
&lt;br /&gt;
Nun kontrollieren wir noch schnell, ob das Programm an der richtigen Stelle im Flash steht. Mit dem Hex-File (Bootloader.hex) wird auch ein List-File (Bootloader.lss) erzeugt. Im List-File findet sich das disassemblierte Programm und die Speicherzuordnungen. Hier ein Auszug der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Bootloader.elf:     file format elf32-avr&lt;br /&gt;
&lt;br /&gt;
Sections:&lt;br /&gt;
Idx Name          Size      VMA       LMA       File off  Algn&lt;br /&gt;
  0 .data         00000080  00800100  00001a68  000002fc  2**0&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, DATA&lt;br /&gt;
  1 .text         00000268  00001800  00001800  00000094  2**1&lt;br /&gt;
                  CONTENTS, ALLOC, LOAD, READONLY, CODE&lt;br /&gt;
  2 .bss          00000085  00800180  00800180  0000037c  2**0&lt;br /&gt;
                  ALLOC&lt;br /&gt;
  3 .debug_aranges 00000040  00000000  00000000  0000037c  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  4 .debug_pubnames 00000095  00000000  00000000  000003bc  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  5 .debug_info   00000459  00000000  00000000  00000451  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  6 .debug_abbrev 00000238  00000000  00000000  000008aa  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  7 .debug_line   000003eb  00000000  00000000  00000ae2  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  8 .debug_frame  000000a0  00000000  00000000  00000ed0  2**2&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
  9 .debug_str    000001cf  00000000  00000000  00000f70  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 10 .debug_loc    0000024a  00000000  00000000  0000113f  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
 11 .debug_ranges 00000048  00000000  00000000  00001389  2**0&lt;br /&gt;
                  CONTENTS, READONLY, DEBUGGING&lt;br /&gt;
&lt;br /&gt;
Disassembly of section .text:&lt;br /&gt;
&lt;br /&gt;
00001800 &amp;lt;__vectors&amp;gt;:&lt;br /&gt;
    1800:	19 c0       	rjmp	.+50     	; 0x1834 &amp;lt;__ctors_end&amp;gt;&lt;br /&gt;
    1802:	33 c0       	rjmp	.+102    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1804:	32 c0       	rjmp	.+100    	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1806:	31 c0       	rjmp	.+98     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    1808:	30 c0       	rjmp	.+96     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180a:	2f c0       	rjmp	.+94     	; 0x186a &amp;lt;__bad_interrupt&amp;gt;&lt;br /&gt;
    180c:	2e c0       	rjmp	.+92     	; 0x186a &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(viele Zeilen)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
0000186c &amp;lt;main&amp;gt;:&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
 &lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    186c:	cf 93       	push	r28&lt;br /&gt;
    186e:	df 93       	push	r29&lt;br /&gt;
    unsigned char	temp,            /* Variable */&lt;br /&gt;
                        flag=1;          /* Flag zum steuern der Endlosschleife */&lt;br /&gt;
    void (*start)( void ) = 0x0000;    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    1870:	85 b7       	in	r24, 0x35	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    1872:	98 2f       	mov	r25, r24&lt;br /&gt;
    1874:	91 60       	ori	r25, 0x01	; 1&lt;br /&gt;
    1876:	95 bf       	out	0x35, r25	; 53&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
    1878:	82 60       	ori	r24, 0x02	; 2&lt;br /&gt;
    187a:	85 bf       	out	0x35, r24	; 53&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
(noch mehr Zeilen)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir erkennen, dass die Sektion &#039;&#039;.text&#039;&#039; ab der Adresse (VMA) 0x1800 beginnt. Weitere sehen wir im Disassembly der Sektion &#039;&#039;.text&#039;&#039;, dass unser Programm mit der Interrupt-Einsprungstabelle ab Adresse 0x1800 beginnt. Unsere &#039;&#039;main()&#039;&#039; beginnt ab Adresse 0x186C. Super. Das hat geklappt. Aber nun schnell zu Schritt 4 - dem Flashen und Ausprobieren des Programms...&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Bootloader-AVRStudio-Fuses.png|200px|thumb|Setzen der Fuses]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
[[Bild:PuTTY-Serconfig-xon.png|200px|thumb|Serielle Konfiguration von PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Start des AVRISPmkII-In-System-Programmers aus dem AVRStudio werden zunächst die Einstellungen geprüft. Die Signatur des AVRs muss stimmen und die ISP-Frequenz. Im Reiter Program muss unter &#039;&#039;Flash&#039;&#039; die richtige Datei angegeben sein (&#039;&#039;Bootloader.hex&#039;&#039;). Danach können wir uns an das setzen der Fuses machen. &#039;&#039;&#039;CKDIV8&#039;&#039;&#039; sollte ausgeschalten werden, der interne Takt von 8 Mhz sollte genutzt werden (&#039;&#039;&#039;SUT_CKSEL&#039;&#039;&#039;) und &#039;&#039;BOOTSZ&#039;&#039; auf 1024 words gestellt werden. Zusätzlich muß die &#039;&#039;&#039;BOOTRST&#039;&#039;&#039;-Fuse gesetzt werden, damit der Bootloader an der richtigen Adresse anfängt. Für alle, die einen anderen Programmer benutzen (z.B. avrdude), hier die exakten Werte der Fuses:&lt;br /&gt;
* Low Fuse: &#039;&#039;&#039;0xE2&#039;&#039;&#039;&lt;br /&gt;
* High Fuse: &#039;&#039;&#039;0xD2&#039;&#039;&#039;&lt;br /&gt;
* Extended Fuse: &#039;&#039;&#039;0xF8&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jetzt kann PuTTY gestartet und konfiguriert werden. Der &#039;&#039;Connection type&#039;&#039; muss auf &#039;&#039;&#039;Serial&#039;&#039;&#039; gestellt werden. Die Baudrate beträgt 9600 Baud. Unter &#039;&#039;Connection/Serial&#039;&#039; muss der &#039;&#039;Flow control&#039;&#039; auf &#039;&#039;&#039;XON/XOFF&#039;&#039;&#039; gestellt werden. Nach dem Konfigurieren kann die Konsole mit &#039;&#039;Open&#039;&#039; geöffnet werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt kann wieder in das AVR Studio gewechselt werden. Mit einem beherztem Druck auf &#039;&#039;Program&#039;&#039; wird das Flash im ATmega88 programmiert. Nach dem Wechsel auf die Konsole erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-start.png|600px|Bootloader in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von ein paar Tasten erscheint folgendes:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-taste.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Nach dem Drücken von &#039;&#039;&#039;q&#039;&#039;&#039; erscheint folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Bootloader-restart.png|600px|Bootloader nach Tastendruck in PuTTY]]&lt;br /&gt;
&lt;br /&gt;
Der Bootloader versucht, zur Adresse 0x0000 zu springen, wo er allerdings keinen Programmcode findet. Wie auch? Wir haben ja den ganzen Flash des AVR gerade gelöscht und mit dem Bootloader gefüllt. Nun muss man wissen, dass in einem gelöschten Flash &#039;&#039;0xFF&#039;&#039; in jeder Speicherzelle steht. &#039;&#039;0xFF&#039;&#039; ist für den AVR kein gültiger Opcode. Der Programmzähler zählt nur um eins nach oben. Damit hopst er sozusagen durch den gesamten Flash bis er wieder beim Bootloader landet.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;[http://de.wikipedia.org/wiki/Heureka Heureka]&#039;&#039;&#039;&#039;&#039; wir haben es geschafft! Ein Programm wird im Bootloaderbereich des Flashs ausgeführt! Es &#039;&#039;bootet&#039;&#039; zwar schon schön, aber es &#039;&#039;loadet&#039;&#039; noch nichts in den Flash. Aber die halbe Miete haben wir damit schon. Nun schreiben wir erst einmal eine kleine Anwendung, welche wir nach der Erweiterung unseres Bootloaders in den Flash-Speicher laden.&lt;br /&gt;
&lt;br /&gt;
= Die Test-Anwendung =&lt;br /&gt;
&lt;br /&gt;
[[Bild:Anwendung-AVRStudio.png|200px|thumb|Erstellen des Projektes]]&lt;br /&gt;
[[Bild:Anwendung-AVRStudio-Code.png|200px|thumb|Sourcecode im AVRStudio]]&lt;br /&gt;
&lt;br /&gt;
Die Kategorie Anwendung möchte ich möglichst kurz halten. Ziel ist es, eine kleine Anwendung zu schreiben, welche dann mit dem (echten) Bootloader ins Flash gespeichert wird. &lt;br /&gt;
&lt;br /&gt;
== Schritt 1 - Erstellen des Projektes ==&lt;br /&gt;
&lt;br /&gt;
Nach dem Erstellen eines neuen Projektes muss in den Projekt-Einstellungen des AVR Studios nur die Taktfrequenz eingetragen werden. Die Linker-Optionen werden nicht verändert, also bleibt wie es ist.&lt;br /&gt;
&lt;br /&gt;
== Schritt 2 - Einbinden der UART Library ==&lt;br /&gt;
&lt;br /&gt;
Dieser Schritt kann vom Bootloader übernommen werden. Es wird wieder die UART-Bibliothek von Peter Fleury verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wir starten also ein neues AVR Studio und legen ein neues Projekt an, konfigurieren die Taktfrequenz (8 Mhz) und laden die &#039;&#039;uart.c&#039;&#039; und &#039;&#039;uart.h&#039;&#039; dazu. Nun schreiben wir in die &#039;&#039;main.c&#039;&#039; folgende Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define UART_BAUD_RATE	9600&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
   unsigned int 	c;&lt;br /&gt;
   void (*bootloader)( void ) = 0x1800;&lt;br /&gt;
&lt;br /&gt;
   uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
   sei();&lt;br /&gt;
    &lt;br /&gt;
   uart_puts_P(&amp;quot;\n\rHier ist das Anwendungsprogramm...&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
   for(;;)&lt;br /&gt;
   {&lt;br /&gt;
       c = uart_getc();&lt;br /&gt;
       if(!(c &amp;amp; UART_NO_DATA))&lt;br /&gt;
       {&lt;br /&gt;
	   switch( (unsigned char)c)&lt;br /&gt;
	   {&lt;br /&gt;
	       case &#039;b&#039;:&lt;br /&gt;
		   uart_puts(&amp;quot;\n\rSpringe zum Bootloader...&amp;quot;);&lt;br /&gt;
		   _delay_ms(1000);&lt;br /&gt;
		    bootloader();&lt;br /&gt;
		    break;&lt;br /&gt;
		default:&lt;br /&gt;
                    uart_puts(&amp;quot;\n\rDu hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
		    uart_putc((unsigned char)c);&lt;br /&gt;
		    break;&lt;br /&gt;
	    }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Viel interessantes ist nicht an diesem Code. Es gibt wie immer die berühmte Endlosschleife. Wir definieren wieder einen fiktiven Funktionspointer&amp;lt;pre&amp;gt;void (*bootloader)( void ) = 0x1800;&amp;lt;/pre&amp;gt; Nach drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; soll das Programm wieder zum Bootloader springen.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren des Programms sehen wir, dass der Programmspeicher mit 686 Byte belegt ist, der Datenspeicher mit 201 Bytes. &lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Ausprobieren der Anwendung ==&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann die Anwendung auf den AVR flashen und ausprobieren. Die Funktion sollte sich von selbst erschließen. Nun wollen wir uns der &lt;br /&gt;
Erweiterung des Bootloader widmen.&lt;br /&gt;
&lt;br /&gt;
= Der &amp;quot;echte&amp;quot; Bootloader =&lt;br /&gt;
[[Bild:Real-Bootloader-AVRStudio.png|200px|thumb|Programmieren des Bootloaders]]&lt;br /&gt;
&lt;br /&gt;
Zum Erstellen des Bootloaders wird wieder Schrittweise vorgegangen. Folgende Schritte sind zu befolgen:&lt;br /&gt;
&lt;br /&gt;
== Schritt 1 und 2 - siehe &amp;quot;Hallo Welt&amp;quot; Bootloader ==&lt;br /&gt;
Schritt 1 und 2 können vom &amp;quot;Hallo Welt&amp;quot; Bootloader übernommen werden.&lt;br /&gt;
&lt;br /&gt;
== Schritt 3 - Programmieren des Bootloaders ==&lt;br /&gt;
&lt;br /&gt;
Nun soll der Bootloader erweitert werden. Nach dem Kompilieren des Anwendungsprogramms erhalten wir eine Datei &#039;&#039;Anwendung.hex&#039;&#039; im [http://de.wikipedia.org/wiki/Intel_HEX Intel-HEX-Format]. Da wir im Bootloader diese Daten auswerten müssen, wollen wir uns kurz mit dem Format beschäftigen. Das Intel-HEX-Format ist geschaffen worden, um Binärdaten als ASCII-Daten zu übertragen. Jedes Byte ist in Form von zwei ASCII-Zeichen gespeichert, d.h. aus der Zahl &#039;&#039;0x4A&#039;&#039; wird die ASCII-Zeichenfolge &#039;&#039;&amp;quot;4A&amp;quot;&#039;&#039;. Das bedeutet aber auch, dass aus den Binärdaten die doppelte Anzahl von Zeichen wird, welche übertragen werden müssen, hinzu kommen noch Steuerzeichen und Zusatzinformationen. Jede Zeile in der Intel-HEX-Datei folgt einem bestimmten Schema, in dem u.a. die Anzahl der Bytes, die Zieladresse und Checksumme stehen.&lt;br /&gt;
&lt;br /&gt;
Für weiterführende Erklärungen zum Thema HEX-Datei-Format empfehle ich folgende Lektüre:&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Intel_HEX Wikipedia Artikel über HEX-Files]&lt;br /&gt;
* [http://www.rn-wissen.de/index.php/HEX-Datei RN-Wissen-Artikel über HEX-Files]&lt;br /&gt;
* [http://www.schulz-koengen.de/biblio/intelhex.htm Artikel von Wolfgang R.Schulz]&lt;br /&gt;
&lt;br /&gt;
Unser Bootloader muss in der Lage sein, dieses Format zu interpretieren. Wir müssen also einen Parser schreiben. &#039;&#039;Oje&#039;&#039; werden manche denken, das ist ja wieder ein Thema für sich. Das stimmt prinzipiell auch. Allerdings kommt uns hier das einfache Format der Intel-Hex-Datei zugute, welches den Aufwand in Grenzen hält.&lt;br /&gt;
&lt;br /&gt;
Also erstes brauchen wir also Funktionen, um die ASCII-Zeichenfolgen wieder in Binärdaten umzuwandeln. Normalerweise könnte man dafür die C-Funktion [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#gaf8ce3b8dae3d45c34c3b172de503f7b3 strtol] aus der stdlib.h nehmen. Allerdings würde das Benutzen dieses Befehls das linken der Standardblibliothek nach sich ziehen und damit den Code unnötig aufblähen. Daher werden werden wir uns eine einfache eigene Methode schreiben, um die Zeichenfolgen umzuwandlen. In der HEX-Datei kommen 2 Byte und 4 Byte Hex-Zahlen im ASCII-Format vor. Wir brauchen also eine Funktion, welche die ASCII-Zeichenfolgen in Zahlen umwandelt, hier ist sie:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static uint16_t hex2num(const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
 &lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Wir benutzen hier einen sehr einfachen Ansatz, um die Zahlen zu generieren. Die Funktionen wandeln die ASCII-Zeichen entsprechend ihrer Wertigkeit in Zahlen um. Soll z.B. das ASCII-Zeichen &#039;1&#039; umgewandelt werden, wird vom ASCII-Code &#039;1&#039;, also dezimal 49, 48=&#039;0&#039; abgezogen: 49 - 48 = 1, somit haben wir ein ASCII-Zeichen in eine Zahl umgewandelt. Wen das ASCII-Zeichen &#039;C&#039; ist (Dezimal: 67), werden &#039;A&#039;=65 und 10 hinzugezählt, um 12 zu erhalten, den Wert der hex-Ziffer C. Näher möchte an dieser Stelle nicht darauf eingehen, wir wollen schnell weiter zum beschreiben des Flashs kommen.&lt;br /&gt;
&lt;br /&gt;
Um in den Flash zu schreiben, werden wir Makros aus der &#039;&#039;[http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html boot.h]&#039;&#039; der avr-libc verwenden. Hier findet man alle Werkzeuge, die wir brauchen. Dabei sollte vor allen das &#039;&#039;API Usage Example&#039;&#039; in der [http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html Online Doku] näher betrachtet werden. Diese Funktion soll weitestgehend übernommen werden, da sie alle nötigen Funktionen beinhaltet. Hier ist die Funktion:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void boot_program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts.*/&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Als erstes fällt auf, dass man der Funktion die &#039;&#039;Page&#039;&#039;-Adresse übergibt. Es wird also immer seitenweise geschrieben. Dies ist eine Spezialität des Flash-Speichers. Es &#039;&#039;&#039;muss&#039;&#039;&#039; immer die gesamte Seite geschrieben werden, dafür gibt es einen &#039;&#039;Page&#039;&#039;-Puffer, welcher die Daten enthält, welche mit der nächsten Schreiboperation in die entsprechende Page geschrieben werden. Dabei werden die Daten Wortweise in den &#039;&#039;Page&#039;&#039;-Puffer geschrieben. Die wesentlichen Funktionen der Routine sind &#039;&#039;boot_page_erase(page)&#039;&#039;, &#039;&#039;boot_page_fill(page + i, w)&#039;&#039; und &#039;&#039;boot_page_write(page)&#039;&#039;. Nicht zu vergessen auch &#039;&#039;boot_spm_busy_wait()&#039;&#039;. Die Bedeutung der Funktionen (naja es sind eher Makros) findet man in der Dokumentation der AVR Libc. Im wesentlichen läuft das Schreiben einer &#039;&#039;Page&#039;&#039; so ab:&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; löschen&lt;br /&gt;
* &#039;&#039;Page&#039;&#039;-Puffer befüllen (aus der Variable &#039;&#039;buf&#039;&#039;)&lt;br /&gt;
* &#039;&#039;Page&#039;&#039; schreiben&lt;br /&gt;
So einfach, so gut. Für den Bootloader bedeutet das, dass er die Daten sammeln muß, bis er genügend Daten für eine &#039;&#039;Page&#039;&#039; hat. Dann wird eine &#039;&#039;Page&#039;&#039; geschrieben und der Spaß fängt von vorn an.&lt;br /&gt;
&lt;br /&gt;
Mit diesen beiden Funktionen sind wir nun in der Lage, den Parser zu schreiben. Die &#039;&#039;main.c&#039;&#039; sieht folgt aus:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;quot;uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define BOOT_UART_BAUD_RATE     9600     /* Baudrate */&lt;br /&gt;
#define XON                     17       /* XON Zeichen */&lt;br /&gt;
#define XOFF                    19       /* XOFF Zeichen */&lt;br /&gt;
#define START_SIGN              &#039;:&#039;      /* Hex-Datei Zeilenstartzeichen */&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Bootloader-Programms */&lt;br /&gt;
#define BOOT_STATE_EXIT	        0        &lt;br /&gt;
#define BOOT_STATE_PARSER       1&lt;br /&gt;
&lt;br /&gt;
/* Zustände des Hex-File-Parsers */&lt;br /&gt;
#define PARSER_STATE_START      0&lt;br /&gt;
#define PARSER_STATE_SIZE       1&lt;br /&gt;
#define PARSER_STATE_ADDRESS    2&lt;br /&gt;
#define PARSER_STATE_TYPE       3&lt;br /&gt;
#define PARSER_STATE_DATA       4&lt;br /&gt;
#define PARSER_STATE_CHECKSUM   5&lt;br /&gt;
#define PARSER_STATE_ERROR      6&lt;br /&gt;
&lt;br /&gt;
void program_page (uint32_t page, uint8_t *buf)&lt;br /&gt;
{&lt;br /&gt;
    uint16_t i;&lt;br /&gt;
    uint8_t sreg;&lt;br /&gt;
&lt;br /&gt;
    /* Disable interrupts */&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
&lt;br /&gt;
    eeprom_busy_wait ();&lt;br /&gt;
&lt;br /&gt;
    boot_page_erase (page);&lt;br /&gt;
    boot_spm_busy_wait ();      /* Wait until the memory is erased. */&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;SPM_PAGESIZE; i+=2)&lt;br /&gt;
    {&lt;br /&gt;
        /* Set up little-endian word. */&lt;br /&gt;
        uint16_t w = *buf++;&lt;br /&gt;
        w += (*buf++) &amp;lt;&amp;lt; 8;&lt;br /&gt;
    &lt;br /&gt;
        boot_page_fill (page + i, w);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    boot_page_write (page);     /* Store buffer in flash page.		*/&lt;br /&gt;
    boot_spm_busy_wait();       /* Wait until the memory is written.*/&lt;br /&gt;
&lt;br /&gt;
    /* Reenable RWW-section again. We need this if we want to jump back */&lt;br /&gt;
    /* to the application after bootloading. */&lt;br /&gt;
    boot_rww_enable ();&lt;br /&gt;
&lt;br /&gt;
    /* Re-enable interrupts (if they were ever enabled). */&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t  i;&lt;br /&gt;
    uint16_t val = 0;&lt;br /&gt;
&lt;br /&gt;
    for (i=0; i&amp;lt;num; i++)&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t c = ascii[i];&lt;br /&gt;
        &lt;br /&gt;
        /* Hex-Ziffer auf ihren Wert abbilden */&lt;br /&gt;
        if (c &amp;gt;= &#039;0&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;9&#039;)            c -= &#039;0&#039;;  &lt;br /&gt;
        else if (c &amp;gt;= &#039;A&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;F&#039;)       c -= &#039;A&#039; - 10;&lt;br /&gt;
        else if (c &amp;gt;= &#039;a&#039; &amp;amp;&amp;amp; c &amp;lt;= &#039;f&#039;)       c -= &#039;a&#039; - 10;&lt;br /&gt;
            &lt;br /&gt;
        val = 16 * val + c;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return val;  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
                    /* Empfangenes Zeichen + Statuscode */&lt;br /&gt;
    uint16_t        c = 0, &lt;br /&gt;
                    /* Intel-HEX Zieladresse */&lt;br /&gt;
           	    hex_addr = 0,&lt;br /&gt;
                    /* Zu schreibende Flash-Page */&lt;br /&gt;
                    flash_page = 0,                    &lt;br /&gt;
                    /* Intel-HEX Checksumme zum Überprüfen des Daten */&lt;br /&gt;
                    hex_check = 0,&lt;br /&gt;
                    /* Positions zum Schreiben in der Datenpuffer */&lt;br /&gt;
                    flash_cnt = 0;&lt;br /&gt;
                    /* temporäre Variable */&lt;br /&gt;
    uint8_t         temp,&lt;br /&gt;
                    /* Flag zum steuern des Programmiermodus */&lt;br /&gt;
                    boot_state = BOOT_STATE_EXIT,&lt;br /&gt;
                    /* Empfangszustandssteuerung */&lt;br /&gt;
                    parser_state = PARSER_STATE_START,&lt;br /&gt;
                    /* Flag zum ermitteln einer neuen Flash-Page */&lt;br /&gt;
                    flash_page_flag = 1,&lt;br /&gt;
                    /* Datenpuffer für die Hexdaten*/&lt;br /&gt;
                    flash_data[SPM_PAGESIZE], &lt;br /&gt;
                    /* Position zum Schreiben in den HEX-Puffer */&lt;br /&gt;
                    hex_cnt = 0, &lt;br /&gt;
                    /* Puffer für die Umwandlung der ASCII in Binärdaten */&lt;br /&gt;
                    hex_buffer[5], &lt;br /&gt;
                    /* Intel-HEX Datenlänge */&lt;br /&gt;
                    hex_size = 0,&lt;br /&gt;
                    /* Zähler für die empfangenen HEX-Daten einer Zeile */&lt;br /&gt;
                    hex_data_cnt = 0, &lt;br /&gt;
                    /* Intel-HEX Recordtype */&lt;br /&gt;
                    hex_type = 0, &lt;br /&gt;
                    /* empfangene HEX-Checksumme */&lt;br /&gt;
                    hex_checksum=0;&lt;br /&gt;
                    /* Funktionspointer auf 0x0000 */&lt;br /&gt;
    void            (*start)( void ) = 0x0000; &lt;br /&gt;
 &lt;br /&gt;
    /* Füllen der Puffer mit definierten Werten */&lt;br /&gt;
    memset(hex_buffer, 0x00, sizeof(hex_buffer));&lt;br /&gt;
    memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren verbiegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Einstellen der Baudrate und aktivieren der Interrupts */&lt;br /&gt;
    uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) ); &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Hallo hier ist der echte Bootloader\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(2000);&lt;br /&gt;
 &lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        c = uart_getc();&lt;br /&gt;
        if( !(c &amp;amp; UART_NO_DATA) )&lt;br /&gt;
        {&lt;br /&gt;
             /* Programmzustand: Parser */&lt;br /&gt;
             if(boot_state == BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                  switch(parser_state)&lt;br /&gt;
                  {&lt;br /&gt;
                      /* Warte auf Zeilen-Startzeichen */&lt;br /&gt;
                      case PARSER_STATE_START:			&lt;br /&gt;
                          if((uint8_t)c == START_SIGN) &lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_SIZE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_check = 0;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Datengröße */&lt;br /&gt;
                      case PARSER_STATE_SIZE:	&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_ADDRESS;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_size = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_size;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zieladresse */&lt;br /&gt;
                      case PARSER_STATE_ADDRESS:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 4)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              parser_state = PARSER_STATE_TYPE;&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              hex_addr = hex2num(hex_buffer, 4);&lt;br /&gt;
                              hex_check += (uint8_t) hex_addr;&lt;br /&gt;
                              hex_check += (uint8_t) (hex_addr &amp;gt;&amp;gt; 8);&lt;br /&gt;
                              if(flash_page_flag) &lt;br /&gt;
                              {&lt;br /&gt;
                                  flash_page = hex_addr - hex_addr % SPM_PAGESIZE;&lt;br /&gt;
                                  flash_page_flag = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Zeilentyp */&lt;br /&gt;
                      case PARSER_STATE_TYPE:	&lt;br /&gt;
                           hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                           if(hex_cnt == 2)&lt;br /&gt;
                           {&lt;br /&gt;
                               uart_putc(XOFF);&lt;br /&gt;
                               hex_cnt = 0;&lt;br /&gt;
                               hex_data_cnt = 0;&lt;br /&gt;
                               hex_type = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                               hex_check += hex_type;&lt;br /&gt;
                               switch(hex_type)&lt;br /&gt;
                               {&lt;br /&gt;
                                   case 0: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                                   case 1: parser_state = PARSER_STATE_CHECKSUM; break;&lt;br /&gt;
                                   default: parser_state = PARSER_STATE_DATA; break;&lt;br /&gt;
                               }&lt;br /&gt;
                               uart_putc(XON);&lt;br /&gt;
                           }&lt;br /&gt;
                           break;&lt;br /&gt;
                      /* Parse Flash-Daten */&lt;br /&gt;
                      case PARSER_STATE_DATA:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              uart_putc(&#039;.&#039;);&lt;br /&gt;
                              hex_cnt = 0;&lt;br /&gt;
                              flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += flash_data[flash_cnt];&lt;br /&gt;
                              flash_cnt++;&lt;br /&gt;
                              hex_data_cnt++;&lt;br /&gt;
                              if(hex_data_cnt == hex_size)&lt;br /&gt;
                              {&lt;br /&gt;
                                  parser_state = PARSER_STATE_CHECKSUM;&lt;br /&gt;
                                  hex_data_cnt=0;&lt;br /&gt;
                                  hex_cnt = 0;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Puffer voll -&amp;gt; schreibe Page */&lt;br /&gt;
                              if(flash_cnt == SPM_PAGESIZE)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  memset(flash_data, 0xFF, sizeof(flash_data));&lt;br /&gt;
                                  flash_cnt = 0;&lt;br /&gt;
                                  flash_page_flag = 1;&lt;br /&gt;
                              }&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;&lt;br /&gt;
                      /* Parse Checksumme */                             &lt;br /&gt;
                      case PARSER_STATE_CHECKSUM:&lt;br /&gt;
                          hex_buffer[hex_cnt++] = (uint8_t)c;&lt;br /&gt;
                          if(hex_cnt == 2)&lt;br /&gt;
                          {&lt;br /&gt;
                              uart_putc(XOFF);&lt;br /&gt;
                              hex_checksum = (uint8_t)hex2num(hex_buffer, 2);&lt;br /&gt;
                              hex_check += hex_checksum;&lt;br /&gt;
                              hex_check &amp;amp;= 0x00FF;&lt;br /&gt;
                              /* Dateiende -&amp;gt; schreibe Restdaten */ &lt;br /&gt;
                              if(hex_type == 1)&lt;br /&gt;
                              {&lt;br /&gt;
                                  uart_puts(&amp;quot;P\n\r&amp;quot;);&lt;br /&gt;
                                  _delay_ms(100);&lt;br /&gt;
                                  program_page((uint16_t)flash_page, flash_data);&lt;br /&gt;
                                  boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                              }&lt;br /&gt;
                              /* Überprüfe Checksumme -&amp;gt; muss &#039;0&#039; sein */&lt;br /&gt;
                              if(hex_check == 0) parser_state = PARSER_STATE_START;&lt;br /&gt;
                              else parser_state = PARSER_STATE_ERROR;&lt;br /&gt;
                              uart_putc(XON);&lt;br /&gt;
                          }&lt;br /&gt;
                          break;			&lt;br /&gt;
                      /* Parserfehler (falsche Checksumme) */&lt;br /&gt;
                      case PARSER_STATE_ERROR:&lt;br /&gt;
                          uart_putc(&#039;#&#039;);&lt;br /&gt;
                          break;			&lt;br /&gt;
                      default:&lt;br /&gt;
                          break;&lt;br /&gt;
                  }&lt;br /&gt;
             }&lt;br /&gt;
             /* Programmzustand: UART Kommunikation */               &lt;br /&gt;
             else if(boot_state != BOOT_STATE_PARSER)&lt;br /&gt;
             {&lt;br /&gt;
                 switch((uint8_t)c)&lt;br /&gt;
                 {&lt;br /&gt;
                     case &#039;p&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_PARSER;&lt;br /&gt;
                         uart_puts(&amp;quot;Programmiere den Flash!\n\r&amp;quot;);&lt;br /&gt;
                         uart_puts(&amp;quot;Kopiere die Hex-Datei und füge sie&amp;quot;&lt;br /&gt;
                                   &amp;quot; hier ein (rechte Maustaste)\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     case &#039;q&#039;: &lt;br /&gt;
                         boot_state = BOOT_STATE_EXIT;&lt;br /&gt;
                         uart_puts(&amp;quot;Verlasse den Bootloader!\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                     default:&lt;br /&gt;
                         uart_puts(&amp;quot;Du hast folgendes Zeichen gesendet: &amp;quot;);&lt;br /&gt;
                         uart_putc((unsigned char)c);&lt;br /&gt;
                         uart_puts(&amp;quot;\n\r&amp;quot;);&lt;br /&gt;
                         break;&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    while(boot_state!=BOOT_STATE_EXIT);&lt;br /&gt;
 &lt;br /&gt;
    uart_puts(&amp;quot;Reset AVR!\n\r&amp;quot;);&lt;br /&gt;
    _delay_ms(1000);&lt;br /&gt;
 &lt;br /&gt;
    /* Interrupt Vektoren wieder gerade biegen */&lt;br /&gt;
    temp = MCUCR;&lt;br /&gt;
    MCUCR = temp | (1&amp;lt;&amp;lt;IVCE);&lt;br /&gt;
    MCUCR = temp &amp;amp; ~(1&amp;lt;&amp;lt;IVSEL);&lt;br /&gt;
 &lt;br /&gt;
    /* Reset */&lt;br /&gt;
    start();&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Erklärung des Codes&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Bootloader wird als &#039;&#039;Zustandsmaschine&#039;&#039; programmiert. Die Variable &#039;&#039;boot_state&#039;&#039; beinhaltet den aktuelle Programmzustand. Für den Parser gibt es eine eigene Zustandsvariable &#039;&#039;parser_state&#039;&#039;, welche die einzelne Zustände des Parsers hält. Mit jeder HEX-Datei-Zeile läuft der Parser einmal durch alle Zustände. Nach Abarbeiten des aktuellen Zustands und Auswertung der empfangenen Daten wird der nächste Zustand aktiviert usw. Für das Verarbeiten der Datei wird jedes Mal die Kommunikation angehalten und ein &#039;&#039;XOFF&#039;&#039; gesendet. Nach Abarbeitung der Daten gibt der Parser den seriellen Empfang wieder frei (&#039;&#039;XON&#039;&#039;). &#039;&#039;&#039;&#039;&#039;Es ist also wichtig, dass in PuTTY die XON/XOFF-Flußkontrolle aktiviert wird!&#039;&#039;&#039;&#039;&#039; Durch den interruptgesteuerten Empfang mit einem 32 Byte tiefen Empfangspuffer geht kein Byte verloren. Am Ende einer HEX-Datei-Zeile wird die Checksumme ausgewertet. Bei falscher Checksumme springt der Parser in den &#039;&#039;PARSER_STATE_ERROR&#039;&#039;, aus dem er nicht mehr rauskommt. Somit wird kein weiteres Byte in den Flash geschrieben. Zusätzlich könnte man noch implementieren, dass der Flash wieder gelöscht wird, wenn falsche Daten empfangen wurden, damit kein unvollständiges Programm im Flash steht.&lt;br /&gt;
Einschränkend muss noch festgehalten werden, das der Bootloader in dieser Fassung einn Adressraum bis 64K unterstützt (HEX-Zeilentyp 1). Die erweiterten Adressräume (HEX-Zeilentyp 2 bis 5) werden noch nicht unterstützt. Für ATmega-Devices mit &amp;gt; 64K Flash muss der Bootloader noch erweitert werden.&lt;br /&gt;
&lt;br /&gt;
Nach dem Kompilieren beträgt die Größe des Bootloaders 1796 Byte, wir sind also unterhalb der 2048 Byte die wird &amp;quot;verbraten&amp;quot; können. Der Datenspeicher wird mit 283 Byte belastet.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Bootloaders sieht wie folgt aus: Nach dem Reset wartet der Bootloader 2 Sekunden auf Eingaben (&#039;&#039;_delay_ms(2000);&#039;&#039;). Falls keine Eingaben von der Konsole kommen, springt der Bootloader zur Anwendung. Damit ist gewährleiset, dass die Anwendung später automatisch startet, auch wenn wir keine Taste drücken. Wird ein &#039;&#039;p&#039;&#039; gedrückt, springt der Bootloader in den &#039;&#039;BOOT_STATE_PARSER&#039;&#039; und erwartet eine HEX-Datei auf der Konsole. Dies ist der Zeitpunkt, die HEX-Datei der Anwendung mit Copy &amp;amp; Paste in die Konsole zu schreiben. Zum Einfügen von Daten aus dem Zwischenspeicher in die Konsole wird bei PuTTY die rechte Maustaste verwendet.&lt;br /&gt;
&lt;br /&gt;
== Schritt 4 - Flashen und Ausprobieren des Bootloaders ==&lt;br /&gt;
Nach dem Flashen via AVRISPmkII startet der Bootloader in der Konsole. Da noch kein Anwendungsprogramm im Flash liegt, startet der Bootloader nach 2 Sekunden immer wieder neu. Nach Drücken der Taste &#039;&#039;p&#039;&#039; erwartet der Bootlader die HEX-Datei der Anwendung. Nach dem Einfügen der Datei in Konsole erscheint für jedes empfangene Byte ein Punkt (&amp;quot;.&amp;quot;). Das Beschreiben einer Flash-&#039;&#039;Page&#039;&#039; kenzeichnet ein &#039;&#039;P&#039;&#039;. Nach dem erfolgreichen Flashen startet die Anwendung automatisch.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen des Bootloaders via AVRISPmkII erscheint nach dem Reset folgendes Bild:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-start.png|PuTTY: Bootloader nach dem Reset]]&lt;br /&gt;
&lt;br /&gt;
Drückt man die Taste &#039;&#039;&#039;p&#039;&#039;&#039;, springt ist der Bootloader bereit zum Empfang der HEX-Datei:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-wait.png|PuTTY: Warten auf die HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach Kopieren &amp;amp; Einfügen der HEX-Datei der vorher kompilierten Anwendung, hier die Intel-HEX-Datei &amp;quot;Anwendung.hex&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:100000002CC046C045C044C043C042C041C040C0EF&lt;br /&gt;
:100010003FC03EC03DC03CC03BC03AC039C038C004&lt;br /&gt;
:1000200037C036C064C08FC033C032C031C030C0AA&lt;br /&gt;
:100030002FC02EC048696572206973742064617393&lt;br /&gt;
:1000400020416E77656E64756E677370726F67724C&lt;br /&gt;
:10005000616D6D2E2E2E0D0A000011241FBECFEFF4&lt;br /&gt;
:10006000D4E0DEBFCDBF11E0A0E0B1E0EAE6F2E00F&lt;br /&gt;
:1000700002C005900D92A434B107D9F711E0A4E4B1&lt;br /&gt;
:10008000B1E001C01D92A938B107E1F702D0EBC081&lt;br /&gt;
:10009000B7CFEF92FF920F931F93CF93DF9383E33A&lt;br /&gt;
:1000A00090E07BD0789484E390E0D0D088ECE82E88&lt;br /&gt;
:1000B000F12C00E018E18BD0EC0190FDFCCF8236F2&lt;br /&gt;
:1000C00069F480E091E0B6D080E197E2F7013197E2&lt;br /&gt;
:1000D000F1F70197D9F7F8010995EDCF8CE191E09F&lt;br /&gt;
:1000E000A9D08C2F91D081E491E0A4D0E4CF1F92CD&lt;br /&gt;
:1000F0000F920FB60F9211242F938F939F93EF932C&lt;br /&gt;
:10010000FF939091C0002091C600E0918601EF5FBF&lt;br /&gt;
:10011000EF7180918701E81711F482E008C0892F00&lt;br /&gt;
:100120008871E0938601F0E0EC59FE4F20838093C4&lt;br /&gt;
:100130008801FF91EF919F918F912F910F900FBEAA&lt;br /&gt;
:100140000F901F9018951F920F920FB60F921124C7&lt;br /&gt;
:100150008F939F93EF93FF939091840180918501FA&lt;br /&gt;
:10016000981769F0E0918501EF5FEF71E0938501E9&lt;br /&gt;
:10017000F0E0EC5BFE4F80818093C60005C080916B&lt;br /&gt;
:10018000C1008F7D8093C100FF91EF919F918F916E&lt;br /&gt;
:100190000F900FBE0F901F9018959C011092840134&lt;br /&gt;
:1001A00010928501109286011092870197FF04C07A&lt;br /&gt;
:1001B00082E08093C0003F773093C5002093C40055&lt;br /&gt;
:1001C00088E98093C10086E08093C20008959091F1&lt;br /&gt;
:1001D000860180918701981719F420E031E012C060&lt;br /&gt;
:1001E000E0918701EF5FEF71E0938701F0E0EC5958&lt;br /&gt;
:1001F000FE4F308120918801922F80E0AC01430FA7&lt;br /&gt;
:10020000511D9A01C9010895282F909184019F5F83&lt;br /&gt;
:100210009F71809185019817E1F3E92FF0E0EC5B85&lt;br /&gt;
:10022000FE4F2083909384018091C100806280936F&lt;br /&gt;
:10023000C1000895CF93DF93EC0102C02196E4DF63&lt;br /&gt;
:1002400088818823D9F7DF91CF910895CF93DF93E9&lt;br /&gt;
:10025000EC0101C0D9DFFE01219684918823D1F7FA&lt;br /&gt;
:0A026000DF91CF910895F894FFCFCD&lt;br /&gt;
:10026A00537072696E6765207A756D20426F6F747C&lt;br /&gt;
:10027A006C6F616465722E2E2E0D0A00447520681B&lt;br /&gt;
:10028A0061737420666F6C67656E646573205A6566&lt;br /&gt;
:10029A00696368656E20676573656E6465743A2084&lt;br /&gt;
:0402AA00000A0D0039&lt;br /&gt;
:00000001FF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erscheinen folgende Ausgaben in PuTTY:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-prog.png|PuTTY: Flashen der HEX-Datei &amp;quot;Anwendung.hex&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreichem Flashen startet die Anwendung und meldet sich mit der Zeile&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hier ist das Anwendungsprogramm...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jetzt können nach belieben Tasten gedrückt werden, was die Anwendung jedesmal quittiert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-anwend.png|PuTTY: Ausprobieren der Anwendung]]&lt;br /&gt;
&lt;br /&gt;
Nach Drücken der Taste &#039;&#039;&#039;b&#039;&#039;&#039; springt die Anwendung wieder zur Startadresse des Bootloaders, also zur Adresse &#039;&#039;0x1800&#039;&#039; im Flash-Speicher. Nun hat man wieder 2 Sekunden Zeit, um die Taste &#039;&#039;&#039;p&#039;&#039;&#039; zu drücken, sonst startet die Anwendung wieder:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PuTTY-Real-Bootloader-reset.png|PuTTY: Rücksprung zum Bootloader auf Adresse 0x1800]]&lt;br /&gt;
&lt;br /&gt;
Damit ist das Tutorial abgeschlossen.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Viel Spass beim Ausprobieren und Weiterentwickeln!&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Zusammenfassung =&lt;br /&gt;
&lt;br /&gt;
Wer den Artikel bis hier hin nachvollzogen hat, ist jetzt in der Lage einen Bootloader selbst zu schreiben. Dabei kann der Bootloader an jede in Frage kommenden ATmega-Plattform angepaßt werden. Dazu muss&lt;br /&gt;
* die Linkereinstellung für das Verschieben der Sektion &#039;&#039;.text&#039;&#039; angepasst werden und (&#039;&#039;&#039;-Ttext = 0xXXXX&#039;&#039;&#039;)&lt;br /&gt;
* evtl. der virtuelle Funktionspointer in der Anwendung geändert werden (falls ein Rücksprung zum Bootloader gewünscht ist &#039;&#039;&#039;void (*bootloader)(void) = 0xXXXX&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
Um dies zu erleichtern, habe ich hier die Sprungadressen für ausgewählte AVRs tabellarisch zusammengetragen. Dabei wurde immer die maximale Größe des Bootloader betrachtet, also &#039;&#039;&#039;BOOTSZ0=0&#039;&#039;&#039; und &#039;&#039;&#039;BOOTSZ1=0&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Größe und Startadresse der Bootloader Sektion für ausgewählte AVR-Devices&lt;br /&gt;
|- &lt;br /&gt;
! Device || Flash-Größe&amp;lt;br&amp;gt;für Applikation || Flash-Größe&amp;lt;br&amp;gt;des Bootloaders || Startadresse des Bootloaders&amp;lt;br&amp;gt;(Byteadresse)&lt;br /&gt;
|-&lt;br /&gt;
| ATmega8/88 || 6144 Byte || 2048 Byte || 0x1800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega16/164/168 || 14336 Byte || 2048 Byte || 0x3800&lt;br /&gt;
|-&lt;br /&gt;
| ATmega32/324/328 || 28672 Byte || 4096 Byte || 0x7000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega64/644/640 || 57344 Byte || 8192 Byte || 0xE000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega128/1284/1280/1281 || 122880 Byte || 8192 Byte || 0x1E000&lt;br /&gt;
|-&lt;br /&gt;
| ATmega2560/2561 ||  253952 Byte || 8192 Byte || 0x3E000&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Damit kann jeder den Bootloader nach seinen Wünschen anpassen. Eine gängige Praxis ist auch, ein kleines PC-Programm zu schreiben, welches dem Bootloader die Flash-Daten gleich Binär übergibt. Das geht schneller und spart das aufwendige Parsen. Eine weitere Idee ist es, den Bootloader so anzupassen, das er sich wie ein STK500 an der seriellen Schnittstelle verhält. Dazu muss man die [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2735 AP068] von Atmel umsetzen. Auch dies sollte nach dem Studium des Tutorial kein Problem mehr sein :)&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/user/show/mario Nachricht an den Author]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader|B]]&lt;/div&gt;</summary>
		<author><name>Mario</name></author>
	</entry>
</feed>