<?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=Niko</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=Niko"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Niko"/>
	<updated>2026-04-10T23:21:24Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=MMC-_und_SD-Karten&amp;diff=60223</id>
		<title>MMC- und SD-Karten</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=MMC-_und_SD-Karten&amp;diff=60223"/>
		<updated>2011-09-05T21:03:54Z</updated>

		<summary type="html">&lt;p&gt;Niko: Laut http://en.wikipedia.org/wiki/Secure_Digital#Compared_to_other_flash_memory_formats sind alle SD (nicht MMC) Karten SPI kompatibel!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;MMC- und SD-Speicherkarten lassen sich im [[SPI]]-Modus relativ einfach mit einem Mikrocontroller ansteuern. Prinzipiell git es zwischen SD-Card und MMC nicht viele Unterschiede, allerdings sind SD-Karten weiter verbreitet, in der Regel schneller als MMCs, und haben eine besser implementiertes SPI-Interface. Es existieren diverse Varianten (miniSD, microSD) die zur normalen SD-Card weitgehend kompatibel sind.&lt;br /&gt;
&lt;br /&gt;
Die Karte liest das anliegende Datenbit mit der steigenden Taktflanke ein, als SPI-Modi eignen sich somit Mode 0 (CPOL=0, CPHA=0) und Mode 3 (CPOL=1, CPHA=1) (siehe auch [[Serial Peripheral Interface]]). Bei MMCs ist der SPI-Modus nicht genau spezifiziert, somit kommt es durchaus mal vor dass der SPI-Modus je nach Karte unterschiedlich gewählt werden muss, oder dass die Karte überhaupt nicht zuverlässig funktioniert (siehe [http://www.mikrocontroller.net/forum/read-1-343528.html Beitrag im Forum]).&lt;br /&gt;
&lt;br /&gt;
== DOs und DON&#039;Ts bei der Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lasst euch nicht verrückt machen&#039;&#039;&#039; wenn es einfach nicht funktioniert, sondern probiert zu allererst mal eine SD-Karte eines anderen Herstellers aus. Die MMC-Implementierung für AVR von Elm Chan z.&amp;amp;nbsp;B. funktioniert mit SanDisk problemlos hat aber mit Platinum Karten ein Problem.&lt;br /&gt;
* Taktfrequenz bei der Initialisierung nicht höher als 400kHz&lt;br /&gt;
* Ein Pullup-Widerstand am Ausgang der MMC/SD Karte (DO) ist für eine saubere Initialisierung per SPI notwendig! [http://www.mikrocontroller.net/topic/112421#1001693 (Thread mit Erklärung dazu)]&lt;br /&gt;
* Saubere Versorgung: Kein Dioden-Pfusch, mit dem eine vorhandene 5V Versorgung mittels in Reihe geschalteter Dioden auf irgendwas im Bereich 3V &amp;quot;geregelt&amp;quot; wird. Stattdessen einen guten 3,3V-Regler verwenden. Die Karte mag es nicht, wenn mehr als 60mV Ripple auf Vcc ist. LM317 oder LM1117-ADC/-3.3 mit entspechenden Kondensatoren reicht zumindest bei Basteleien allemal.&lt;br /&gt;
* Sauberer Anschluss der Digitalschnittstelle: Spannungsteiler &amp;quot;verschleifen&amp;quot; die Signale bei hohen Frequenzen und die Übertragungsrate muss dann begrenzt werden. Also entweder ein [http://www.mikrocontroller.net/articles/Pegelwandler Pegelwandler] oder gleich an ein 3,3V I/O anschließen.&lt;br /&gt;
* Ein Pullup-Widerstand an der Select-Leitung (/CS) schadet nicht und stellt sicher, dass die Karte erst mit Absicht selektiert wird.&lt;br /&gt;
* Nachdem die Karte deselektiert wurde (/CS auf high), die Taktleitung noch einige Male pulsen, damit die Karte DO hochohmig/tri-state schaltet (vgl. Chans Erläuterungen).&lt;br /&gt;
* Die Karten verfügen weder über einen Reset- noch einen Sleep-Anschluss. Moderne Karten reduzieren bei Nichtbenutzung ihren Stromverbrauch, einen vollständigen Reset kann man jedoch nicht per Software auslösen. Daher sollten die Karten per P-Channel-FET oder Spannungsregler/-wandler mit Enable-Funktion so angeschlossen werden, dass über Versorgung an/aus ein (Power-On-)Reset ausgelöst werden kann. Dabei darauf achten, dass vorhandene Pull-Up-Widerstände bei abgeschalteter Versorgung ebenfalls deaktiviert werden (vgl. z.&amp;amp;nbsp;B. Schaltplan für den Anschluss von SD-Card/MMC per SPI an AVR in Chans Beispielen. Link unten).&lt;br /&gt;
* Guter Kontakt im Steckplatz, sehr gut eignen sich mit der Zange verbogene Stiftleisten, oft sieht es aus als ob es &amp;quot;passt&amp;quot;, aber es gibt doch keinen Kontakt, daher bei Fehlern: Immer Durchmessen! Auch zu erwähnen wären da alte ISA-Bus Buchsen, die auf jedem alten PC Mainboard drauf sind. Um sicher zu gehen, dass der Kontakt wirklich gut ist, sollte man aber trotzdem SD-Slots benutzen. Diese bekommt man u.a. bei CSD (günstig), Reichelt (teuer) oder aus alten Kartenlesern.&lt;br /&gt;
* Guter Kontakt #2: Was sich im übrigen auch sehr gut eignet sind Adapter von MiniSD auf normales SD-Format, um dann MiniSD zu benutzen. Wenn man eine Stiftleiste im 2.54mm-Format oder Lötnägel im selben Format auf der Platine hat, kann man daran wunderbar den SD-Kartenadapter anlöten. Das ist mechanisch recht stabil. Ein kleines Manko ist allerdings, daß dann eine Gold-Lötzinn-Legierung durch die vergoldeten Kontakte entsteht und das soll ja dem Lötzinn langfristig nicht sehr zuträglich sein. Aber für&#039;s Hobby funktioniert das wunderbar.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||[[Bild:SD Steck Stift.jpg]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(Liefere Bild in besserer Qualität nach!)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Bibliotheken zur Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_e.html ELM ChaN FatFs]  FAT(12,16,32)-Dateisystem. Klein und übersichtlich, hochoptimiert, frei auch für kommerzielle Anwendung. Beispiele für AVR, H8, LPC2k mit MCI u.a. enthalten (&amp;quot;samples&amp;quot;), neuere Versionen mit LFN-Unterstützung. Beispiel für AT91SAM7 inkl. DMA im Projekt [[ARM MP3/AAC Player]]. (siehe auch [http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/ M. Thomas&#039; ARM+SD/MMC Seite])&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_p.html ELM ChaN Petit FatFS] FAT(12,16,32)-Dateisystem. Sehr klein. Beispiele für AVR.&lt;br /&gt;
&lt;br /&gt;
* [http://sourceforge.net/projects/efsl EFSL] FAT16/32-Dateisystem, unterstützt Partitionen und Superfloppys, Beispielcode für AVR, LPC2000 und AT91SAM7 enthalten (siehe auch [http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/ M. Thomas&#039; ARM+SD/MMC Seite])&lt;br /&gt;
&lt;br /&gt;
* [http://www.holger-klabunde.de/avr/avrboard.htm#cf Holger Klabundes FAT16/32] mit Beispielen für AVR MMC/SD und CF, LPC2k mit SPI&lt;br /&gt;
&lt;br /&gt;
* libfat aus dem [http://sourceforge.net/projects/devkitpro devkitpro-Projekt] u.a. LFN-Unterstützung.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikro-control.de/Joomla/index.php?option=com_content&amp;amp;task=view&amp;amp;id=18&amp;amp;Itemid=30 SD-Logger] - FAT 16, für den privaten Einsatz kostenfrei&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-125350.html MMC-Ansteuerung mit FAT16 von Ulrich Radig]&lt;br /&gt;
&lt;br /&gt;
* [http://www.zws.com/products/dosfs/index.html DOSFS Free FAT12/FAT16/FAT32 Filesystem] &amp;quot;DOSFS is a free FAT-compatible filesystem intended for fairly low-end embedded applications. Intended target systems would be in the ballpark of 1K RAM, 4K ROM or more&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/48481 MMC/SD-Karte mit FAT16 an AVR] von Roland Riegel&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.circuitcellar.com/pub/Circuit_Cellar/2005/176/Sham176.zip Circuit Cellar FAT16 MMC/SD] mit MMC/SD Hardwaretreiber für MSP430&lt;br /&gt;
&lt;br /&gt;
* [http://www.analog.com &amp;quot;Implementing FAT32 File Systems on ADSP-BF533 Blackfin Processors&amp;quot;] Application Note und Code von ADI&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools.asp?family_id=682 FAT-Code in Atmel&#039;s AVR32 UC3 Software-Library]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/FAT32 AVR FAT16/32] Bibliothek mit Wiki. Unterstützt LFN und Ordner Rekursiv löschen. SVN für neuste Version.&lt;br /&gt;
&lt;br /&gt;
* [http://www.embedded-os.de/index.html?pcfat_port.htm pC/FAT driver] &amp;quot;using SPI for sector read/write to MMC/MMCplus/HD-MMC/M-Bridge/SD/SDHC-cards on different platforms&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* [http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html SD Card Interfacing with ATmega8] (FAT32 implementation) by CC Dharmani&lt;br /&gt;
&lt;br /&gt;
* [[AVR FAT32]]&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Informationen ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.digitalspirit.org/file/index.php/obj/docs/sd/ Datenblätter] ( [http://www.digitalspirit.org/file/index.php/obj-download/docs/sd/ProductManualSDCardv2.2final.pdf SD Card Product Manual 2.2] )&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/docs/mmc/mmc_e.html ELM ChaN - How to Use an MMC]&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_e.html ELM ChaN - MMC/SD Benchmarks]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=MMC*+SD Beiträge zum Thema MMC/SD im Forum (ca. 200 Threads)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.shop.display3000.com/elektronikmodule/sd-speicherkartenplatine.html Weiter unten im Text (runterscrollen) gibt es interessante Oszi-Bilder zu den oft genannten Spannungsteilern oder Transistorlösungen als Pegelwandler]&lt;br /&gt;
&lt;br /&gt;
* [http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=2680&amp;amp;dDocName=en547784 Microchip Memory Disk Drive System mit FAT32 und SDHC Unterstützung]&lt;br /&gt;
&lt;br /&gt;
* [http://www.ifas.htwk-leipzig.de/easytoweb/download/D&amp;amp;E_11_2006_Anbindung_von_SD-Karten.pdf Gute Beschreibung der SDIO-Architektur und wie man eine SD-Karte mit ARM bzw. AVR benutzt]&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.instructables.com/id/EZD6Q18LYGES1762SJ/ DIY SD-Card Fassung aus einem alten Floppy Kabel]&lt;br /&gt;
&lt;br /&gt;
* [http://www.watterott.com/index.php?page=search&amp;amp;page_action=query&amp;amp;desc=on&amp;amp;sdesc=on&amp;amp;keywords=multicomp&amp;amp;x=0&amp;amp;y=0 Halter für SD-Karten] (Shop www.watterott.com)&lt;br /&gt;
&lt;br /&gt;
* [http://www.shop.display3000.com/elektronikmodule/sd-speicherkartenplatine.html günstige Komplettlösung mit bidirektionalem Pegelwandler, Spannungsregler, Kartenhalter]&lt;br /&gt;
&lt;br /&gt;
== Projekte ==&lt;br /&gt;
&lt;br /&gt;
* [http://retromaster.wordpress.com/ufe/ Ultimate Floppy Emulator] von Retromaster (PIC32 @ 80 Mhz, 16 Mb SDRAM)&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>Niko</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=60089</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=60089"/>
		<updated>2011-09-03T11:37:29Z</updated>

		<summary type="html">&lt;p&gt;Niko: /* 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 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. 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>Niko</name></author>
	</entry>
</feed>