<?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=Pfleury</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=Pfleury"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/Pfleury"/>
	<updated>2026-04-11T13:37:06Z</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=101264</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=101264"/>
		<updated>2019-10-30T21:50:28Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Software */&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.peterfleury.epizy.com/avr-software.html 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ändnis 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 besonderen 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 entspricht 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://www.peterfleury.epizy.com/avr-software.html UART-Library]  von [http://www.peterfleury.epizy.com 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 wie 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; kennzeichnet 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>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=GPS_Logger&amp;diff=101263</id>
		<title>GPS Logger</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=GPS_Logger&amp;diff=101263"/>
		<updated>2019-10-30T21:24:32Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Verwendete Libraries */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:gLogger_Logo.png|right]]&lt;br /&gt;
&#039;&#039;von Martin &#039;Kaktus621&#039; Matysiak&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Wichtige Information==&lt;br /&gt;
&lt;br /&gt;
Mittlerweile gibt es diesen GPS-Logger in einer deutlich kleineren und verbesserten Version: [[GPS Logger Mini]]. &lt;br /&gt;
&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Der Sommer nähert sich und die Versuchung steigt, sich mal vom Arbeitstisch zu entfernen und etwas im Freien zu unternehmen. Wie schön wäre es, den Bekannten daheim oder den Freunden im Internet ganz leicht zu zeigen, wo man überall gewesen ist?&lt;br /&gt;
&lt;br /&gt;
Dies war der Auslöser für mein erstes großes Projekt : Ein GPS-Logger!&lt;br /&gt;
&lt;br /&gt;
Über ein GPS Modul empfängt das Gerät die aktuelle Position des Trägers und schreibt diese auf eine SD-Speicherkarte.&lt;br /&gt;
Zu Hause angekommen kann man die aufgezeichneten Daten unter verschiedenen Gesichtspunkten analysieren, zum Beispiel die zurückgelegte Strecke (in Google Earth betrachtbar) oder die bewältigten Höhenmeter.&lt;br /&gt;
&lt;br /&gt;
Der GPS-Logger besteht im Groben aus den folgenden 3 Komponenten:&lt;br /&gt;
&lt;br /&gt;
* Ein AVR Mikrocontroller (z.&amp;amp;nbsp;B. ATMega32)&lt;br /&gt;
* Ein GPS Modul (z.&amp;amp;nbsp;B. Navilock NL507 TTL)&lt;br /&gt;
* Eine SD Speicherkarte&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Planung ===&lt;br /&gt;
Neben den drei Hauptkomponenten gehören natürlich noch ein paar andere Teile zur Schaltung, damit die Kommunikation untereinander funktioniert. Im Folgenden der Schaltplan meines Aufbaus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_schalt_v2.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Versorgt wird es von einem LiPo-Handyakku, der im voll geladenen Zustand 4,1V liefert. Das Ganze wird auf einer einseitigen Platine (u.A. mit Verwendung von SMD Bauteilen) aufgebaut, hier das Board Layout:&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_board_v2.png|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Umsetzung ===&lt;br /&gt;
Und so sieht das Ganze fertig bestückt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_platine_top.jpg|300px]] [[Bild:gLogger_platine_bottom.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
Der TQFP AVR hat mir schon ein paar Schwierigkeiten beim Löten bereitet, aber es hat alles geklappt (konnte keine Kurzschlüsse feststellen), auch wenn es nicht ganz perfekt aussieht. Der verwendete MicroSD Kartenslot ist erhältlich bei CSD-Electronics unter der Artikelnummer SCHA2B0300.&lt;br /&gt;
&lt;br /&gt;
Nachtrag 08.08.08:&lt;br /&gt;
Der eingesetzte MosFET stellt sich als in diesem Fall unbrauchbar heraus, da er einen zu hohen Source-Drain Widerstand besitzt, was zur Folge hat, dass das GPS Modul mit seinen ca. 60mA Stromverbrauch zu wenig Spannung erhält (lediglich 2,4V anstatt der vollen 3,3V). &lt;br /&gt;
Gelöst werden kann das Problem durch einen BSS138 als Ersatz, diesen gibt es allerdings nur im SOT23 Gehäuse, weshalb ich das Layout und den Schaltplan soeben aktualisiert habe. Ansonsten kann man den MosFET auch einfach entfernen, die Source-Drain-Strecke überbrücken und hat so immer volle 3,3V am GPS Modul (allerdings fehlt dann die Möglichkeit des Abschaltens bei schwachem Akku).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Außerdem habe ich das Gehäuse nun fertiggestellt, hier neue Fotos:&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_gehaeuse_auf.jpg|300px]] [[Bild:gLogger_gehaeuse_zu.jpg|300px]]&lt;br /&gt;
&lt;br /&gt;
=== Downloads ===&lt;br /&gt;
* [[Media:gLogger_eagle.zip|Eagle Projekt Dateien zum gLogger]]&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== AVR ===&lt;br /&gt;
&lt;br /&gt;
==== Features ====&lt;br /&gt;
&lt;br /&gt;
* Aufnahme kann per Taster gestartet / gestoppt werden&lt;br /&gt;
* Status LEDs zeigen den Status an&lt;br /&gt;
* Speicherverbrauch : ca. 1,4 MB pro Stunde (bei GPS Fix und 7 NMEA Meldungen pro Sekunde), man kann natürlich 6 von den 7 Meldungen abschalten, wenn man NUR die Position haben möchte, dann sinkt der Speicherverbrauch auf ca. 200KB pro Stunde&lt;br /&gt;
* Automatischer Stromsparmodus bei schwachem Akku (Aufnahme wird gestoppt, GPS Modul deaktiviert, der Mikrocontroller in einen Sleep-Modus versetzt)&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Libraries ====&lt;br /&gt;
&lt;br /&gt;
* [[http://www.peterfleury.epizy.com/ UART library von Peter Fleury]]&lt;br /&gt;
* [[http://www.ulrichradig.de/home/index.php/avr/mmc-sd MMC/SD library von Ulrich Radig]]&lt;br /&gt;
* [[http://www.mikro-control.de/Joomla/index.php?option=com_content&amp;amp;task=view&amp;amp;id=5&amp;amp;Itemid=26 FAT16 library von Stephan Busker]]&lt;br /&gt;
&lt;br /&gt;
==== Changelog ====&lt;br /&gt;
&lt;br /&gt;
Version 1.02 (16.08.2008):&lt;br /&gt;
&lt;br /&gt;
* Akkuspannung wird über den ADC ausgelesen und bei einer Akkuspannung kleiner als ca. 3,6V die Aufnahme gestoppt, das GPS Modul abgeschaltet und der µC in einen Sleepmode versetzt (muss noch getestet werden)&lt;br /&gt;
* Blinken während der Aufnahme überarbeitet (per Timer Interrupt)&lt;br /&gt;
&lt;br /&gt;
Version 1.01 (17.07.2008):&lt;br /&gt;
&lt;br /&gt;
* GPS Library um den Befehl gps_get_nmea erweitert -&amp;gt; Wartet erst auf einen vollständigen NMEA Datensatz und schreibt diesen in den übergebenen Buffer&lt;br /&gt;
* Die Ledcodes wurden überarbeitet. Überschreiben nicht mehr den ganzen Ports (wodurch z.&amp;amp;nbsp;B. Pull-Ups deaktiviert werden könnten) sondern verändern nur die jeweiligen Bits der Leuchtdioden&lt;br /&gt;
  &lt;br /&gt;
Version 1.00 (12.07.2008):&lt;br /&gt;
&lt;br /&gt;
* Erstes öffentliches Release&lt;br /&gt;
* AVR empfängt im Aufzeichnungsmodus fortlaufend Zeichen vom GPS Modul und schreibt diese direkt auf die SD Karte&lt;br /&gt;
&lt;br /&gt;
==== FAQ ====&lt;br /&gt;
&lt;br /&gt;
Falls ihr Fragen oder Probleme habt, wendet euch per E-Mail an [[mailto:mail@k621.de mich]] oder schreibt die Frage in die [[http://www.mikrocontroller.net/topic/102452 Foren-Diskussion]]. Wenn ihr damit einverstanden seid, werde ich die Fragen dann auch hier veröffentlichen.&lt;br /&gt;
&lt;br /&gt;
===== Was hat es mit den LED-Codes auf sich? =====&lt;br /&gt;
&#039;&#039;eingesandt von Marcel&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es gibt drei Leuchtdioden, nämlich &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_STAT&#039;&#039;&#039; (Grün)&amp;lt;/span&amp;gt;, &amp;lt;span style=&amp;quot;color:#ccaa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_WARN&#039;&#039;&#039; (Gelb)&amp;lt;/span&amp;gt; und &amp;lt;span style=&amp;quot;color:#aa2222;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_BATT&#039;&#039;&#039; (Rot)&amp;lt;/span&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Erste Regel:&#039;&#039;&#039; Ein Blinken ist ein gutes Zeichen dafür, dass alles funktioniert. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zweite Regel:&#039;&#039;&#039; Grün bedeutet alles OK, Gelb möchte vor etwas Warnen und Rot zeigt an, dass der Akku schwach ist.&lt;br /&gt;
&lt;br /&gt;
* Wenn alles in Ordnung ist, sollte nach dem Einschalten &amp;lt;span style=&amp;quot;color:#ccaa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_WARN&#039;&#039;&#039;&amp;lt;/span&amp;gt; Blinken. Dies bedeutet, dass sich der Controller in der Hauptschleife befindet, aber keine Aufnahme läuft - Also kein Grund zur Sorge. Ist dies nicht der Fall (z.&amp;amp;nbsp;B. &amp;lt;span style=&amp;quot;color:#ccaa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_WARN&#039;&#039;&#039;&amp;lt;/span&amp;gt; leuchtet dauerhaft), dann hat höchstwahrscheinlich die Initalisierung der SD Karte nicht funktioniert&lt;br /&gt;
* Betätigt man nun die Aufnahmetaste, leuchtet zunächst &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_STAT&#039;&#039;&#039;&amp;lt;/span&amp;gt; als Feedback auf, nach ca. 2 Sekunden erlischen dann beide LED&#039;s und &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_STAT&#039;&#039;&#039;&amp;lt;/span&amp;gt; beginnt zu blinken, das bedeutet, dass die Aufnahme läuft&lt;br /&gt;
&lt;br /&gt;
===== Wieso kann ich die Aufnahme nicht beenden ? =====&lt;br /&gt;
&#039;&#039;eingesandt von Marcel&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es kann der Fall auftreten, dass ihr die Aufnahme ganz normal starten konntet und &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;&#039;&#039;&#039;LED_STAT&#039;&#039;&#039;&amp;lt;/span&amp;gt; auch blinkt, aber ein erneutet Druck auf die Taste bewirkt nichts.&lt;br /&gt;
&lt;br /&gt;
Dies ist ein anzeichen dafür, dass der Controller beim Auslesen der NMEA Meldung stecken geblieben ist. Die LED blinkt weiter, da diese über einen Timer gesteuert wird, parallel zum Hauptprogramm. &lt;br /&gt;
&lt;br /&gt;
Wieso bleibt der Controller denn einfach so stecken? Das hat entweder den Grund, dass das GPS-Modul überhaupt nicht angesteckt ist, oder aber, dass dieses keine NMEA-Konformen Meldungen ausgibt oder die falsche Baudrate eingestellt ist.&lt;br /&gt;
&lt;br /&gt;
==== Download ====&lt;br /&gt;
&lt;br /&gt;
* [[Media:gLogger_avr_1.02.zip|gLogger AVR Software - Version 1.02 (16.08.2008)]]&lt;br /&gt;
* [[Media:gLogger_avr_1.01.zip|gLogger AVR Software - Version 1.01 (17.07.2008)]]&lt;br /&gt;
&lt;br /&gt;
=== Computer ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_nmeaTool.png|thumb|300px|Screenshot der NMEA Toolbox]]&lt;br /&gt;
&lt;br /&gt;
Für den Computer habe ich derweilen eine Software entwickelt, die die aufgezeichneten GPS Daten verarbeiten kann und diese zum Beispiel in eine *.kml-Datei (Für Google Earth oder Google Maps) schreibt, oder aber Höhen- und Geschwindigkeitsdiagramme erstellen kann.&lt;br /&gt;
&lt;br /&gt;
Die Funktion des Konvertierens zu *.kml funktioniert derzeit perfekt, die Optionen zum erstellen der Höhen- und Geschwindigkeitsdiagramme erzeugen bis jetzt lediglich Excel-Tabellen, wo man sich später leicht ein Diagramm anzeigen lassen kann, geplant ist allerdings, dass die &amp;quot;NMEA Toolbox&amp;quot; direkt Diagramme erstellt und anzeigt.&lt;br /&gt;
&lt;br /&gt;
Die Toolbox wurde in Java entwickelt, der NetBeans-Projektordner in seiner aktuellen Version, sowie die kompilierte *.jar zum ausführen liegt hier als Download bereit.&lt;br /&gt;
&lt;br /&gt;
==== Features ====&lt;br /&gt;
&lt;br /&gt;
* Eingabeformat : Jedes beliebige, solange NMEA-Datensätze der Dateiinhalt sind&lt;br /&gt;
* Ausgabeformat : &lt;br /&gt;
** Google Earth Pfad (*.kml)&lt;br /&gt;
** Google Maps Pfad (*.kml)&amp;lt;sup&amp;gt;[1]&amp;lt;/sup&amp;gt;&lt;br /&gt;
** Geschwindigkeitsdiagramm (*.csv)&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;&lt;br /&gt;
** Höhendiagramm (*.csv)&amp;lt;sup&amp;gt;[2]&amp;lt;/sup&amp;gt;&lt;br /&gt;
** GPX-Track (*.gpx)&lt;br /&gt;
* Verschiedene konfigurierbare Filter, um Genauigkeitsverluste des GPS-Moduls möglichst zu entfernen&lt;br /&gt;
&lt;br /&gt;
[1] : Das besondere am Maps Pfad ggü. Google Earth ist, dass hier die Anzahl der Pfadpunkte auf 350 reduziert wird, da Google Maps nicht mehr in einem Pfad akzeptiert, dadurch gibt es allerdings auch einen Verlust von Details in der Strecke&lt;br /&gt;
&lt;br /&gt;
[2] : In Zukunft ist auch ein echtes Diagramm als Ausgabe geplant, dann als *.png oder ähnliches&lt;br /&gt;
&lt;br /&gt;
==== Changelog ====&lt;br /&gt;
&lt;br /&gt;
Version 1.3 (18.04.2010)&lt;br /&gt;
&lt;br /&gt;
* Verwendung von &amp;quot;Preferences&amp;quot; zum Sichern des zuletzt verwendeten Ordners (Implementiert von [http://projekte.esuark.de/ Jens Krause], vielen Dank!)&lt;br /&gt;
&lt;br /&gt;
Version 1.2 (21.03.2009):&lt;br /&gt;
&lt;br /&gt;
*  Javadoc angelegt&lt;br /&gt;
* Kompletten Code umstrukturiert&lt;br /&gt;
** Konvertierer in eigene Klassen gepackt&lt;br /&gt;
** Konvertierer laufen nun als Threads&lt;br /&gt;
** Insgesamt Code etwas aufgeräumt&lt;br /&gt;
* Interface &#039;erfrischt&#039;&lt;br /&gt;
** Konvertier-Buttons durch Grafiken ersetzt (Text wird als Tooltip angezeigt)&lt;br /&gt;
** Statusanzeige eingebaut&lt;br /&gt;
* Weitere kleine Übersetzungen auf Deutsch&lt;br /&gt;
* Im description-Tag der KML-Datei wird nun das Erstellungsdatum angezeigt&lt;br /&gt;
* Einige Filter entfernt (werden nun autom. ausgeführt)&lt;br /&gt;
* Google Maps Frage erscheint nur noch, wenn man eine KML Datei erstellen möchte und der Pfad mehr als 350 Punkte besitzt&lt;br /&gt;
* Google Maps - Reduzieralgorithmus optimiert&lt;br /&gt;
* GPX Format implementiert&lt;br /&gt;
&lt;br /&gt;
Version 1.1 (03.01.2009):&lt;br /&gt;
&lt;br /&gt;
* Meldungen in Dialogfenstern auf Deutsch übersetzt&lt;br /&gt;
* Fehler bei Google-Maps-Filter behoben&lt;br /&gt;
&lt;br /&gt;
Version 1.0d (25.09.2008):&lt;br /&gt;
&lt;br /&gt;
* NMEA Toolbox auf Deutsch übersetzt&lt;br /&gt;
  &lt;br /&gt;
Version 1.00 (12.07.2008):&lt;br /&gt;
&lt;br /&gt;
* Erstes öffentliches Release&lt;br /&gt;
&lt;br /&gt;
==== FAQ ====&lt;br /&gt;
&lt;br /&gt;
Falls ihr Fragen oder Probleme habt, wendet euch per E-Mail an [[mailto:mail@k621.de mich]] oder schreibt die Frage in die [[http://www.mikrocontroller.net/topic/102452 Foren-Diskussion]]. Wenn ihr damit einverstanden seid, werde ich die Fragen dann auch hier veröffentlichen.&lt;br /&gt;
&lt;br /&gt;
==== Download ====&lt;br /&gt;
&lt;br /&gt;
* [[Media:nmea_toolbox_src_1.3.zip|NMEA Toolbox - NetBeans Projektordner V1.3]]&lt;br /&gt;
* [[Media:nmea_toolbox_jar_1.3.zip|NMEA Toolbox - Ausführbare .jar V1.3]] &lt;br /&gt;
* [[Media:nmea_toolbox_src_1.2.zip|NMEA Toolbox - NetBeans Projektordner V1.2]]&lt;br /&gt;
* [[Media:nmea_toolbox_jar_1.2.zip|NMEA Toolbox - Ausführbare .jar V1.2]] &lt;br /&gt;
* [[Media:nmea_toolbox_src_1.1.zip|NMEA Toolbox - NetBeans Projektordner V1.1]]&lt;br /&gt;
* [[Media:nmea_toolbox_jar_1.1.zip|NMEA Toolbox - Ausführbare .jar V1.1]] &lt;br /&gt;
* [[Media:nmea_toolbox_src_1.0.d.zip|NMEA Toolbox - NetBeans Projektordner V1.0d]]&lt;br /&gt;
* [[Media:nmea_toolbox_jar_1.0.d.zip|NMEA Toolbox - Ausführbare .jar V1.0d]]&lt;br /&gt;
* [[Media:nmea_toolbox_src.zip|NMEA Toolbox - NetBeans Projektordner V1.0]]&lt;br /&gt;
* [[Media:nmea_toolbox_jar.zip|NMEA Toolbox - Ausführbare .jar V1.0]]&lt;br /&gt;
&lt;br /&gt;
== To-Do / Ideen für die Zukunft ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;Stromversorgung überdenken -&amp;gt; Geht es effizienter?&amp;lt;/span&amp;gt; &lt;br /&gt;
** Ja, siehe aktuelle Schaltung&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;Das ganze auf einer richtigen Platine entwerfen, evtl. SMD-Bauteile verwenden, damit das Gerät noch kleiner wird&amp;lt;/span&amp;gt;&lt;br /&gt;
** Erledigt; Layout siehe oben, 6 Drahtbrücken, Platinengröße 61mm x 72mm&lt;br /&gt;
&lt;br /&gt;
* Eine kompakte SMD-Platine entwerfen.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#aa2222;&amp;quot;&amp;gt;Erweiterung : Einfaches Navigationssystem -&amp;gt; Am Computer wird ein Pfad festgelegt und das Gerät leitet einen von Wegpunkt zu Wegpunkt, indem die Richtung über LEDs oder ein Display gezeigt wird&amp;lt;/span&amp;gt;&lt;br /&gt;
** Idee wieder verworfen, Statusanzeige über Display wird aber möglicherweise noch realisiert&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;Besseren Algorithmus für das Reduzieren der Pfadpunkte in der NMEA Toolbox entwickeln (mit geringerem Genauigkeitsverlust)&amp;lt;/span&amp;gt;&lt;br /&gt;
** Ist in Bearbeitung&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#22aa22;&amp;quot;&amp;gt;NMEA-Toolbox : Export in das *.GPX-Format&amp;lt;/span&amp;gt;&lt;br /&gt;
** Ist in Bearbeitung&lt;br /&gt;
&lt;br /&gt;
* Echte Diagramme in der NMEA Toolbox erstellen&lt;br /&gt;
** Hierfür kann ich dir die JFreeChart Library empfehlen! (falls du die nicht schon kennst) Sehr einfach zu verwenden und macht sehr saubere Diagramme.&lt;br /&gt;
** Folgt in der übernächsten Version!&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span style=&amp;quot;color:#aa2222;&amp;quot;&amp;gt;Über das PC-Tool Parameter verändern (welche Werte sollen aufgezeichnet werden, welche Aufzeichnungsrate...)&amp;lt;/span&amp;gt;&lt;br /&gt;
** Würde eine direkte Verbindung zw. PC und gLogger erfordern -&amp;gt; vorerst nicht geplant (außer jemand möchte sich da dran setzen? :))&lt;br /&gt;
*** Hier wird man fündig: http://www.obdev.com/products/vusb/benefits-de.html, da gibt es viele Beispielprojekte, aus denen man sich das Benötigte zusammenkopieren kann.&lt;br /&gt;
&lt;br /&gt;
* Digitaleingänge hinzu und die zustände dieser im log hinzufügen&lt;br /&gt;
** Folgt möglicherweise im Sommer (z.&amp;amp;nbsp;B. Temperaturüberwachung o.ä.)&lt;br /&gt;
&lt;br /&gt;
* Alle Anschlüsse zum AVR per Pads freilegen&lt;br /&gt;
&lt;br /&gt;
* Anschlussmöglichkeit für große Tastenmatrix (30-50 Tasten), um sehr schnell Geo-Attribute eingeben zu können. Die Idee ist, es für OpenStreetMap zu verwenden: dann kann man auf Knopfdruck &amp;quot;ab hier asphaltiert&amp;quot;, &amp;quot;hier Foto gemacht&amp;quot;, &amp;quot;hier steht eine Bank&amp;quot; usw. eingeben.&lt;br /&gt;
** Eine gute Idee. An sich kann man die Tastatur an den freigelegten Port klemmen, man müsste dann nur noch die Software dementsprechend anpassen&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
* [http://maps.google.com/maps/ms?ie=UTF&amp;amp;msa=0&amp;amp;msid=102721812355516657756.0004540865131e3917627 Beispiel-Route in Google Maps]&lt;br /&gt;
&lt;br /&gt;
[[Bild:gLogger_vgl.jpg|thumb|600px|Genauigkeitsvergleich Google Maps Pfad - Originalpfad (G. Earth)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr style=&amp;quot;visibility:hidden; clear:both&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe Auch ==&lt;br /&gt;
&lt;br /&gt;
* Diskussion im Forum : http://www.mikrocontroller.net/topic/102452&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.sparkfun.com/datasheets/GPS/NMEA%20Reference%20Manual1.pdf NMEA Reference Manual (PDF-Dokument)]&lt;br /&gt;
&lt;br /&gt;
[[Category:1. Wettbewerb]]&lt;br /&gt;
[[Kategorie:GPS]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=101262</id>
		<title>AVR-GCC-Tutorial/Der UART</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=101262"/>
		<updated>2019-10-30T21:22:53Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Einzelne Zeichen empfangen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Über den [[UART]] kann ein [[AVR]] leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;[[seriell]]er Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
;Debug-Schnittstelle: z.&amp;amp;nbsp;B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;Logging&amp;quot; oder &amp;quot;UART-debugging&amp;quot;) über [[RS-232]] auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser [https://sites.google.com/site/terminalbpp/], [http://www.der-hammer.info/terminal/ HTerm]; Unix/Linux z.&amp;amp;nbsp;B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.&amp;amp;nbsp;B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
;Mensch-Maschine Schnittstelle: z.&amp;amp;nbsp;B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.&amp;amp;nbsp;B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle] und Artikel [[Tinykon]]) &lt;br /&gt;
;Übertragen von gespeicherten Werten: z.&amp;amp;nbsp;B. bei einem Datenlogger&lt;br /&gt;
;Anschluss von Geräten: mit serieller Schnittstelle (z.&amp;amp;nbsp;B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
;&amp;quot;Feldbusse&amp;quot;: auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.&amp;amp;nbsp;B. MAX485)&lt;br /&gt;
;DMX, Midi: etc.&lt;br /&gt;
;LIN-Bus: &#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork: Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähige UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal &#039;&#039;&#039;A&#039;&#039;&#039;synchronous &#039;&#039;&#039;R&#039;&#039;&#039;eceiver and &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen bis vier U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (logisch 0) und 5V (logisch 1). Die Schnittstellenspezifikation für RS-232 definiert jedoch -12V ... -3V (logisch 1) und&lt;br /&gt;
+3 ... +12V (logisch 0). Daher muss der Signalaustausch zwischen AVR und Partnergerät invertiert werden. Für die Anpassung der Pegel und das Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2 Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.&amp;amp;nbsp;B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
Controller mit nur einem Quarzanschluss (z.B Atmega328), die den Timer2 im Asynchron Modus benutzen, müssen intern mit dem RC-Oszillator getaktet werden. Dieser sollte dann kalibriert werden:&lt;br /&gt;
[[AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR#Kalibrieren_des_internen_Oszillators_mit_Timer2_als_Zeitbasis|Kalibrieren des internen Oszillators mit Timer2 als Zeitbasis]]&lt;br /&gt;
&lt;br /&gt;
== Die UART-Register ==&lt;br /&gt;
&lt;br /&gt;
Die UART wird über vier separate Register angesprochen. Die USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die folgende Tabelle gibt nur die Register für die UARTs der ATmega8/16/32 u.ä. wieder.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| width=&amp;quot;100&amp;quot; | &#039;&#039;&#039;UCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;|| &#039;&#039;&#039;TXC&#039;&#039;&#039;|| &#039;&#039;&#039;UDRE&#039;&#039;&#039;|| &#039;&#039;&#039;FE&#039;&#039;&#039;|| &#039;&#039;&#039;DOR&#039;&#039;&#039;|| &#039;&#039;&#039;PE&#039;&#039;&#039;|| &#039;&#039;&#039;U2X&#039;&#039;&#039;|| &#039;&#039;&#039;MPCM&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R|| R/W|| R|| R|| R|| R|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 1|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt, bevor ein weiteres Zeichen komplett empfangen wurde, wird eine Überlauf-Fehlersituation eintreten. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, dass die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
:Um das Bit zu löschen muss eine 1 an die entsprechende Position geschrieben werden!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sende-Schieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DOR&#039;&#039;&#039; (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;O&#039;&#039;&#039;ver &#039;&#039;&#039;R&#039;&#039;&#039;un)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PE&#039;&#039;&#039; (&#039;&#039;&#039;P&#039;&#039;&#039;arity &#039;&#039;&#039;E&#039;&#039;&#039;rror&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Empfangsdatenregister bereit liegende Zeichen einen Paritätsfehler aufweist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;U2X&#039;&#039;&#039; (Double the transmission speed)&lt;br /&gt;
:Dieses Bit wird lediglich im asynchronen Modus genutzt. Im synchronen Modus ist es 0 zu setzen. Wird das Bit gesetzt, so wird der Baudraten Divisor von 16 auf 8 reduziert, was einer Verdopplung der Transferrate gleich kommt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MPCM&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;ulti &#039;&#039;&#039;P&#039;&#039;&#039;rozessor &#039;&#039;&#039;C&#039;&#039;&#039;ommunication &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit aktiviert die Multi-Prozessor-Kommunikation. Jeder eintreffende Frame der keine Adressinformation enthält wird dadurch ignoriert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UCSRB &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;|| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;|| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;|| &#039;&#039;&#039;RXEN&#039;&#039;&#039;|| &#039;&#039;&#039;TXEN&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ2&#039;&#039;&#039;|| &#039;&#039;&#039;RXB8&#039;&#039;&#039;|| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 0|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCSZ2&#039;&#039;&#039; (Characters Size)&lt;br /&gt;
:Dieses Bit setzt in Verbindung mit dem UCSZ1:0 Bits im UCSRC Register die Anzahl von Datenbits eines Frames beim Empfang oder Senden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UCSRC &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;C&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;URSEL&#039;&#039;&#039;|| &#039;&#039;&#039;UMSEL&#039;&#039;&#039;|| &#039;&#039;&#039;UPM1&#039;&#039;&#039;|| &#039;&#039;&#039;UPM0&#039;&#039;&#039;|| &#039;&#039;&#039;USBS&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ1&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ0&#039;&#039;&#039;|| &#039;&#039;&#039;UCPOL&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 1|| 0|| 0|| 0|| 0|| 1|| 1|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;URSEL&#039;&#039;&#039; (Register Select)&lt;br /&gt;
:Dieses Bit selektiert die Auswahl des UCSRC- bzw. des UBRRH Registers. Beim Lesen von UCSRC wird es als 1 gelesen. Beim Schreiben auf UCSRC muss es auf 1 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
:Achtung: Manche Prozessoren verfügen über dieses Bit, andere wiederrum nicht. Was hat es damit auf sich?&amp;lt;br&amp;gt;Um Zugriffsadressen einzusparen, wurde von Atmel ein etwas seltsamer Weg gewählt. Das UCSRC Register und das High-Byte des Baudratenregisters teilen sich dieselbe Registeradresse! Um der Hardware mitzuteilen, welche Bedeutung ein zugewiesener Wert haben soll, entweder neue Belegung des Baudratenregisters oder eben Konfiguration des UCSRC Registers, dient dieses Bit. Ist es nicht gesetzt, dann wird eine Zuweisung immer als Zuweisung an das High-Byte des Baudratenregisters angesehen, selbst wenn das so nicht beabsichtigt war. Nur dann wenn dieses Bit gesetzt ist, dann wird eine Zuweisung auch tatsächlich als eine Zuweisung an das UCSRC Register gewertet und die Konfiguration verändert. Lässt man das Bit irrtümlich weg, dann verursacht eine Zuweisung an UCSRC eine Veränderung der Baudrateneinstellung!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UMSEL&#039;&#039;&#039; (USART Mode Select)&lt;br /&gt;
:Durch dieses Bit kann eine asynchrone oder synchrone Übertragung eingestellt werden. Durch Setzen des Bits wird eine synchrone Übertragung eingestellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UPM1:0&#039;&#039;&#039; (Parity Mode)&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UPM1&#039;&#039;&#039;|| &#039;&#039;&#039;UPM0&#039;&#039;&#039;|| &#039;&#039;&#039;Parity Mode&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| Disabled&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| Enabled, Even Priority&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| Enabled, OddPriority&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;USBS&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;SART &#039;&#039;&#039;S&#039;&#039;&#039;top &#039;&#039;&#039;B&#039;&#039;&#039;it &#039;&#039;&#039;S&#039;&#039;&#039;elect)&lt;br /&gt;
:Diese Bits setzen die Anzahl der zu sendenden Stopbits eines Frames. Beim Setzen werden 2 Stopbits übertragen, andernfalls nur 1 Stopbit.&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USBS&#039;&#039;&#039;|| &#039;&#039;&#039;Anzahl der Stop Bits&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|  0|| 1&lt;br /&gt;
|- &lt;br /&gt;
|  1|| 2&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCSZ1:0&#039;&#039;&#039; (Character Size)&lt;br /&gt;
:Diese Bits setzen in Verbindung mit UCSZ2 aus dem UCSRB Register die Anzahl der Datenbits eines Frames.&lt;br /&gt;
:Diese Bits setzen den entsprechenden Paritätsmodus.&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCSZ2&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ1&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ0&#039;&#039;&#039;|| &#039;&#039;&#039;Character Size&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| 0|| 5-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| 1|| 6-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| 0|| 7-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| 1|| 8-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| 0|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| 1|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| 0|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| 1|| 9-Bit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCPOL&#039;&#039;&#039; (Clock Polarity)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne kommunizieren möchten. Der Wert, der in dieses Register geschrieben werden muss, errechnet sich nach folgender Formel (wenn U2X Bit 0 gesetzt ist):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis über 115200 Baud möglich, je nach Controller und CPU-Frequenz. Siehe Datenblatt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.&amp;amp;nbsp;B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);                           // UART TX einschalten&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1 &amp;lt;&amp;lt; UCSZ1)|(1 &amp;lt;&amp;lt; UCSZ0); // Asynchron 8N1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; der verwendeten UARTs einzustellen, bzw. bei neueren AVRs die beiden Register &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. &amp;lt;!--Der Wert dafür ergibt sich aus der unten angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen. Dabei ist zu beachten, dass der Präprozessor keine Floating Point Ergebnisse liefert, sondern Integer Ergebnisse. Das bedeutet, dass wenn durch Division auf einen Integer Wert 1.99 zugewiesen wird, 0.99 abgeschnitten werden und das Ergebnis 1.0 ist - obwohl 2.0 viel näher wäre.--&amp;gt;. Die Berechnung wird während des Compilerlaufs ausgeführt, beansprucht also in der gezeigten Form weder Speicher noch Rechenzeit des Controllers. Das Ergebniss wird jedoch als ganzzahliger Wert eingesetzt, d.h. Nachkommastellen werden einfach abgeschnitten und es erfolgt keine Rundung. Aus diesem Grund kann man sich eines kleinen Tricks bedienen, indem vor der eigentlichen Division bei der Zuweisung die Hälfte des Wertes dazu addiert wird. Allgemein formuliert bedeutet das: &amp;lt;code&amp;gt;int i = ( a + b/2 ) / b;&amp;lt;/code&amp;gt;. Dies wird in der unten angegebenen Berechnung von UBRR_VAL ausgenutzt um den Fehler zu minimieren. (Eine ausführliche Erklärung zum &#039;&#039;cleveren Runden&#039;&#039; findet sich in einer [http://www.mikrocontroller.net/topic/170617#1631916 Forumsdiskussion].) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
  UART-Init: &lt;br /&gt;
Berechnung des Wertes für das Baudratenregister &lt;br /&gt;
aus Taktrate und gewünschter Baudrate&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL  // Systemtakt in Hz - Definition als unsigned long beachten &lt;br /&gt;
                         // Ohne ergeben sich unten Fehler in der Berechnung&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL      // Baudrate&lt;br /&gt;
&lt;br /&gt;
// Berechnungen&lt;br /&gt;
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate&lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da damit sowohl automatisch der Wert für UBRR, als auch die Abweichung in der generierten (möglichen) von der gewünschten Baudrate berechnet wird. Im Falle einer zu hohen Abweichung (+/-1%) wird eine Fehlermeldung ausgegeben und der Compilerablauf abgebrochen. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Initialisierung der UART Register kann im Hauptprogramm main() vorgenommen werden. Öfters wird jedoch eine Funktion z.&amp;amp;nbsp;B. uart_init() dafür geschrieben, die in der eigenen Codesammlung in mehreren Projekten verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.&amp;amp;nbsp;B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.&amp;amp;nbsp;B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann den Wert direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init Bsp. ATmega48 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRR0 = UBRR_VAL;&lt;br /&gt;
  UCSR0B |= (1&amp;lt;&amp;lt;TXEN0);&lt;br /&gt;
  // Frame Format: Asynchron 8N1&lt;br /&gt;
  UCSR0C = (1&amp;lt;&amp;lt;UCSZ01)|(1&amp;lt;&amp;lt;UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die einzelne Anweisung ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im nächsten Beispiel gezeigt, ist universeller und portabler und daher vorzuziehen. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init Bsp. ATmega16 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
  UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);  // UART TX einschalten&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0);  // Asynchron 8N1 &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inzwischen gibt es in der avr-libc Makros für obige Berechnung der UBRR Registerwerte aus Taktrate F_CPU und Baudrate BAUD. Dazu wird die Includedatei  [http://www.nongnu.org/avr-libc/user-manual/group__util__setbaud.html &amp;lt;util/setbaud.h&amp;gt;] eingebunden, nachdem F_CPU und die gewünschte Baudrate definiert wurden. Einige Beispiele zur Anwendung finden sich in der Dokumentation der avr-libc. Im Quellcode kann dann analog zur oben gezeigten Vorgehensweise einfach das Makro UBRR_VALUE (bzw. UBRRH_VALUE und UBRRL_VALUE) an der entsprechenden Stelle eingesetzt werden. Es wird auch automatisch ermittelt, ob der U2X-Modus (vgl. Datenblatt) zu geringeren Abweichungen führt und dann dem Makro USE_U2X ein Wert ungleich null zugewiesen. Ein Beispiel (angelehnt an die avr-libc-Dokumentation):&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;
#define F_CPU 1000000 /* evtl. bereits via Compilerparameter definiert */&lt;br /&gt;
#define BAUD 9600&lt;br /&gt;
#include &amp;lt;util/setbaud.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)   &lt;br /&gt;
{&lt;br /&gt;
   UBRRH = UBRRH_VALUE;&lt;br /&gt;
   UBRRL = UBRRL_VALUE;&lt;br /&gt;
   /* evtl. verkuerzt falls Register aufeinanderfolgen (vgl. Datenblatt)&lt;br /&gt;
      UBRR = UBRR_VALUE;&lt;br /&gt;
   */&lt;br /&gt;
#if USE_2X&lt;br /&gt;
   /* U2X-Modus erforderlich */&lt;br /&gt;
   UCSRA |= (1 &amp;lt;&amp;lt; U2X);&lt;br /&gt;
#else&lt;br /&gt;
   /* U2X-Modus nicht erforderlich */&lt;br /&gt;
   UCSRA &amp;amp;= ~(1 &amp;lt;&amp;lt; U2X);&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
   // hier weitere Initialisierungen (TX und/oder RX aktivieren, Modus setzen &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__util__setbaud.html Dokumentation der avr-libc zu &amp;lt;util/setbaud.h&amp;gt;]&lt;br /&gt;
* [http://www.wormfood.net/avrbaudcalc.php WormFood&#039;s AVR Baud Rate Calculator] online.&lt;br /&gt;
* [http://www.gjlay.de/helferlein/avr-uart-rechner.html AVR Baudraten-Rechner] in JavaScript&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
// das entpricht dem &amp;quot;C Code Example USART_Transmit&amp;quot; in der ATMEL-Doku ...&lt;br /&gt;
// besser ist es aber, die CPU nicht in einer Warteschleife zu blockieren:&lt;br /&gt;
&lt;br /&gt;
    if(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE))         /* Senden, wenn UDR frei ist                    */&lt;br /&gt;
    {    UDR = &#039;x&#039;;               /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
    }   &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zum &amp;quot;Blockieren&amp;quot; vgl. [[AVR-GCC-Tutorial#Warten_auf_einen_bestimmten_Zustand|AVR-GCC-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
=== Senden einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* ATmega16 */&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen(Terminator)&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagiert werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Senden von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Gleitkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&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;stdlib.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main (void) &lt;br /&gt;
{&lt;br /&gt;
   // Ausgabe von 0123456789&lt;br /&gt;
   char c;&lt;br /&gt;
&lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
      c = i + &#039;0&#039;;&lt;br /&gt;
      uart_putc( c );&lt;br /&gt;
      // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      &lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Gleitkommazahlen (float/double) können mit bereits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== Einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Siehe auch obere Baudrateneinstellung */&lt;br /&gt;
/* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0);  // Asynchron 8N1 &lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;RXEN);                        // UART RX einschalten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und die Anwendung in einem Beispiel:&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// hier Makro für die Baudratenberechnung &lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_getc (s.o.)&lt;br /&gt;
 &lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uart_init();&lt;br /&gt;
  while (1) &lt;br /&gt;
  {&lt;br /&gt;
    uint8_t c;&lt;br /&gt;
    c = uart_getc();&lt;br /&gt;
&lt;br /&gt;
    // hier etwas mit c machen z.B. auf PORT ausgeben&lt;br /&gt;
    DDRC = 0xFF; // PORTC Ausgang&lt;br /&gt;
    PORTC = c;&lt;br /&gt;
  }&lt;br /&gt;
  return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion uart_getc() blockiert allerdings den Programmablauf, denn es wird gewartet, bis ein Zeichen empfangen wird! Möchte man das Warten vermeiden, kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. &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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// hier Makro für die Baudratenberechnung &lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_getc (s.o.)&lt;br /&gt;
 &lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uart_init();&lt;br /&gt;
  while (1) &lt;br /&gt;
  {&lt;br /&gt;
    if ( (UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
    {&lt;br /&gt;
      // Zeichen wurde empfangen, jetzt abholen&lt;br /&gt;
      uint8_t c;&lt;br /&gt;
      c = uart_getc();&lt;br /&gt;
      // hier etwas mit c machen z.B. auf PORT ausgeben&lt;br /&gt;
      DDRC = 0xFF; // PORTC Ausgang&lt;br /&gt;
      PORTC = c;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      // Kein Zeichen empfangen, Restprogramm ausführen...&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.&amp;amp;nbsp;B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_sprintf_und_printf Die Nutzung von sprintf und printf]&lt;br /&gt;
* [http://www.peterfleury.epizy.com/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein Text zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Textes zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, den Benutzer die Eingabe eines Textes mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das Text Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von sizeof() ist allerdings zu beachten, dass sizeof() nicht die Anzahl der Elemente des Arrays liefert, sondern die Länge in Byte. Da ein char nur ein Byte lang ist, passt der Aufruf &#039;uart_gets(Line, sizeof( Line ) );&#039; in diesem Fall. Falls man - aus welchen Gründen auch immer - andere Datentypen benutzen möchte, sollte man zur korrekten Angabe der Array-Länge folgende Vorgehensweise bevorzugen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  int Line[40];      // Array vom Typ int&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) / sizeof( Line[0] ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Hier wird das Grundwissen des Artikels [[Interrupt]] und des Abschnitts [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts|AVR-GCC-Tutorial: Programmieren_mit_Interrupts]] vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Empfangen (RX) ===&lt;br /&gt;
&lt;br /&gt;
Beim ATmega8 muss das &#039;&#039;&#039;RXCIE&#039;&#039;&#039; Bit im Register UCSRB gesetzt werden, damit ein Interrupt beim Empfang eines Zeichens ausgelöst werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* siehe auch obere Baudrateneinstellung */&lt;br /&gt;
/* USART-Init beim ATmega16 */&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
  UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0); // Asynchron 8N1 &lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;RXEN)|(1&amp;lt;&amp;lt;TXEN)|(1&amp;lt;&amp;lt;RXCIE);  // UART RX, TX und RX Interrupt einschalten&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; mittels des Befehls sei() aktiviert sein. Interrupt-spezifische Definitionen werden über die Includedatei eingebunden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird immer ausgelöst, wenn ein Zeichen erfolgreich empfangen wurde. Zusätzlich braucht man die Interruptserviceroutine (ISR). &lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel enthält die ISR einen FIFO-Puffer (First in, First out). Dafür werden ein paar globale Variablen und Makros benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define UART_MAXSTRLEN 10&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t uart_str_complete = 0;     // 1 .. String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_str_count = 0;&lt;br /&gt;
volatile char uart_string[UART_MAXSTRLEN + 1] = &amp;quot;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(USART_RXC_vect)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char nextChar;&lt;br /&gt;
&lt;br /&gt;
  // Daten aus dem Puffer lesen&lt;br /&gt;
  nextChar = UDR;&lt;br /&gt;
  if( uart_str_complete == 0 ) {	// wenn uart_string gerade in Verwendung, neues Zeichen verwerfen&lt;br /&gt;
&lt;br /&gt;
    // Daten werden erst in uart_string geschrieben, wenn nicht String-Ende/max Zeichenlänge erreicht ist/string gerade verarbeitet wird&lt;br /&gt;
    if( nextChar != &#039;\n&#039; &amp;amp;&amp;amp;&lt;br /&gt;
        nextChar != &#039;\r&#039; &amp;amp;&amp;amp;&lt;br /&gt;
        uart_str_count &amp;lt; UART_MAXSTRLEN ) {&lt;br /&gt;
      uart_string[uart_str_count] = nextChar;&lt;br /&gt;
      uart_str_count++;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      uart_string[uart_str_count] = &#039;\0&#039;;&lt;br /&gt;
      uart_str_count = 0;&lt;br /&gt;
      uart_str_complete = 1;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Funktion: Wurde eine komplette Zeichenkette empfangen, also das Ende (\n oder \r) erkannt oder die maximale Länge &#039;&#039;UART_MAXSTRLEN&#039;&#039; erreicht, wird die globale Variable &#039;&#039;uart_str_complete&#039;&#039; auf &#039;1&#039; gesetzt. Damit wird dem Hauptprogramm, welches auf diese Variable pollt, mitgeteilt, dass die Zeichenkette &#039;&#039;uart_string&#039;&#039; zur Verarbeitung bereit steht. Nach der Verarbeitung der Zeichenkette in der entsprechenden main-Routine, muss die Variable &#039;&#039;uart_str_complete&#039;&#039; wieder auf &#039;0&#039; zurück gesetzt werden. Dadurch werden alle neu empfangenen Zeichen wieder in den globalen Puffer geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== (Baustelle) ===&lt;br /&gt;
&lt;br /&gt;
* Empfangen (Receive) (Anm.: z.T. erledigt)&lt;br /&gt;
** ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!])&lt;br /&gt;
** Komplettes, einfaches Beispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) [http://www.mikrocontroller.net/topic/101472#882716]&lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
&lt;br /&gt;
* FIFO-Puffer [http://www.mikrocontroller.net/topic/101472#882716], Ringpuffer (Byte Buffering (circular))&lt;br /&gt;
&lt;br /&gt;
* UART-Bibliotheken &lt;br /&gt;
** [http://www.peterfleury.epizy.com/avr-software.html UART-Library] von Peter Fleury (UART (interrupt driven), Byte Buffering (circular))&lt;br /&gt;
** [http://beaststwo.org/avr-uart/index.shtml Updated AVR UART Library]  (modernisierte und erweiterte Version der Bibliothek von Peter Fleury)&lt;br /&gt;
** [http://www.procyonengineering.com/embedded/avr/avrlib/ Procyon AVRlib] von Pascal Stang (UART (interrupt driven), Byte Buffering (circular), VT100 Terminal Output)&lt;br /&gt;
** [http://die-seite.ch/public/uart_driver.c uart_driver.c] Beispieltreiber in C mit minimalstem Code fuer ATMega8. Sendet und empfaengt Strings ueber Interrupts (von Markus Zimmermann)&lt;br /&gt;
** [https://github.com/crapp/uartavr uartavr] BSD-3-Clause Library von Christian Rapp für ATmega328. Interrupt driven und Byte Buffering (circular), [https://crapp.github.io/uartavr/uart_8h.html API Dokumentation]&lt;br /&gt;
&lt;br /&gt;
* Literatur &lt;br /&gt;
** [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.&amp;amp;nbsp;B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://www.procyonengineering.com/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.&amp;amp;nbsp;B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Stattdessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, das Senden einzustellen bzw. wieder aufzunehmen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist, dass dadurch keine direkte binäre Datenübertragung mehr möglich ist, denn von den möglichen 256 Bytewerten werden ja nun zwei (nämlich für &#039;&#039;&#039;XON&#039;&#039;&#039; und für &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus. In einem quasi-zufälligen Bytestrom, den eine Binärdatei darstellt, kämen diese beiden reservierten früher oder später vor und würden dann den Strom ungewollt stoppen und starten.&lt;br /&gt;
&lt;br /&gt;
==Galvanische Trennung==&lt;br /&gt;
Für eine hohe Überspannungsfestigkeit empfielt es sich, die Datenkanäle über Optokoppler zu führen. Es bietet sich z.b. der 6N138 an, ein &amp;quot;normaler&amp;quot; CNY-17 ist für hohe Baudraten nicht brauchbar.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Hilfreich zum Aufspüren solcher Fehler ist auch die [[AVR_Checkliste#UART/USART|AVR-Checkliste]].&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
Tipps zur Verarbeitung von Strings sind in den [[FAQ]].&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial]]&lt;br /&gt;
[[Kategorie:UART und RS232]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=101261</id>
		<title>AVR-GCC-Tutorial/Der UART</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=101261"/>
		<updated>2019-10-30T21:21:55Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* (Baustelle) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Über den [[UART]] kann ein [[AVR]] leicht mit einer [[RS-232]]-Schnittstelle eines PC oder sonstiger Geräte mit &amp;quot;[[seriell]]er Schnittstelle&amp;quot; verbunden werden. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines zum UART ==&lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungen des UART:&lt;br /&gt;
&lt;br /&gt;
;Debug-Schnittstelle: z.&amp;amp;nbsp;B. zur Anzeige von Zwischenergebnissen (&amp;quot;printf-debugging&amp;quot; - hier besser &amp;quot;Logging&amp;quot; oder &amp;quot;UART-debugging&amp;quot;) über [[RS-232]] auf einem PC. Auf dem Rechner reicht dazu ein [[RS-232#Terminalprogramme|Terminalprogramm]] (MS-Windows: Hyperterm oder besser [https://sites.google.com/site/terminalbpp/], [http://www.der-hammer.info/terminal/ HTerm]; Unix/Linux z.&amp;amp;nbsp;B. minicom). Ein direkter Anschluss ist aufgrund unterschiedlicher Pegel nicht möglich, jedoch sind entsprechende Schnittstellen-ICs wie z.&amp;amp;nbsp;B. ein MAX232 günstig und leicht zu integrieren. Rechner ohne serielle Schnittstelle können über fertige USB-seriell-Adapter angeschlossen werden. &lt;br /&gt;
;Mensch-Maschine Schnittstelle: z.&amp;amp;nbsp;B. Konfiguration und Statusabfrage über eine &amp;quot;Kommandozeile&amp;quot; oder Menüs (siehe z.&amp;amp;nbsp;B. Forumsbeitrag [http://www.mikrocontroller.net/topic/52985 Auswertung RS232-Befehle] und Artikel [[Tinykon]]) &lt;br /&gt;
;Übertragen von gespeicherten Werten: z.&amp;amp;nbsp;B. bei einem Datenlogger&lt;br /&gt;
;Anschluss von Geräten: mit serieller Schnittstelle (z.&amp;amp;nbsp;B. (Funk-)Modems, Mobiltelefone, Drucker, Sensoren, &amp;quot;intelligente&amp;quot; LC-Displays, GPS-Empfänger). &lt;br /&gt;
;&amp;quot;Feldbusse&amp;quot;: auf RS485/RS422-Basis mittels entsprechenden Bustreiberbausteinen (z.&amp;amp;nbsp;B. MAX485)&lt;br /&gt;
;DMX, Midi: etc.&lt;br /&gt;
;LIN-Bus: &#039;&#039;&#039;L&#039;&#039;&#039;ocal &#039;&#039;&#039;I&#039;&#039;&#039;nterconnect &#039;&#039;&#039;N&#039;&#039;&#039;etwork: Preiswerte Sensoren/Aktoren in der Automobiltechnik und darüber hinaus&lt;br /&gt;
&lt;br /&gt;
Einige AVR-Controller haben ein bis zwei vollduplexfähige UART (&#039;&#039;&#039;U&#039;&#039;&#039;niversal &#039;&#039;&#039;A&#039;&#039;&#039;synchronous &#039;&#039;&#039;R&#039;&#039;&#039;eceiver and &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter) schon eingebaut (&amp;quot;Hardware-UART&amp;quot;). &lt;br /&gt;
Übrigens: Vollduplex heißt nichts anderes, als dass der Baustein gleichzeitig senden und empfangen kann.&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (ATmega, ATtiny) verfügen über einen bis vier U&#039;&#039;&#039;S&#039;&#039;&#039;ART(s), dieser unterscheidet sich vom UART hauptsächlich durch interne FIFO-Puffer für Ein- und Ausgabe und erweiterte Konfigurationsmöglichkeiten. Die Puffergröße ist allerdings nur 1 Byte.&lt;br /&gt;
&lt;br /&gt;
== Die Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der UART basiert auf normalem TTL-Pegel mit 0V (logisch 0) und 5V (logisch 1). Die Schnittstellenspezifikation für RS-232 definiert jedoch -12V ... -3V (logisch 1) und&lt;br /&gt;
+3 ... +12V (logisch 0). Daher muss der Signalaustausch zwischen AVR und Partnergerät invertiert werden. Für die Anpassung der Pegel und das Invertieren der Signale gibt es fertige Schnittstellenbausteine. Der bekannteste davon ist wohl der MAX232. &lt;br /&gt;
&amp;lt;!-- &amp;quot;Hackerloesung&amp;quot; auskommentiert - nicht so gut in einem &amp;quot;Einsteiger-Tutorial&amp;quot; - mthomas&lt;br /&gt;
Allerdings kostet der auch wieder Geld und benötigt&lt;br /&gt;
zusätzlich immerhin 4 externe Elkos.&lt;br /&gt;
&lt;br /&gt;
Die in den PC eingebauten Schnittstellen vertragen ohne Klagen auch den TTL-Pegel vom AVR. Allerdings müssen wir immer noch die Signale invertieren. Im einfachtesn Fall verwenden wir dazu jeweils einen einfachen NPN-Transistor und 2 Widerstände. Näheres dazu erfahrt ihr in den folgenden Übungen.--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Streikt die Kommunikation per UART, so ist oft eine fehlerhafte Einstellung der Baudrate die Ursache. Die Konfiguration auf eine bestimmte Baudrate ist abhängig von der Taktfrequenz des Controllers. Gerade bei neu aufgebauten Schaltungen (bzw. neu gekauften Controllern) sollte man sich daher noch einmal vergewissern, dass der Controller auch tatsächlich mit der vermuteten Taktrate arbeitet und nicht z.&amp;amp;nbsp;B. den bei einigen Modellen werksseitig eingestellten internen [[Oszillator]] statt eines externen Quarzes nutzt. Die Werte der verschiedenen fuse-bits im Fehlerfall also beispielsweise mit &#039;&#039;[[AVRDUDE]]&#039;&#039; kontrollieren und falls nötig anpassen. Grundsätzlich empfiehlt sich auch immer ein Blick in die [[AVR_Checkliste]].&lt;br /&gt;
&lt;br /&gt;
Controller mit nur einem Quarzanschluss (z.B Atmega328), die den Timer2 im Asynchron Modus benutzen, müssen intern mit dem RC-Oszillator getaktet werden. Dieser sollte dann kalibriert werden:&lt;br /&gt;
[[AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR#Kalibrieren_des_internen_Oszillators_mit_Timer2_als_Zeitbasis|Kalibrieren des internen Oszillators mit Timer2 als Zeitbasis]]&lt;br /&gt;
&lt;br /&gt;
== Die UART-Register ==&lt;br /&gt;
&lt;br /&gt;
Die UART wird über vier separate Register angesprochen. Die USARTs der ATMEGAs verfügen über mehrere zusätzliche Konfigurationsregister. Das Datenblatt gibt darüber Auskunft. Die folgende Tabelle gibt nur die Register für die UARTs der ATmega8/16/32 u.ä. wieder.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| width=&amp;quot;100&amp;quot; | &#039;&#039;&#039;UCSRA&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;A&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier teilt uns der UART mit, was er gerade so macht.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;RXC&#039;&#039;&#039;|| &#039;&#039;&#039;TXC&#039;&#039;&#039;|| &#039;&#039;&#039;UDRE&#039;&#039;&#039;|| &#039;&#039;&#039;FE&#039;&#039;&#039;|| &#039;&#039;&#039;DOR&#039;&#039;&#039;|| &#039;&#039;&#039;PE&#039;&#039;&#039;|| &#039;&#039;&#039;U2X&#039;&#039;&#039;|| &#039;&#039;&#039;MPCM&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R|| R/W|| R|| R|| R|| R|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 1|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXC&#039;&#039;&#039; (UART Receive Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn ein empfangenes Zeichen vom Empfangs-Schieberegister in das Empfangs-Datenregister transferiert wurde.&lt;br /&gt;
:Das Zeichen muss nun schnellstmöglich aus dem Datenregister ausgelesen werden. Falls dies nicht erfolgt, bevor ein weiteres Zeichen komplett empfangen wurde, wird eine Überlauf-Fehlersituation eintreten. Mit dem Auslesen des Datenregisters wird das Bit automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXC&#039;&#039;&#039; (UART Transmit Complete)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Sende-Schieberegister befindliche Zeichen vollständig ausgegeben wurde und kein weiteres Zeichen im Sendedatenregister ansteht. Dies bedeutet also, dass die Kommunikation vollumfänglich abgeschlossen ist.&lt;br /&gt;
:Dieses Bit ist wichtig bei Halbduplex-Verbindungen, wenn das Programm nach dem Senden von Daten auf Empfang schalten muss. Im Vollduplexbetrieb brauchen wir dieses Bit nicht zu beachten.&lt;br /&gt;
:Das Bit wird nur dann automatisch gelöscht, wenn der entsprechende Interrupthandler aufgerufen wird, ansonsten müssen wir das Bit selber löschen.&lt;br /&gt;
:Um das Bit zu löschen muss eine 1 an die entsprechende Position geschrieben werden!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty)&lt;br /&gt;
:Dieses Bit zeigt an, ob der Sendepuffer bereit ist, um ein zu sendendes Zeichen aufzunehmen. Das Bit wird vom AVR gesetzt (1), wenn der Sendepuffer leer ist. Es wird gelöscht (0), wenn ein Zeichen im Sendedatenregister vorhanden ist und noch nicht in das Sende-Schieberegister übernommen wurde. Atmel empfiehlt aus Kompatibilitätsgründen mit kommenden µC, UDRE auf 0 zu setzen, wenn das UCSRA Register beschrieben wird.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn ein Zeichen in das Sendedatenregister geschrieben wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FE&#039;&#039;&#039; (&#039;&#039;&#039;F&#039;&#039;&#039;raming &#039;&#039;&#039;E&#039;&#039;&#039;rror)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn der UART einen Zeichenrahmenfehler detektiert, d.h. wenn das Stopbit eines empfangenen Zeichens 0 ist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das Stopbit des empfangenen Zeichens 1 ist.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DOR&#039;&#039;&#039; (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;O&#039;&#039;&#039;ver &#039;&#039;&#039;R&#039;&#039;&#039;un)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn unser Programm das im Empfangsdatenregister bereit liegende Zeichen nicht abholt bevor das nachfolgende Zeichen komplett empfangen wurde.&lt;br /&gt;
:Das nachfolgende Zeichen wird verworfen.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PE&#039;&#039;&#039; (&#039;&#039;&#039;P&#039;&#039;&#039;arity &#039;&#039;&#039;E&#039;&#039;&#039;rror&#039;&#039;&#039;)&lt;br /&gt;
:Dieses Bit wird vom AVR gesetzt, wenn das im Empfangsdatenregister bereit liegende Zeichen einen Paritätsfehler aufweist.&lt;br /&gt;
:Das Bit wird automatisch gelöscht, wenn das empfangene Zeichen in das Empfangsdatenregister transferiert werden konnte.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;U2X&#039;&#039;&#039; (Double the transmission speed)&lt;br /&gt;
:Dieses Bit wird lediglich im asynchronen Modus genutzt. Im synchronen Modus ist es 0 zu setzen. Wird das Bit gesetzt, so wird der Baudraten Divisor von 16 auf 8 reduziert, was einer Verdopplung der Transferrate gleich kommt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MPCM&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;ulti &#039;&#039;&#039;P&#039;&#039;&#039;rozessor &#039;&#039;&#039;C&#039;&#039;&#039;ommunication &#039;&#039;&#039;M&#039;&#039;&#039;ode)&lt;br /&gt;
:Dieses Bit aktiviert die Multi-Prozessor-Kommunikation. Jeder eintreffende Frame der keine Adressinformation enthält wird dadurch ignoriert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UCSRB &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;B&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register stellen wir ein, wie wir den UART verwenden möchten.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Register ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;RXCIE&#039;&#039;&#039;|| &#039;&#039;&#039;TXCIE&#039;&#039;&#039;|| &#039;&#039;&#039;UDRIE&#039;&#039;&#039;|| &#039;&#039;&#039;RXEN&#039;&#039;&#039;|| &#039;&#039;&#039;TXEN&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ2&#039;&#039;&#039;|| &#039;&#039;&#039;RXB8&#039;&#039;&#039;|| &#039;&#039;&#039;TXB8&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 0|| 0|| 0|| 0|| 0|| 0|| 0|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXCIE&#039;&#039;&#039; (&#039;&#039;&#039;RX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART RX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART empfangen wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXCIE&#039;&#039;&#039; (&#039;&#039;&#039;TX&#039;&#039;&#039; &#039;&#039;&#039;C&#039;&#039;&#039;omplete &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART TX Complete Interrupt ausgelöst, wenn ein Zeichen vom UART gesendet wurde. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UDRIE&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;E&#039;&#039;&#039;mpty &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Wenn dieses Bit gesetzt ist, wird ein UART Datenregister Leer Interrupt ausgelöst, wenn der UART wieder bereit ist um ein neues zu sendendes Zeichen zu übernehmen. Das Global Enable Interrupt Flag muss selbstverständlich auch gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXEN&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;eceiver &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Empfänger des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXEN&#039;&#039;&#039; (&#039;&#039;&#039;T&#039;&#039;&#039;ransmitter &#039;&#039;&#039;E&#039;&#039;&#039;nable)&lt;br /&gt;
:Nur wenn dieses Bit gesetzt ist, arbeitet der Sender des UART überhaupt. Wenn das Bit nicht gesetzt ist, kann der entsprechende Pin des AVR als normaler I/O-Pin verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCSZ2&#039;&#039;&#039; (Characters Size)&lt;br /&gt;
:Dieses Bit setzt in Verbindung mit dem UCSZ1:0 Bits im UCSRC Register die Anzahl von Datenbits eines Frames beim Empfang oder Senden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RXB8&#039;&#039;&#039; (Receive Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann enthält dieses Bit das 9. Datenbit eines empfangenen Zeichens.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TXB8&#039;&#039;&#039; (Transmit Data Bit 8)&lt;br /&gt;
:Wenn das vorher erwähnte CHR9-Bit gesetzt ist, dann muss in dieses Bit das 9. Bit des zu sendenden Zeichens eingeschrieben werden bevor das eigentliche Datenbyte in das Datenregister geschrieben wird.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UCSRC &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol and &#039;&#039;&#039;S&#039;&#039;&#039;tatus &#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;C&#039;&#039;&#039;.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
! Bit&lt;br /&gt;
| 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0&lt;br /&gt;
|- &lt;br /&gt;
! Name&lt;br /&gt;
| &#039;&#039;&#039;URSEL&#039;&#039;&#039;|| &#039;&#039;&#039;UMSEL&#039;&#039;&#039;|| &#039;&#039;&#039;UPM1&#039;&#039;&#039;|| &#039;&#039;&#039;UPM0&#039;&#039;&#039;|| &#039;&#039;&#039;USBS&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ1&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ0&#039;&#039;&#039;|| &#039;&#039;&#039;UCPOL&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
! R/W&lt;br /&gt;
| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W|| R/W&lt;br /&gt;
|- &lt;br /&gt;
! Initialwert&lt;br /&gt;
| 1|| 0|| 0|| 0|| 0|| 1|| 1|| 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;URSEL&#039;&#039;&#039; (Register Select)&lt;br /&gt;
:Dieses Bit selektiert die Auswahl des UCSRC- bzw. des UBRRH Registers. Beim Lesen von UCSRC wird es als 1 gelesen. Beim Schreiben auf UCSRC muss es auf 1 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
:Achtung: Manche Prozessoren verfügen über dieses Bit, andere wiederrum nicht. Was hat es damit auf sich?&amp;lt;br&amp;gt;Um Zugriffsadressen einzusparen, wurde von Atmel ein etwas seltsamer Weg gewählt. Das UCSRC Register und das High-Byte des Baudratenregisters teilen sich dieselbe Registeradresse! Um der Hardware mitzuteilen, welche Bedeutung ein zugewiesener Wert haben soll, entweder neue Belegung des Baudratenregisters oder eben Konfiguration des UCSRC Registers, dient dieses Bit. Ist es nicht gesetzt, dann wird eine Zuweisung immer als Zuweisung an das High-Byte des Baudratenregisters angesehen, selbst wenn das so nicht beabsichtigt war. Nur dann wenn dieses Bit gesetzt ist, dann wird eine Zuweisung auch tatsächlich als eine Zuweisung an das UCSRC Register gewertet und die Konfiguration verändert. Lässt man das Bit irrtümlich weg, dann verursacht eine Zuweisung an UCSRC eine Veränderung der Baudrateneinstellung!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UMSEL&#039;&#039;&#039; (USART Mode Select)&lt;br /&gt;
:Durch dieses Bit kann eine asynchrone oder synchrone Übertragung eingestellt werden. Durch Setzen des Bits wird eine synchrone Übertragung eingestellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UPM1:0&#039;&#039;&#039; (Parity Mode)&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UPM1&#039;&#039;&#039;|| &#039;&#039;&#039;UPM0&#039;&#039;&#039;|| &#039;&#039;&#039;Parity Mode&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| Disabled&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| Enabled, Even Priority&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| Enabled, OddPriority&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;USBS&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;SART &#039;&#039;&#039;S&#039;&#039;&#039;top &#039;&#039;&#039;B&#039;&#039;&#039;it &#039;&#039;&#039;S&#039;&#039;&#039;elect)&lt;br /&gt;
:Diese Bits setzen die Anzahl der zu sendenden Stopbits eines Frames. Beim Setzen werden 2 Stopbits übertragen, andernfalls nur 1 Stopbit.&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;USBS&#039;&#039;&#039;|| &#039;&#039;&#039;Anzahl der Stop Bits&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|  0|| 1&lt;br /&gt;
|- &lt;br /&gt;
|  1|| 2&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCSZ1:0&#039;&#039;&#039; (Character Size)&lt;br /&gt;
:Diese Bits setzen in Verbindung mit UCSZ2 aus dem UCSRB Register die Anzahl der Datenbits eines Frames.&lt;br /&gt;
:Diese Bits setzen den entsprechenden Paritätsmodus.&lt;br /&gt;
{|  class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UCSZ2&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ1&#039;&#039;&#039;|| &#039;&#039;&#039;UCSZ0&#039;&#039;&#039;|| &#039;&#039;&#039;Character Size&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| 0|| 5-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 0|| 1|| 6-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| 0|| 7-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 0|| 1|| 1|| 8-Bit&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| 0|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 0|| 1|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| 0|| Reserved&lt;br /&gt;
|- &lt;br /&gt;
| 1|| 1|| 1|| 9-Bit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;UCPOL&#039;&#039;&#039; (Clock Polarity)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UDR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
Hier werden Daten zwischen UART und CPU übertragen. Da der UART im&lt;br /&gt;
Vollduplexbetrieb gleichzeitig empfangen und senden kann, handelt es sich&lt;br /&gt;
hier physikalisch um 2 Register, die aber über die gleiche I/O-Adresse&lt;br /&gt;
angesprochen werden. Je nachdem, ob ein Lese- oder ein Schreibzugriff auf&lt;br /&gt;
den UART erfolgt wird automatisch das richtige UDR angesprochen.&lt;br /&gt;
|- &lt;br /&gt;
| &#039;&#039;&#039;UBRR&#039;&#039;&#039;&lt;br /&gt;
| &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;B&#039;&#039;&#039;aud &#039;&#039;&#039;R&#039;&#039;&#039;ate &#039;&#039;&#039;R&#039;&#039;&#039;egister.&amp;lt;br /&amp;gt;&lt;br /&gt;
In diesem Register müssen wir dem UART mitteilen, wie schnell wir gerne kommunizieren möchten. Der Wert, der in dieses Register geschrieben werden muss, errechnet sich nach folgender Formel (wenn U2X Bit 0 gesetzt ist):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
\mathrm{UBRR} = \frac{\mathrm{Taktfrequenz}}{\mathrm{Baudrate} \cdot 16} - 1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sind Baudraten bis über 115200 Baud möglich, je nach Controller und CPU-Frequenz. Siehe Datenblatt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== UART initialisieren ==&lt;br /&gt;
&lt;br /&gt;
Wir wollen nun Daten mit dem UART auf die serielle Schnittstelle ausgeben. Dazu müssen wir den UART zuerst mal initialisieren. Dazu setzen wir je nach gewünschter Funktionsweise die benötigten Bits im &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister.&lt;br /&gt;
&lt;br /&gt;
Da wir vorerst nur senden möchten und noch keine Interrupts auswerten wollen, gestaltet sich die Initialisierung wirklich sehr einfach, da wir lediglich das &#039;&#039;&#039;Transmitter Enable&#039;&#039;&#039; Bit setzen müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs mit USART haben mehrere Konfigurationsregister und erfordern eine etwas andere Konfiguration. Für einen ATmega16 z.&amp;amp;nbsp;B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);                           // UART TX einschalten&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1 &amp;lt;&amp;lt; UCSZ1)|(1 &amp;lt;&amp;lt; UCSZ0); // Asynchron 8N1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun ist noch das Baudratenregister &#039;&#039;&#039;UBRR&#039;&#039;&#039; der verwendeten UARTs einzustellen, bzw. bei neueren AVRs die beiden Register &#039;&#039;&#039;UBRRL&#039;&#039;&#039; und &#039;&#039;&#039;UBRRH&#039;&#039;&#039;. &amp;lt;!--Der Wert dafür ergibt sich aus der unten angegebenen Formel durch Einsetzen der Taktfrequenz und der gewünschten Übertragungsrate. Das Berechnen der Formel wird dem [[C-Präprozessor|Präprozessor]] überlassen. Dabei ist zu beachten, dass der Präprozessor keine Floating Point Ergebnisse liefert, sondern Integer Ergebnisse. Das bedeutet, dass wenn durch Division auf einen Integer Wert 1.99 zugewiesen wird, 0.99 abgeschnitten werden und das Ergebnis 1.0 ist - obwohl 2.0 viel näher wäre.--&amp;gt;. Die Berechnung wird während des Compilerlaufs ausgeführt, beansprucht also in der gezeigten Form weder Speicher noch Rechenzeit des Controllers. Das Ergebniss wird jedoch als ganzzahliger Wert eingesetzt, d.h. Nachkommastellen werden einfach abgeschnitten und es erfolgt keine Rundung. Aus diesem Grund kann man sich eines kleinen Tricks bedienen, indem vor der eigentlichen Division bei der Zuweisung die Hälfte des Wertes dazu addiert wird. Allgemein formuliert bedeutet das: &amp;lt;code&amp;gt;int i = ( a + b/2 ) / b;&amp;lt;/code&amp;gt;. Dies wird in der unten angegebenen Berechnung von UBRR_VAL ausgenutzt um den Fehler zu minimieren. (Eine ausführliche Erklärung zum &#039;&#039;cleveren Runden&#039;&#039; findet sich in einer [http://www.mikrocontroller.net/topic/170617#1631916 Forumsdiskussion].) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
  UART-Init: &lt;br /&gt;
Berechnung des Wertes für das Baudratenregister &lt;br /&gt;
aus Taktrate und gewünschter Baudrate&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
/* In neueren Version der WinAVR/Mfile Makefile-Vorlage kann&lt;br /&gt;
   F_CPU im Makefile definiert werden, eine nochmalige Definition&lt;br /&gt;
   hier wuerde zu einer Compilerwarnung fuehren. Daher &amp;quot;Schutz&amp;quot; durch&lt;br /&gt;
   #ifndef/#endif &lt;br /&gt;
&lt;br /&gt;
   Dieser &amp;quot;Schutz&amp;quot; kann zu Debugsessions führen, wenn AVRStudio &lt;br /&gt;
   verwendet wird und dort eine andere, nicht zur Hardware passende &lt;br /&gt;
   Taktrate eingestellt ist: Dann wird die folgende Definition &lt;br /&gt;
   nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) &lt;br /&gt;
   von AVRStudio - daher Ausgabe einer Warnung falls F_CPU&lt;br /&gt;
   noch nicht definiert: */&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000&amp;quot;&lt;br /&gt;
#define F_CPU 4000000UL  // Systemtakt in Hz - Definition als unsigned long beachten &lt;br /&gt;
                         // Ohne ergeben sich unten Fehler in der Berechnung&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600UL      // Baudrate&lt;br /&gt;
&lt;br /&gt;
// Berechnungen&lt;br /&gt;
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate&lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.&lt;br /&gt;
&lt;br /&gt;
#if ((BAUD_ERROR&amp;lt;990) || (BAUD_ERROR&amp;gt;1010))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Makros sind sehr praktisch, da damit sowohl automatisch der Wert für UBRR, als auch die Abweichung in der generierten (möglichen) von der gewünschten Baudrate berechnet wird. Im Falle einer zu hohen Abweichung (+/-1%) wird eine Fehlermeldung ausgegeben und der Compilerablauf abgebrochen. Damit können viele Probleme mit &amp;quot;UART sendet komische Zeichen&amp;quot; vermieden werden. Ausserdem kann man mühelos die Einstellung an eine neue Taktfrequenz bzw. Baudrate anpassen, ohne selber rechnen oder in Tabellen nachschlagen zu müssen.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Initialisierung der UART Register kann im Hauptprogramm main() vorgenommen werden. Öfters wird jedoch eine Funktion z.&amp;amp;nbsp;B. uart_init() dafür geschrieben, die in der eigenen Codesammlung in mehreren Projekten verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
Für einige AVR (z.&amp;amp;nbsp;B. ATmega169, ATmega48/88/168, AT90CAN jedoch nicht für z.&amp;amp;nbsp;B. ATmega16/32, ATmega128, ATtiny2313) wird durch die Registerdefinitionen der avr-libc (io*.h) auch für Controller mit zwei UBRR-Registern (UBRRL/UBRRH) ein UBRR bzw. UBRR0 als &amp;quot;16-bit-Register&amp;quot; definiert und man kann den Wert direkt per UBRR = UBRR_VAL zuweisen. Intern werden dann zwei Zuweisungen für UBRRH und UBRRL generiert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init Bsp. ATmega48 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRR0 = UBRR_VAL;&lt;br /&gt;
  UCSR0B |= (1&amp;lt;&amp;lt;TXEN0);&lt;br /&gt;
  // Frame Format: Asynchron 8N1&lt;br /&gt;
  UCSR0C = (1&amp;lt;&amp;lt;UCSZ01)|(1&amp;lt;&amp;lt;UCSZ00);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die einzelne Anweisung ist nicht bei allen Controllern möglich, da die beiden Register nicht bei allen aufeinanderfolgende Addressen aufweisen. Die getrennte Zuweisung an UBRRH und UBRRL wie im nächsten Beispiel gezeigt, ist universeller und portabler und daher vorzuziehen. Wichtig ist, dass UBRRH &#039;&#039;&#039;vor&#039;&#039;&#039; UBRRL geschrieben wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* UART-Init Bsp. ATmega16 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
  UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);  // UART TX einschalten&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0);  // Asynchron 8N1 &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inzwischen gibt es in der avr-libc Makros für obige Berechnung der UBRR Registerwerte aus Taktrate F_CPU und Baudrate BAUD. Dazu wird die Includedatei  [http://www.nongnu.org/avr-libc/user-manual/group__util__setbaud.html &amp;lt;util/setbaud.h&amp;gt;] eingebunden, nachdem F_CPU und die gewünschte Baudrate definiert wurden. Einige Beispiele zur Anwendung finden sich in der Dokumentation der avr-libc. Im Quellcode kann dann analog zur oben gezeigten Vorgehensweise einfach das Makro UBRR_VALUE (bzw. UBRRH_VALUE und UBRRL_VALUE) an der entsprechenden Stelle eingesetzt werden. Es wird auch automatisch ermittelt, ob der U2X-Modus (vgl. Datenblatt) zu geringeren Abweichungen führt und dann dem Makro USE_U2X ein Wert ungleich null zugewiesen. Ein Beispiel (angelehnt an die avr-libc-Dokumentation):&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;
#define F_CPU 1000000 /* evtl. bereits via Compilerparameter definiert */&lt;br /&gt;
#define BAUD 9600&lt;br /&gt;
#include &amp;lt;util/setbaud.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)   &lt;br /&gt;
{&lt;br /&gt;
   UBRRH = UBRRH_VALUE;&lt;br /&gt;
   UBRRL = UBRRL_VALUE;&lt;br /&gt;
   /* evtl. verkuerzt falls Register aufeinanderfolgen (vgl. Datenblatt)&lt;br /&gt;
      UBRR = UBRR_VALUE;&lt;br /&gt;
   */&lt;br /&gt;
#if USE_2X&lt;br /&gt;
   /* U2X-Modus erforderlich */&lt;br /&gt;
   UCSRA |= (1 &amp;lt;&amp;lt; U2X);&lt;br /&gt;
#else&lt;br /&gt;
   /* U2X-Modus nicht erforderlich */&lt;br /&gt;
   UCSRA &amp;amp;= ~(1 &amp;lt;&amp;lt; U2X);&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
   // hier weitere Initialisierungen (TX und/oder RX aktivieren, Modus setzen &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__util__setbaud.html Dokumentation der avr-libc zu &amp;lt;util/setbaud.h&amp;gt;]&lt;br /&gt;
* [http://www.wormfood.net/avrbaudcalc.php WormFood&#039;s AVR Baud Rate Calculator] online.&lt;br /&gt;
* [http://www.gjlay.de/helferlein/avr-uart-rechner.html AVR Baudraten-Rechner] in JavaScript&lt;br /&gt;
&lt;br /&gt;
== Senden mit dem UART ==&lt;br /&gt;
&lt;br /&gt;
=== Senden einzelner Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Um nun ein Zeichen auf die Schnittstelle auszugeben, müssen wir dasselbe&lt;br /&gt;
lediglich in das &#039;&#039;&#039;U&#039;&#039;&#039;ART &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister schreiben. Vorher ist zu prüfen, ob das UART-Modul bereit ist, das zu sendende Zeichen entgegenzunehmen. Die Bezeichnungen des/der Statusregisters mit dem Bit UDRE ist abhängig vom Controllertypen (vgl. Datenblatt).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.B. fuer ATmega16:&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich                   */&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    UDR = &#039;x&#039;;                    /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
&lt;br /&gt;
// das entpricht dem &amp;quot;C Code Example USART_Transmit&amp;quot; in der ATMEL-Doku ...&lt;br /&gt;
// besser ist es aber, die CPU nicht in einer Warteschleife zu blockieren:&lt;br /&gt;
&lt;br /&gt;
    if(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE))         /* Senden, wenn UDR frei ist                    */&lt;br /&gt;
    {    UDR = &#039;x&#039;;               /* schreibt das Zeichen x auf die Schnittstelle */&lt;br /&gt;
    }   &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Zum &amp;quot;Blockieren&amp;quot; vgl. [[AVR-GCC-Tutorial#Warten_auf_einen_bestimmten_Zustand|AVR-GCC-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
=== Senden einer Zeichenkette (String) ===&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe &amp;quot;String senden&amp;quot; wird durch zwei Funktionen abgearbeitet. Die universelle/controllerunabhängige Funktion uart_puts übergibt jeweils ein Zeichen der Zeichenkette an eine Funktion uart_putc, die abhängig von der vorhandenen Hardware implementiert werden muss. In der Funktion zum Senden eines Zeichens ist darauf zu achten, dass vor dem Senden geprüft wird, ob der UART bereit ist den &amp;quot;Sendeauftrag&amp;quot; entgegenzunehmen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* ATmega16 */&lt;br /&gt;
int uart_putc(unsigned char c)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)))  /* warten bis Senden moeglich */&lt;br /&gt;
    {&lt;br /&gt;
    }                             &lt;br /&gt;
&lt;br /&gt;
    UDR = c;                      /* sende Zeichen */&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* puts ist unabhaengig vom Controllertyp */&lt;br /&gt;
void uart_puts (char *s)&lt;br /&gt;
{&lt;br /&gt;
    while (*s)&lt;br /&gt;
    {   /* so lange *s != &#039;\0&#039; also ungleich dem &amp;quot;String-Endezeichen(Terminator)&amp;quot; */&lt;br /&gt;
        uart_putc(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die in uart_putc verwendeten Schleifen, in denen gewartet wird bis die UART-Hardware zum senden bereit ist, sind insofern etwas kritisch, da während des Sendens eines Strings nicht mehr auf andere Ereignisse reagiert werden kann. Universeller ist die Nutzung von FIFO(first-in first-out)-Puffern, in denen die zu sendenden bzw. empfangenen Zeichen/Bytes zwischengespeichert und in Interruptroutinen an die U(S)ART-Hardware weitergegeben bzw. von ihr ausgelesen werden. Dazu existieren fertige Komponenten (Bibliotheken, Libraries), die man recht einfach in eigene Entwicklungen integrieren kann. Es empfiehlt sich, diese Komponenten zu nutzen und das Rad nicht neu zu erfinden.&lt;br /&gt;
&lt;br /&gt;
=== Senden von Variableninhalten ===&lt;br /&gt;
&lt;br /&gt;
Sollen Inhalte von Variablen (Ganzzahlen, Gleitkomma) in &amp;quot;menschenlesbarer&amp;quot; Form gesendet werden, ist vor dem Transfer eine Umwandlung in Zeichen (&amp;quot;ASCII&amp;quot;) erforderlich. Bei nur einer Ziffer ist diese Umwandlung relativ einfach: man addiert den ASCII-Wert von Null zur Ziffer und kann diesen Wert direkt senden.&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;stdlib.h&amp;gt;&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_putc (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main (void) &lt;br /&gt;
{&lt;br /&gt;
   // Ausgabe von 0123456789&lt;br /&gt;
   char c;&lt;br /&gt;
&lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   for (uint8_t i=0; i&amp;lt;=9; ++i) {&lt;br /&gt;
      c = i + &#039;0&#039;;&lt;br /&gt;
      uart_putc( c );&lt;br /&gt;
      // verkuerzt: uart_putc( i + &#039;0&#039; );&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      &lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll mehr als eine Ziffer ausgegeben werden, bedient man sich zweckmäßigerweise vorhandener Funktionen zur Umwandlung von Zahlen in Zeichenketten/Strings. Die Funktion der avr-libc zur Umwandlung von vorzeichenbehafteten 16bit-Ganzzahlen (int16_t) in Zeichenketten heißt &#039;&#039;itoa&#039;&#039; (Integer to ASCII). Man muss der Funktion einen Speicherbereich zur Verarbeitung (buffer) mit Platz für alle Ziffern, das String-Endezeichen (&#039;\0&#039;) und evtl. das Vorzeichen bereitstellen.&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   char s[7];&lt;br /&gt;
   int16_t i = -12345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   itoa( i, s, 10 ); // 10 fuer radix -&amp;gt; Dezimalsystem&lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
&lt;br /&gt;
   // da itoa einen Zeiger auf den Beginn von s zurueckgibt verkuerzt auch:&lt;br /&gt;
   uart_puts( itoa( i, s, 10 ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für vorzeichenlose 16bit-Ganzzahlen (uint16_t) exisitert &#039;&#039;utoa&#039;&#039;. Die Funktionen für 32bit-Ganzzahlen (int32_t und uint32_t) heißen &#039;&#039;ltoa&#039;&#039; bzw. &#039;&#039;ultoa&#039;&#039;. Da 32bit-Ganzzahlen mehr Stellen aufweisen können, ist ein entsprechend größerer Pufferspeicher vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Auch Gleitkommazahlen (float/double) können mit bereits vorhandenen Funktionen in Zeichenfolgen umgewandelt werden, dazu existieren die Funktionen &#039;&#039;dtostre&#039;&#039; und &#039;&#039;dtostrf&#039;&#039;. dtostre nutzt Exponentialschreibweise (&amp;quot;engineering&amp;quot;-Format). (Hinweis: z.Zt. existiert im avr-gcc kein &amp;quot;echtes&amp;quot; double, intern wird immer mit &amp;quot;einfacher Genauigkeit&amp;quot;, entsprechend float, gerechnet.) &lt;br /&gt;
&lt;br /&gt;
dtostrf und dtostre benötigen die libm.a der avr-libc. Bei Nutzung von Makefiles ist der Parameter -lm in in LDFLAGS anzugeben (Standard in den WinAVR/mfile-Makefilevorlagen). Nutzt man AVRStudio als IDE für den GNU-Compiler (gcc-Plugin) ist die libm.a unter Libaries auszuwählen: Project -&amp;gt; Configurations Options -&amp;gt; Libaries -&amp;gt; libm.a mit dem Pfeil nach rechts einbinden. Siehe auch die [[FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio|FAQ]]&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//...&lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_putc, uart_puts (s.o.)&lt;br /&gt;
&lt;br /&gt;
/* lt. avr-libc Dokumentation:&lt;br /&gt;
char* dtostrf(&lt;br /&gt;
  double __val,&lt;br /&gt;
  char   __width,&lt;br /&gt;
  char   __prec,&lt;br /&gt;
  char * __s&lt;br /&gt;
)  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   // Pufferspeicher ausreichend groß&lt;br /&gt;
   // evtl. Vorzeichen + width + Endezeichen:&lt;br /&gt;
   char s[8]; &lt;br /&gt;
   float f = -12.345;&lt;br /&gt;
   &lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   dtostrf( f, 6, 3, s ); &lt;br /&gt;
   uart_puts( s );&lt;br /&gt;
   // verkürzt: uart_puts( dtostrf( f, 7, 3, s ) );&lt;br /&gt;
&lt;br /&gt;
   while (1) {&lt;br /&gt;
      ;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfangen ==&lt;br /&gt;
=== Einzelne Zeichen empfangen ===&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von Zeichen muss der Empfangsteil des UART bei der Initialisierung aktiviert werden, indem das RXEN-Bit im jeweiligen Konfigurationsregister (UCSRB bzw UCSR0B/UCSR1B) gesetzt wird. Im einfachsten Fall wird solange gewartet, bis ein Zeichen empfangen wurde, dieses steht dann im UART-Datenregister (UDR bzw. UDR0 und UDR1 bei AVRs mit 2 UARTS) zur Verfügung (sogen. &amp;quot;Polling-Betrieb&amp;quot;). Ein Beispiel für den ATmega16:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* Siehe auch obere Baudrateneinstellung */&lt;br /&gt;
/* USART-Init beim ATmega16 */&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0);  // Asynchron 8N1 &lt;br /&gt;
    UCSRB |= (1&amp;lt;&amp;lt;RXEN);                        // UART RX einschalten&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und die Anwendung in einem Beispiel:&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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// hier Makro für die Baudratenberechnung &lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_getc (s.o.)&lt;br /&gt;
 &lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uart_init();&lt;br /&gt;
  while (1) &lt;br /&gt;
  {&lt;br /&gt;
    uint8_t c;&lt;br /&gt;
    c = uart_getc();&lt;br /&gt;
&lt;br /&gt;
    // hier etwas mit c machen z.B. auf PORT ausgeben&lt;br /&gt;
    DDRC = 0xFF; // PORTC Ausgang&lt;br /&gt;
    PORTC = c;&lt;br /&gt;
  }&lt;br /&gt;
  return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion uart_getc() blockiert allerdings den Programmablauf, denn es wird gewartet, bis ein Zeichen empfangen wird! Möchte man das Warten vermeiden, kann das RXC-Bit in einer Programmschleife abgefragt werden und dann nur bei gesetztem RXC-Bit UDR ausgelesen werden. &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;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// hier Makro für die Baudratenberechnung &lt;br /&gt;
&lt;br /&gt;
// hier uart_init, uart_getc (s.o.)&lt;br /&gt;
 &lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uart_init();&lt;br /&gt;
  while (1) &lt;br /&gt;
  {&lt;br /&gt;
    if ( (UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
    {&lt;br /&gt;
      // Zeichen wurde empfangen, jetzt abholen&lt;br /&gt;
      uint8_t c;&lt;br /&gt;
      c = uart_getc();&lt;br /&gt;
      // hier etwas mit c machen z.B. auf PORT ausgeben&lt;br /&gt;
      DDRC = 0xFF; // PORTC Ausgang&lt;br /&gt;
      PORTC = c;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      // Kein Zeichen empfangen, Restprogramm ausführen...&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return 0; // never reached &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eleganter und in den meisten Anwendungsfällen &amp;quot;stabiler&amp;quot; ist die Vorgehensweise, die empfangenen Zeichen in einer Interrupt-Routine einzulesen und zur späteren Verarbeitung in einem Eingangsbuffer (FIFO-Buffer) zwischenzuspeichern. Dazu existieren fertige und gut getestete [[Libraries|Bibliotheken]] &amp;lt;!-- &amp;quot;echte Libraries&amp;quot; (.a) wie im Verweis beschrieben sind hier eigentlich nicht gemeint, verwirrt hier etwas, da AVR-&amp;quot;Libraries&amp;quot; meist per #defines anpassbare Source-Codes sind, vielleicht so: --&amp;gt; und Quellcodekomponenten (z.&amp;amp;nbsp;B. UART-Library von P. Fleury, procyon-avrlib und einige in der &amp;quot;Academy&amp;quot; von avrfreaks.net).&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html Dokumenation der avr-libc/stdlib.h]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_Nutzung_von_sprintf_und_printf Die Nutzung von sprintf und printf]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/ Peter Fleurys] UART-Bibiliothek fuer avr-gcc/avr-libc&lt;br /&gt;
&amp;lt;!-- nimmermehr: * siehe auch: Weiterführende Informationen inkl. Beispielen für die Nutzung von stdio-Funktionen (printf etc.) im [[AVR-Tutorial:_UART]]. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: 9bit&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Zeichenketten (Strings) ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Zeichenketten, muß man sich zunächst darüber im klaren sein, daß es ein Kriterium geben muß, an dem der µC erkennen kann, wann ein Text zu Ende ist. Sehr oft wird dazu das Zeichen &#039;Return&#039; benutzt, um das Ende eines Textes zu markieren. Dies ist vom Benutzer einfach eingebbar und er ist auch daran gewöhnt, daß er eine Eingabezeile mit einem Druck auf die Return Taste abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Prinzipiell gibt es jedoch keine Einschränkung bezüglich dieses speziellen Zeichens. Es muß nur sichergestellt werden, daß dieses spezielle &#039;Ende eines Strings&#039; - Zeichen nicht mit einem im Text vorkommenden Zeichen verwechselt werden kann. Wenn also im zu übertragenden Text beispielsweise kein &#039;;&#039; vorkommt, dann spricht nichts dagegen, den Benutzer die Eingabe eines Textes mit einem &#039;;&#039; abschließen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird die durchaus übliche Annahme getroffen, daß eine Stringübertragung identisch ist mit der Übertragung einer Textzeile und daher mit einem Return (&#039;\n&#039;) abgeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
Das Problem der Übertragung eines Strings reduziert sich damit auf die Aufgabenstellung: Empfange und sammle Zeichen in einem char Array, bis entweder das Array voll ist oder das Text Ende Zeichen&#039; empfangen wurde. Danach wird der empfangene Text noch mit einem &#039;\0&#039; Zeichen abgeschlossen um einen Standard C-String daraus zu machen, mit dem dann weiter gearbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* Zeichen empfangen */&lt;br /&gt;
uint8_t uart_getc(void)&lt;br /&gt;
{&lt;br /&gt;
    while (!(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)))   // warten bis Zeichen verfuegbar&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;                   // Zeichen aus UDR an Aufrufer zurueckgeben&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_gets( char* Buffer, uint8_t MaxLen )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t NextChar;&lt;br /&gt;
  uint8_t StringLen = 0;&lt;br /&gt;
&lt;br /&gt;
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen&lt;br /&gt;
&lt;br /&gt;
                                  // Sammle solange Zeichen, bis:&lt;br /&gt;
                                  // * entweder das String Ende Zeichen kam&lt;br /&gt;
                                  // * oder das aufnehmende Array voll ist&lt;br /&gt;
  while( NextChar != &#039;\n&#039; &amp;amp;&amp;amp; StringLen &amp;lt; MaxLen - 1 ) {&lt;br /&gt;
    *Buffer++ = NextChar;&lt;br /&gt;
    StringLen++;&lt;br /&gt;
    NextChar = uart_getc();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
                                  // Noch ein &#039;\0&#039; anhängen um einen Standard&lt;br /&gt;
                                  // C-String daraus zu machen&lt;br /&gt;
  *Buffer = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf ist darauf zu achten, dass das empfangende Array auch mit einer&lt;br /&gt;
vernünftigen Größe definiert wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  char Line[40];      // String mit maximal 39 zeichen&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von sizeof() ist allerdings zu beachten, dass sizeof() nicht die Anzahl der Elemente des Arrays liefert, sondern die Länge in Byte. Da ein char nur ein Byte lang ist, passt der Aufruf &#039;uart_gets(Line, sizeof( Line ) );&#039; in diesem Fall. Falls man - aus welchen Gründen auch immer - andere Datentypen benutzen möchte, sollte man zur korrekten Angabe der Array-Länge folgende Vorgehensweise bevorzugen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  int Line[40];      // Array vom Typ int&lt;br /&gt;
&lt;br /&gt;
  uart_gets( Line, sizeof( Line ) / sizeof( Line[0] ) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Interruptbetrieb==&lt;br /&gt;
&lt;br /&gt;
Hier wird das Grundwissen des Artikels [[Interrupt]] und des Abschnitts [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts|AVR-GCC-Tutorial: Programmieren_mit_Interrupts]] vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Empfangen (RX) ===&lt;br /&gt;
&lt;br /&gt;
Beim ATmega8 muss das &#039;&#039;&#039;RXCIE&#039;&#039;&#039; Bit im Register UCSRB gesetzt werden, damit ein Interrupt beim Empfang eines Zeichens ausgelöst werden kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/* siehe auch obere Baudrateneinstellung */&lt;br /&gt;
/* USART-Init beim ATmega16 */&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
  UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
  UCSRC = (1&amp;lt;&amp;lt;URSEL)|(1&amp;lt;&amp;lt;UCSZ1)|(1&amp;lt;&amp;lt;UCSZ0); // Asynchron 8N1 &lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;RXEN)|(1&amp;lt;&amp;lt;TXEN)|(1&amp;lt;&amp;lt;RXCIE);  // UART RX, TX und RX Interrupt einschalten&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich muss &amp;quot;Global Interrupt Enable&amp;quot; mittels des Befehls sei() aktiviert sein. Interrupt-spezifische Definitionen werden über die Includedatei eingebunden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Interrupt wird immer ausgelöst, wenn ein Zeichen erfolgreich empfangen wurde. Zusätzlich braucht man die Interruptserviceroutine (ISR). &lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel enthält die ISR einen FIFO-Puffer (First in, First out). Dafür werden ein paar globale Variablen und Makros benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define UART_MAXSTRLEN 10&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t uart_str_complete = 0;     // 1 .. String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_str_count = 0;&lt;br /&gt;
volatile char uart_string[UART_MAXSTRLEN + 1] = &amp;quot;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
ISR(USART_RXC_vect)&lt;br /&gt;
{&lt;br /&gt;
  unsigned char nextChar;&lt;br /&gt;
&lt;br /&gt;
  // Daten aus dem Puffer lesen&lt;br /&gt;
  nextChar = UDR;&lt;br /&gt;
  if( uart_str_complete == 0 ) {	// wenn uart_string gerade in Verwendung, neues Zeichen verwerfen&lt;br /&gt;
&lt;br /&gt;
    // Daten werden erst in uart_string geschrieben, wenn nicht String-Ende/max Zeichenlänge erreicht ist/string gerade verarbeitet wird&lt;br /&gt;
    if( nextChar != &#039;\n&#039; &amp;amp;&amp;amp;&lt;br /&gt;
        nextChar != &#039;\r&#039; &amp;amp;&amp;amp;&lt;br /&gt;
        uart_str_count &amp;lt; UART_MAXSTRLEN ) {&lt;br /&gt;
      uart_string[uart_str_count] = nextChar;&lt;br /&gt;
      uart_str_count++;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      uart_string[uart_str_count] = &#039;\0&#039;;&lt;br /&gt;
      uart_str_count = 0;&lt;br /&gt;
      uart_str_complete = 1;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Funktion: Wurde eine komplette Zeichenkette empfangen, also das Ende (\n oder \r) erkannt oder die maximale Länge &#039;&#039;UART_MAXSTRLEN&#039;&#039; erreicht, wird die globale Variable &#039;&#039;uart_str_complete&#039;&#039; auf &#039;1&#039; gesetzt. Damit wird dem Hauptprogramm, welches auf diese Variable pollt, mitgeteilt, dass die Zeichenkette &#039;&#039;uart_string&#039;&#039; zur Verarbeitung bereit steht. Nach der Verarbeitung der Zeichenkette in der entsprechenden main-Routine, muss die Variable &#039;&#039;uart_str_complete&#039;&#039; wieder auf &#039;0&#039; zurück gesetzt werden. Dadurch werden alle neu empfangenen Zeichen wieder in den globalen Puffer geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== (Baustelle) ===&lt;br /&gt;
&lt;br /&gt;
* Empfangen (Receive) (Anm.: z.T. erledigt)&lt;br /&gt;
** ggf. Fallstricke ([http://www.mikrocontroller.net/topic/84256#707214 UDR in der ISR lesen!])&lt;br /&gt;
** Komplettes, einfaches Beispiel ([http://www.mikrocontroller.net/topic/84228#707052 Echo] (noch buggy beim Datenzugriff, siehe Lit. 2+3!)), ggf. LED zur ISR-Empfangsanzeige oder Overflow-Anzeige&lt;br /&gt;
&lt;br /&gt;
* Senden (Transmit)&lt;br /&gt;
** Variante &amp;quot;UART Data Register Empty&amp;quot; (UDRE) [http://www.mikrocontroller.net/topic/101472#882716]&lt;br /&gt;
** Variante &amp;quot;UART Transmit Complete&amp;quot; (TXC) &lt;br /&gt;
&lt;br /&gt;
* FIFO-Puffer [http://www.mikrocontroller.net/topic/101472#882716], Ringpuffer (Byte Buffering (circular))&lt;br /&gt;
&lt;br /&gt;
* UART-Bibliotheken &lt;br /&gt;
** [http://www.peterfleury.epizy.com/avr-software.html UART-Library] von Peter Fleury (UART (interrupt driven), Byte Buffering (circular))&lt;br /&gt;
** [http://beaststwo.org/avr-uart/index.shtml Updated AVR UART Library]  (modernisierte und erweiterte Version der Bibliothek von Peter Fleury)&lt;br /&gt;
** [http://www.procyonengineering.com/embedded/avr/avrlib/ Procyon AVRlib] von Pascal Stang (UART (interrupt driven), Byte Buffering (circular), VT100 Terminal Output)&lt;br /&gt;
** [http://die-seite.ch/public/uart_driver.c uart_driver.c] Beispieltreiber in C mit minimalstem Code fuer ATMega8. Sendet und empfaengt Strings ueber Interrupts (von Markus Zimmermann)&lt;br /&gt;
** [https://github.com/crapp/uartavr uartavr] BSD-3-Clause Library von Christian Rapp für ATmega328. Interrupt driven und Byte Buffering (circular), [https://crapp.github.io/uartavr/uart_8h.html API Dokumentation]&lt;br /&gt;
&lt;br /&gt;
* Literatur &lt;br /&gt;
** [http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=48188 avrfreaks.net Tutorial] inkl. Diskussion (engl.)&lt;br /&gt;
** avr-libc FAQ: [http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_16bitio Why do some 16-bit timer registers sometimes get trashed?]&lt;br /&gt;
&lt;br /&gt;
==Software-UART==&lt;br /&gt;
&lt;br /&gt;
Falls die Zahl der vorhandenen Hardware-UARTs nicht ausreicht, können weitere Schnittstellen über sogennante Software-UARTs ergänzt werden. Es gibt dazu (mindestens) zwei Ansätze: &lt;br /&gt;
* Der bei AVRs üblichste Ansatz basiert auf dem Prinzip, dass ein externer Interrupt-Pin für den Empfang (&amp;quot;RX&amp;quot;) genutzt wird. Das Startbit löst den Interrupt aus, in der Interrupt-Routine (ISR) wird der externe Interrupt deaktiviert und ein Timer aktiviert. In der Interrupt-Routine des Timers wird der Zustand des Empfangs-Pins entsprechend der Baudrate abgetastet. Nach Empfang des Stop-Bits wird der externe Interrupt wieder aktiviert. Senden kann über einen beliebigen Pin (&amp;quot;TX&amp;quot;) erfolgen, der entsprechend der Baudrate und dem zu sendenden Zeichen auf 0 oder 1 gesetzt wird. Die Implementierung ist nicht ganz einfach, es existieren dazu aber fertige Bibliotheken (z.&amp;amp;nbsp;B. bei [http://www.avrfreaks.net/ avrfreaks] oder in der [http://www.procyonengineering.com/embedded/avr/avrlib/ Procyon avrlib]).&lt;br /&gt;
* Ein weiterer Ansatz erfordert keinen Pin mit &amp;quot;Interrupt-Funktion&amp;quot; aber benötigt mehr Rechenzeit. Jeder Input-Pin kann als Empfangspin (RX) dienen. Über einen Timer wird der Zustand des RX-Pins mit einem vielfachen der Baudrate abgetastet (dreifach scheint üblich) und High- bzw. Lowbits anhand einer Mindestanzahl identifiziert. (Beispiel: &amp;quot;Generic Software Uart&amp;quot; Application-Note von IAR)&lt;br /&gt;
&lt;br /&gt;
Neuere AVRs (z.&amp;amp;nbsp;B. ATtiny26 oder ATmega48,88,168,169) verfügen über ein Universal Serial Interface (USI), das teilweise UART-Funktion übernehmen kann. Atmel stellt eine Application-Note bereit, in der die Nutzung des USI als UART erläutert wird (im Prinzip &amp;quot;Hardware-unterstützter Software-UART&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==Handshaking==&lt;br /&gt;
Wenn der Sender ständig sendet, wird irgendwann der Fall eintreten, daß der Empfänger nicht bereit ist, neue Zeichen zu empfangen. In diesem Fall muß durch ein &#039;&#039;&#039;Handshake-Verfahren&#039;&#039;&#039; die Situation bereinigt werden. Handshake bedeutet nichts anderes, als daß der Empfänger dem Sender mitteilt, daß er zur Zeit keine Daten annehmen kann und der Sender die Übertragung der nächsten Zeichen solange einstellen soll, bis der Empfänger signalisiert, daß er wieder Zeichen aufnehmen kann.&lt;br /&gt;
===Hardwarehandshake (RTS/CTS)===&lt;br /&gt;
Beim Hardwarehandshake werden zusätzlich zu den beiden Daten-Übertragungsleitungen noch 2 weitere Leitungen benötigt: &#039;&#039;&#039;RTS&#039;&#039;&#039; (&#039;&#039;&#039;R&#039;&#039;&#039;equest &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end) und &#039;&#039;&#039;CTS&#039;&#039;&#039; (&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;o &#039;&#039;&#039;S&#039;&#039;&#039;end). Jeder der beiden Kommunikationspartner ist verpflichtet, bevor ein Zeichen gesendet wird, den Zustand der &#039;&#039;&#039;RTS&#039;&#039;&#039; Leitung zu überprüfen. Nur wenn die Gegenstelle darauf Empfangsbereitschaft signalisiert, darf das Zeichen gesendet werden. Um der Gegenstelle zu signalisieren, daß sie zur Zeit keine Zeichen schicken soll, wird die Leitung &#039;&#039;&#039;CTS&#039;&#039;&#039; benutzt.&lt;br /&gt;
&lt;br /&gt;
===Softwarehandshake (XON/XOFF)===&lt;br /&gt;
Beim Softwarehandshake sind keine speziellen Leitungen notwendig. Stattdessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, das Senden einzustellen bzw. wieder aufzunehmen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;XOFF&#039;&#039;&#039; Aufforderung das Senden einzustellen&lt;br /&gt;
* &#039;&#039;&#039;XON&#039;&#039;&#039;  Gegenstelle darf wieder senden&lt;br /&gt;
&lt;br /&gt;
Nachteilig bei einem Softwarehandshake ist, dass dadurch keine direkte binäre Datenübertragung mehr möglich ist, denn von den möglichen 256 Bytewerten werden ja nun zwei (nämlich für &#039;&#039;&#039;XON&#039;&#039;&#039; und für &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus. In einem quasi-zufälligen Bytestrom, den eine Binärdatei darstellt, kämen diese beiden reservierten früher oder später vor und würden dann den Strom ungewollt stoppen und starten.&lt;br /&gt;
&lt;br /&gt;
==Galvanische Trennung==&lt;br /&gt;
Für eine hohe Überspannungsfestigkeit empfielt es sich, die Datenkanäle über Optokoppler zu führen. Es bietet sich z.b. der 6N138 an, ein &amp;quot;normaler&amp;quot; CNY-17 ist für hohe Baudraten nicht brauchbar.&lt;br /&gt;
&lt;br /&gt;
==Fehlersuche==&lt;br /&gt;
Erstaunlich oft wird im Forum der Hilferuf laut: &amp;quot;Meine UART funktioniert nicht, was mache ich falsch&amp;quot;. In der überwiegenden Mehrzahl der Fälle stellt sich dann heraus, daß es sich um ein Hardwareproblem handelt, wobei da wiederrum der Löwenanteil auf das Konto einer nicht korrekt eingestellten Taktrate geht: Der µC benutzt nicht einen angeschlossenen Quarz, so wie er auch im Programm eingetragen ist, sondern läuft immer noch mit dem internen RC-Takt. Daraus resultiert aber auch, daß der Baudraten Konfigurationswert falsch berechnet wird.&lt;br /&gt;
&lt;br /&gt;
Hilfreich zum Aufspüren solcher Fehler ist auch die [[AVR_Checkliste#UART/USART|AVR-Checkliste]].&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
Tipps zur Verarbeitung von Strings sind in den [[FAQ]].&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:avr-gcc Tutorial]]&lt;br /&gt;
[[Kategorie:UART und RS232]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=3-Phasen_Frequenzumrichter_mit_AVR&amp;diff=101260</id>
		<title>3-Phasen Frequenzumrichter mit AVR</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=3-Phasen_Frequenzumrichter_mit_AVR&amp;diff=101260"/>
		<updated>2019-10-30T21:20:04Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Siehe auch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von matzetronics&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Wettbewerb Header}}&lt;br /&gt;
&lt;br /&gt;
In diesem Artikel wird ein universeller 3-Phasen Frequenzumrichter mit einem einzigen ATMega 88/168/328P beschrieben. Der ATMega übernimmt dabei die komplette Steuerung der Bedienelemente, des LC-Displays und die Erzeugung der 3 Phasen. Das Projekt sollte auf fertigen Boards wie dem Arduino 2009 oder dem Uno auch laufen, das wurde aber nicht ausprobiert.&lt;br /&gt;
Im Gegensatz zu anderen Lösungen wird hier der Sinus nicht errechnet, sondern aus einer Tabelle gewonnen. Das spart sowohl Rechenzeit als auch Speicherplatz und ermöglicht dem MC, die gesamte Steuerung als Einzelprozessor (Single Chip) zu erledigen. Gleitkommaberechnungen kommen nicht vor. &lt;br /&gt;
&lt;br /&gt;
Die Frequenz und Amplitude der Ausgangssignale sind über 3 Tasten einstellbar und können permanent in das EEPROM des MC gespeichert werden. Ebenso ist eine externe Steuerung über 2 analoge Eingänge vorgesehen. Die Drehrichtung der 3 Phasen wird über eine Steckbrücke oder Schalter festgelegt.&lt;br /&gt;
&lt;br /&gt;
Eine einstellbare V/f Charakteristik erlaubt die Anpassung an viele Motoren und andere Verbraucher. Ein integrierter PID Regler für die analogen Eingänge wurde ebenso programmiert, die PID Parameter können im EEPROM abgelegt werden. Die Totzeit ist einstell- und abspeicherbar.&lt;br /&gt;
&lt;br /&gt;
Im groben Zügen basiert die Software auf der Application Note AVR447 von Atmel/Microchip, ist aber erheblich geändert, um sie für den hier beschriebenen Regler zu benutzen. Ebenso wird die LCD Bibliothek von Peter Fleury benutzt, herzlichen Dank an ihn für diese schöne Software. Leider passt das Programm beim derzeitigen Stand nicht ganz in einen ATMega48, mit ein wenig Optimieren in den Stringfunktionen könnte es aber klappen. &lt;br /&gt;
Um etwaigen Fragen vorzubeugen - nein, das Ganze läuft nicht in einem (veralteten) ATMega 8. Diesem fehlen sowohl die Timerfunktionen als auch die Pinchange Interrupts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Daten:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Frequenzbereich: regelbar von ca. 0,6 Hz - 162 Hz&lt;br /&gt;
&lt;br /&gt;
Ausgangsspannung: regelbar von 0V - 325 Vpp, maximal die vorhandene Zwischenkreisspannung &lt;br /&gt;
&lt;br /&gt;
Wellenformen: Entweder Motorkurven (&#039;Popokurven&#039;) oder Sinus, wählbar vor dem Kompilieren&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
Der MC ist mit einem LC-Display des HD44780 Industriestandard im 4-bit Modus und 3 einfachen (Digi-)Tastern ausgerüstet. Beim Einschalten prüft der MC das Vorhandensein einer Steckbrücke (Jumper) und schaltet dann entweder in den internen oder externen Steuermodus. Die komplette MC Steuerplatine ist galvanisch von der Motorendstufe getrennt, hierzu dienen 6 Optokoppler vom Typ HCPL3120. Diese Optokoppler liefern die Signale für klassische Halbbrücken Treiber vom Typ IR2110/2113, welche wiederum die MOSFet/IGBT Endstufen ansteuern. Die gezeichnete Endstufe ist nur als Beispiel zu verstehen, natürlich sind auch andere Konfigurationen denk- und machbar. &lt;br /&gt;
&lt;br /&gt;
Die 3 Timer des ATMega laufen synchron und es werden die OC (Output Compare) Ausgänge aller Timer benutzt. Einer der Timer (Timer 1) liefert einen Overflow Interrupt, der dazu benutzt wird, die Schrittgeschwindigkeit der Sinuserzeugung einzustellen und danach neue PWM Werte in alle 3 Timer zu schreiben. Der Quellcode erhält dazu auch ausführliche Kommentare. Die Schrittgeschwindigkeit ist direkt proportional der erzeugten Frequenz und wird vom Benutzer eingestellt. Mehr dazu findet sich im Abschnitt   [[3-Phasen_Frequenzumrichter_mit_AVR#Sinuserzeugung|&amp;quot;Sinuserzeugung&amp;quot;]]. &lt;br /&gt;
&lt;br /&gt;
Aus Gründen der Übersichtlichkeit errechnet der MC aber daraus die Frequenz in Hertz und zeigt diese auf dem Display. Ebenso wird die Amplitude der Wellenzüge über die Pulsbreite der PWM Signale eingestellt und auf dem Display in Prozent der Zwischenkreisspannung dargestellt. Da Drehstrommotore meistens mit einer V/f Charakteristik angesteuert werden, wird auch diese errechnet, bevor die Timer neu geladen werden.&lt;br /&gt;
&lt;br /&gt;
Hier Fotos vom Prototyp. Der Kontrollteil wurde auf 2,54 mm Punktraster in Fädeltechnik gebaut, der Leistungsteil auf Streifenleiterplatine mit 5,08 mm Raster. Fädeltechnik ist meine bevorzugte Technik für Einzelstücke und Prototypen. Der MC befindet sich unter dem LC Display. Ganz rechts auch noch ein Bild eines meiner treuen Drehstrom Motore. Dieser Motor hat 550 Watt und arbeitete seit 1969 in meiner Brunnenpumpe. Die Pumpe ist völlig verrostet und nun ausser Betrieb, aber der Motor läuft noch wunderbar. Gefertigt wurde er von den Stephan-Werken in Hameln.  &lt;br /&gt;
&lt;br /&gt;
[[Bild:Kontroll.jpg|200px]] [[Bild:PA+Koppler.jpg|200px]] [[Bild:Stephan_Motor.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
== Software, Bedienung und Kompilieren==&lt;br /&gt;
&lt;br /&gt;
Die Software ist so konzipiert, das die PWM Erzeugung komplett in Interrupts läuft. Die Hauptschleife des Programmes erledigt die Benutzerführung mit Tastenabfrage und LCD Ansteuerung. Ingesamt gibt es zwei Anzeigemodi: &lt;br /&gt;
Modus 1 ist das normale Menü, in dem per Taste 1 zwischen V/f Einstellung und Frequenzjustage gewechselt werden kann. Taste 2 und 3 verringern bzw. erhöhen den gewählten Wert. Der Cursor des Displays steht dabei unter dem editierbaren Wert. Eine einfache Autorepeat Funktion mit Beschleunigung erleichtert das Einstellen. &lt;br /&gt;
&lt;br /&gt;
Modus 2 wird durch gleichzeitiges Drücken der Tasten 1 und 2 erreicht. Dieses Menü dient dazu, die PID Werte des Reglers und die Totzeit der Brückensteuerung einzustellen und sowohl diese als auch die Startwerte für V/f und Frequenz ins EEPROM zu schreiben. Auch hier wechselt Taste 1 zwischen den Parametern und Taste 2 und 3 dienen zum Einstellen. Gleichzeitiges Drücken von 1 und 2 speichert die Werte, gleichzeitiges Drücken von 1 und 3 verlässt den Modus ohne Abspeichern. Es werden dabei die Werte für V/f und Frequenz gespeichert, die im Menü Modus 1 gerade eingestellt sind. &lt;br /&gt;
&lt;br /&gt;
Im Betrieb mit externen Signal zeigt Modus 1 die Werte an, sie lassen sich aber nicht per Taster ändern. Modus 2 hingegen ist anwählbar und funktioniert auch bei externer Kontrolle, um PID Werte und Totzeit zu justieren. Ein Interrupt wird durch den Analog-Digital Wandler ausgelöst. Dieser liest den Wert des ADC aus und speichert ihn in globalen Variablen. Das &#039;switch-case&#039; Konstrukt erlaubt es, weitere Kanäle des ADC zu nutzen, wenn das erforderlich werden sollte. Die DIP Variante des ATMega88/168 bietet diese Möglichkeit zwar nicht, hier sind alle Anschlüsse des ADC benutzt. Allerdings hat die QFP Ausführung noch die ADC Kanäle 6 und 7, diese könnten genutzt werden. &lt;br /&gt;
&lt;br /&gt;
Die hier beschriebene Schaltung ermöglicht es, sowohl Frequenz als auch V/f Werte als analoge Signale anzulegen. Ohne externe Spannungsteiler wird eine Eingangsspannung von 0-5 Volt an den Eingängen erwartet. &lt;br /&gt;
&lt;br /&gt;
Die LCD Bibliothek musste leicht modifiziert werden, da hier zwei unterschiedliche Ports für Daten- und Steuerleitungen benutzt werden. Die Änderungen befinden sich in lcd.h und sollten so übernommen werden, da die PWM Ausgänge des MC nicht verändert werden können.&lt;br /&gt;
&lt;br /&gt;
Übrigens wurden bis auf den PID Regler und die LCD Routinen alle Softwareteile in einer Datei zusammengefasst, um dem Compiler die beste Optimierung zu ermöglichen. Als Optimierungseinstellung hat sich -Os bewährt. Vor dem Kompilieren sollte nicht vergessen werden, die benutzte Quarzfrequenz in Hz in die Projekteinstellungen einzutragen. Unter Linux sollte das Makefile mit einem -DF_CPU 16000000 ergänzt werden, in AS4 unter &amp;quot;Projekt Settings -&amp;gt; General&amp;quot;. Wird das mitgelieferte *.aps als Projektvorlage benutzt, ist das jedoch unnötig. Das Software Paket enthält alle Dateien, um mit AVR Studio 4 das Projekt zusammenzubauen. Der Sourcecode besteht aus folgenden Dateien:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;main.c, pid.c, lcd.c&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dazu kommen die Header:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;vfd.h, vfdtables.h, pid.h, lcd.h&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Besondere Aufmerksamkeit verdient vfd.h. Hier sind alle Deklarationen für das Projekt zusammengefasst. Wer main.c studiert, wird eine Menge Konstanten finden, sie alle werden in vfd.h deklariert.  &lt;br /&gt;
Die am häufigsten benutzten Variablen sind die Schrittgeschwindigkeit (inco), der Zeiger in die Sinustabelle(sinTableIncrement), die Amplitude (amplitude) und das V/f Verhältnis (VperHz). Diese Variablen werden direkt in Registern gehalten, um die Arbeitsgeschwindigkeit zu erhöhen. Vermutlich wäre das nicht nötig, aber als Beispiel zur Verwendung direkter Registervariablen ist es evtl. auch für andere Projekte hilfreich. &lt;br /&gt;
Eine weitere direkte Registervariable ist (fastFlags). Dieses struct wird bitweise benutzt, allerdings sind hier nur 4 bits benutzt worden. Ein Bit für die Umschaltung zwischen externer und interner Kontrolle, dann 2 Bits für die benutzte Wellenform (entweder &#039;undefined&#039; oder &#039;Sinus&#039;) und dann noch ein Bit für die gewünschte Drehrichtung. &lt;br /&gt;
Die Anzeigeparameter werden über Integermathematik errechnet, da Gleitkommazahlen vermieden werden sollten. Eine regelmässige Aufgabe des Timerinterrupts ist das Aufrufen der Routine &#039;SpeedController&#039; (nach 200 Timerüberläufen). Hier wird im externen Kontrollmodus der PID Regler aufgerufen und ausserdem die Anzeigeparameter errechnet. Die Anzeige auf dem LCD erfolgt im Kommandointerpreter. &lt;br /&gt;
&lt;br /&gt;
Nach dem Reset werden zuerst das LCD und die Ports initialisiert und alle Variablen vorbelegt. Im Anschluss folgen die Initialisierung der Timer, der Pinchange Interrupts, des ADC und des PID Reglers. Nun werden die Jumper abgefragt und das EEPROM eingelesen. Zum Schluss erfolgt die Freigabe der globalen Interrupts und damit der Frequenzerzeugung. &lt;br /&gt;
&lt;br /&gt;
Während des Vorganges werden Diagnosezahlen auf dem LCD ausgegeben, um etwaige Probleme mit &#039;steckengebliebenen&#039; Tasten oder andere Probleme zu melden. Am Ende folgt die Hauptschleife, in der der &#039;Kommandointerpreter&#039; (execCommand) läuft.&lt;br /&gt;
&lt;br /&gt;
Der Kommandointerpreter tut nichts anderes, als erstmal nach gedrückten Tasten zu schauen. Findet er eine oder mehrere Tasten gedrückt, wird das switch-case Konstrukt abgearbeitet. Anschliessend wird das LCD aktualisiert. Eine zweite Kommandoebene tut das für das erweiterte Menü, die Arbeitsweise ist die gleiche. Bei allen Einstellungen wird dafür gesorgt, das die Werte nicht &#039;überschlagen&#039; können, da das oft fatale Folgen hätte. So ist es z.B. nicht möglich, durch Drücken der - Taste die Arbeitsfrequenz von 0,1 Hz direkt auf 162 Hz umzuschalten oder die Totzeit auf 0 zu stellen.&lt;br /&gt;
&lt;br /&gt;
Noch eine Anmerkung zu AVR Studio 4 - die letzte Version ist 4.19 und um Kompilierfehler zu vermeiden, sollte man die AVR Toolchain 3.3.1 - 1020 benutzen, sonst wird man mit Fehlern belästigt. (Vielen Dank an kabelbieger). Alternativ ist WinAVR geeignet - die Version 20100110 funktioniert bspw. recht gut.&lt;br /&gt;
&lt;br /&gt;
== Sinuserzeugung ==&lt;br /&gt;
&lt;br /&gt;
Die drei Phasen für den Sinusausgang werden aus einer Tabelle geholt, diese befindet sich in der Datei &#039;vfdtables.h&#039;. Hier liegen in einem Array die Werte für U,V und W (Heute auch oft L1, L2 und L3 genannt, früher auch als R, S und T bezeichnet). &lt;br /&gt;
Die erzeugte Frequenz wird dadurch bestimmt, nach wieviel Aufrufen der &#039;TIMER1_CAPT&#039; ISR der Zeiger in die Tabelle inkrementiert wird. Wenn &#039;inco&#039; z.B. 1 ist, wird der Zeiger in die Tabelle erst nach 255 Durchläufen inkrementiert und der nächste Tabellenwert in die Timer geladen. Da die Tabelle 192 Werte lang ist, resultiert das in einer Ausgangsfrequenz von 0,635 Hertz, die sich aus der PWM Wiederholrate von 31,25 kHz/256 = 8,192ms * 192 ergibt.  &lt;br /&gt;
Ist &#039;inco&#039; bei 255, wird der nächste Tabellenwert bereits nach 1/31,25kHz = 32µs geladen und das resultiert in einer Ausgangsfrequenz von 1/(32µs * 192) = 162,7 Hz.  Mein Dank geht an CNCler, der das richtig erklärt hat. &lt;br /&gt;
 &lt;br /&gt;
Die Drehrichtung wird einfach dadurch gewechselt, das die Werte für V und W vertauscht werden, bevor die Timer geladen werden. Tabellenzugriff und das Laden der PWM Timer erfolgen gesammelt in der Timer 1 Overflow Interrupt Routine.&lt;br /&gt;
 &lt;br /&gt;
Diese Routine stellt somit den Kern der Sinuserzeugung dar. Der Betrieb von Halbbrücken erfordert das Einfügen einer Totzeit, in der weder der obere noch der untere Schalter der Halbbrücke angeschaltet sein darf. Würde das der Fall sein, wird die Versorgungsspannung kurzgeschlossen, was auf keinen Fall passieren darf. (Dieser Fall wird oft als &#039;Shoot-through&#039; bezeichnet). &lt;br /&gt;
Da die Totzeit stark von den verwendeten Halbbrücken abhängig ist, wurde sie einstellbar gemacht. Die Routine &#039;InsertDeadband&#039; errechnet für jeden Timer die erforderliche Totzeit, bevor die Werte für die PWM in den Timer geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Die Totzeit wird im EEPROM abgespeichert. Aus Sicherheitgründen wird bei EEPROM Lesefehlern oder zu kleinen Werten für die Totzeit ein Minimum statt des ungültigen Wertes angewendet. Eine zu grosse Totzeit ist nicht schädlich, begrenzt aber den nutzbaren PWM Bereich und die Effektivität des Frequenzumrichters. &lt;br /&gt;
Es schadet aber natürlich nicht, bei der Erstinbetriebnahme von einer sehr hohen Totzeit auszugehen und diese dann unter Beobachtung der Stromaufnahme zu verringern. Die Einheit der Totzeit (DEAD_TIME_HALF) bei der Anzeige im Display sind MC-Zyklen. Die resultierende Totzeit über die Halbbrücke ist doppelt so hoch wie die eingestellten MC-Zyklen. Bei der gezeigten Schaltung ist ein Wert von 20 verwendet worden, resultierend in einer Totzeit von (2 * 20 * 62,5ns = 2500ns).&lt;br /&gt;
Bei schnellen MOSFets oder IGBTs kann dieser Wert möglicherweise noch verringert werden. Die Stromaufnahme kann am Shunt R7 gemessen werden. &lt;br /&gt;
&#039;&#039;&#039;Achtung: Dieser Teil ist nicht netzgetrennt und sollte deswegen mit Vorsicht behandelt werden.&#039;&#039;&#039; &#039;&#039;&#039;Eine Verbindung zwischen GNDI und PE führt unweigerlich zur Zerstörung von Bauteilen im Netzeingang.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Wellenform der Phasen ist bei der gezeigten Tabelle dem Betrieb von Motoren angepasst. Andere Wellenformen lassen sich durch Austausch der Tabelle erreichen. Seit dem Update wird nun die Option &#039;#define PURE_SINE_DRIVE&#039; in vfd.h angeboten. Es werden in diesem Fall 3 echte Sinuskurven mit 120° Phasenverschiebung erzeugt, ohne die typischen &#039;Popokurven&#039; für BLDC ähnliche Ansteuerung.  &lt;br /&gt;
Beim Austausch gegen andere Tabellen sollte beachtet werden, das die Länge der Tabelle mit der Originaltabelle übereinstimmt, da die Berechnung der Schrittweite davon abhängt. Wenn der MC mit einer anderen Frequenz als 16Mhz betrieben wird, sollte auch der Parameter &#039;DIVISIONEER&#039; in der Datei &#039;vfd.h&#039; angepasst werden, damit die Darstellung am Display korrekt ist. Irgendwie kriege ich es nicht hin, das der Wert direkt aus F_CPU errechnet wird, wäre schön, wenn mir das mal jemand beibringt. &lt;br /&gt;
Wer lediglich einen Wechselstrom Ausgang benötigt, sollte die Tabelle durch echte Sinuswerte ersetzen ( wie jetzt im Update vorgesehen) und nur 2 Endstufen aufbauen. Zwischen den Ausgängen dieser Endstufen ist dann ein regelbares Wechselspannungspotential. &lt;br /&gt;
&lt;br /&gt;
Natürlich sollte die Drehrichtung eines mit voller Geschwindigkeit laufenden Motors nicht einfach umgekehrt werden. In jedem Fall ist es ratsam, den Motor zu stoppen und erst dann die Drehrichtung zu ändern.&lt;br /&gt;
&lt;br /&gt;
== V/f ==&lt;br /&gt;
&lt;br /&gt;
Die V/f Charakteristik ist die meist angewandte Methode bei Frequenzumrichtern, um die aufgenommene Leistung eines Motors mit seiner Drehzahl zu regeln. &#039;Volt per Frequenz&#039; Charakteristik heisst, das ein Motor bei z.B. 100% seiner Drehzahl zwar schon mit der vollen Nennspannung betrieben werden darf, das aber bei reduzierter Drehzahl auch die Betriebsspannung heruntergeregelt werden muss. Bei z.B. 50% der Nenndrehzahl sollten auch nicht mehr als 50% der Betriebsspannung angelegt werden usw. Die genauen Werte sind unterschiedlich und sollten deswegen entweder dem Datenblatt des Motors entnommen werden oder durch Ausprobieren ermittelt werden. &lt;br /&gt;
&lt;br /&gt;
Ein Hinweis liefert dabei das erzielte Drehmoment (die &amp;quot;Kraft&amp;quot;) des Motors. Selbstverständlich sollte es dabei sein, dem Motor nicht mehr abzuverlangen, als er liefern kann. Lautes Brummen ist z.B. ein Zeichen für zu viel Spannung, das V/f Verhältnis sollte kleiner gemacht werden. &lt;br /&gt;
Oft ist es aber ausreichend, der Maschine bei Nennfrequenz (z.B. 50 Hz) gerade 100% Betriebsspannung zu liefern, bei einer reduzierten Frequenz reduziert der MC dann auch proportional die mittlere Spannung an den Motorwicklungen.&lt;br /&gt;
&lt;br /&gt;
Übrigens erlaubt diese Steuerung auch, den Umrichter als &#039;regelbares Netzteil&#039; zu missbrauchen. Ich habe z.B. mit einem schnellen Gleichrichter auch schon einen DC-Motor aus einer Waschmaschine mit einer Spannung von 24 Volt (Waschen) bis 200 Volt (Schleudern) betrieben.&lt;br /&gt;
&lt;br /&gt;
== Schaltbild ==&lt;br /&gt;
Das Schaltbild (siehe unten) bietet keine grossen Besonderheiten. Was hier angeberisch als &#039;DC/DC Wandler&#039; beschrieben wird, ist im Fall des 5 Volt Wandlers ein Schaltnetzteil vom Typ Handylader und im Fall des 18 Volt Wandlers ein kleines Schaltnetzteil für LED Lichterketten, das durch geringfügiges ändern des Regelkreises von 12 auf 18 Volt hochgedreht wurde. Der benötigte Strom ist abhängig von der gewählten Treiberschaltung und den Endstufen und liegt bei der gezeichneten Variante so um die 150mA für alle 3 Endstufen. Wichtig ist wie immer eine gute Entkopplung möglichst direkt an den Treiberbausteinen. Die impulsartige Belastung durch den Ladevorgang der Gates sollte durch Elkos aufgefangen werden, wie im Schaltbild gezeigt.&lt;br /&gt;
Der 5 Volt Zweig wird ohne Hintergrundbeleuchtung des LCD mit etwa 40-80mA belastet, wobei der Löwenanteil für die LEDs der Optokoppler draufgeht.  Übrigens sind auch Kleinnetzteile mit Einweggleichrichtung am Markt zu finden, die auf keinen Fall aus der Zwischenkreisspannung gespeist werden dürfen. Solche Netzteile sind auf der Wechselstromseite des Netzeingangs anzuschliessen.&lt;br /&gt;
&lt;br /&gt;
18 Volt werden übrigens nur deswegen verwendet, weil die gerade vorhandenen Optokoppler vom Typ 3120 mindestens 16 Volt Betriebsspannung benötigen. Hätte ich welche vom Typ HCPL3180 gehabt, hätten 12 Volt ausgereicht und das Netzteil hätte nicht manipuliert werden müssen. Leider sind die Optokoppler nicht ganz billig, theoretisch sollten es aber auch preiswertere schnelle Typen tun, wie z.B. der 6N137 oder HCPL2630. &lt;br /&gt;
&lt;br /&gt;
Letzterer bietet 2 Koppler in einem Gehäuse. Dabei ist allerdings die Signaltreue zu beachten. Keinesfalls darf der Optokoppler die Phase invertieren! Dieser Fehler würde zum &#039;Shoot-Through&#039; der Endstufen führen. Falls invertierende Koppler benutzt werden, müssen also die Polaritäten der OC Ausgänge in der Software umgedreht werden und mit entsprechenden Pulldowns und -Ups dafür gesorgt werden, das keine falschen Signale an den IR Treibern erscheinen. Die gezeigte Endstufe (&#039;&#039;&#039;gezeigt wird nur eine Phase, die anderen beiden sind identisch&#039;&#039;&#039;) besitzt mit dem Shunt R7 und den Transistoren T1 und T2 eine integrierte Überstromsicherung. &lt;br /&gt;
&lt;br /&gt;
Bei einem zu hohen Strom (hier ca. 2 Ampere) leitet T1 und steuert T2 durch. Das Highsignal am Kollektor von T2 aktiviert die Shutdown Eingänge der Treiber ICs. L4 und L3 sollten hochstromfähig sein, da hier der Motorstrom herüberfliesst.&lt;br /&gt;
Die kleinen Schaltnetzteile (aka &amp;quot;DC/DC Wandler&amp;quot;) werden direkt aus der Zwischenkreisspannung versorgt. Diese liegt ja typisch bei ca. 325 Volt und ist damit genau richtig. Man könnte die internen Gleichrichter und Siebelkos entfernen, aus Faulheit ist das bei mir nicht geschehen. &lt;br /&gt;
&lt;br /&gt;
Auffällig ist das Netzeingangsfilter. Frequenzumrichter sind dafür bekannt, das sie harte Pulse mit hoher Frequenz erzeugen und um zu verhindern, das diese auf das Netz zurückwirken, wurde eine recht aufwendige Schaltung vorgesehen. Es ist auch ratsam, bei längeren Leitungen vom Ausgang der Endstufe zum Verbraucher diese Leitung entweder zu schirmen, oder über ein Filter direkt am Ausgang des FU zu führen. Die PWM Frequenz liegt bei ca. 31 kHz und hat steile Flanken, deswegen sollte der Abstrahlung von Störfeldern bzw. die Verhinderung derselben ein wenig Aufmerksamkeit geschenkt werden. C15 bis C18 sollten übrigens X2 Typen sein, wobei C17/C18 auch ein Y Typ sein kann. Bestens geeignet sind designierte Entstörkondensatoren, solange sie die nötige Spannungsfestigkeit haben. Die Drosseln können entweder stromkompensiert sein oder einzeln. Wie auf dem Bild oben zu sehen ist, habe ich ein fertiges Netzfilter mit Kaltgeräteeingang benutzt, das erspart den diskreten Aufbau eines Filters. R7 (der Shunt) muss natürlich den Motorstrom vertragen, ich verwand einen 5 Watt Betonwiderstand. Der hohe Einschaltstrom bei leerem Zwischenkreiskondensator wird durch R11, einen Hochstrom NTC, begrenzt, wie er auch in PC Netzteilen üblich ist. Die Bauform 644 von z.B. Philips ist hier gut geeignet. &lt;br /&gt;
&lt;br /&gt;
Die Kondensatoren parallel zu den Tastern sind übrigens keineswegs zum Entprellen, obwohl sie diese Funktion nebenbei auch erfüllen. Sie verhindern die Einstrahlung von Störsignalen durch naheliegende LCD- und PWM- Leitungen und sorgen für einen Mindeststrom in den Tastern. (Siehe auch: http://www.mikrocontroller.net/topic/287303 ). Die Taster werden zyklisch im &#039;Kommandointerpreter&#039; der Software abgefragt und benötigen deswegen keine gesonderte Entprellung. &lt;br /&gt;
&lt;br /&gt;
Neben dem ISP Anschluss ist ein als &#039;Feature&#039; Anschluss bezeichneter Verbinder zu sehen. An diesen können die beiden Analogsignale (Pin 6 und 8) angelegt werden.  Pin 6 (ADC Kanal 5) steuert das V/f Verhältnis während an Pin 8 (ADC-Kanal 4) das Frequenzregelsignal anliegt. Pin 4 dient zur Änderung der Drehrichtung. Eine nach Masse gesetzte Brücke wendet den Motor. Pin 2 schaltet zwischen externer (ADC) und interner Kontrolle (Taster und LCD) um. Bei gesetzter Brücke nach Masse wird auf externe Kontrolle umgeschaltet. Diese Prüfung wird nur nach einem Resetzyklus des MC durchgeführt. Änderungen im Betrieb sind also nicht vorgesehen. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig ist die Unterscheidung zwischen GND und GNDI&#039;&#039;&#039;: GND ist der netzgetrennte Bezugspunkt für die Steuerelektronik des MC, während GNDI direkt mit dem Netz verbunden ist. &#039;&#039;&#039;Auf keinen Fall dürfen GND und GNDI miteinander verbunden werden!&#039;&#039;&#039; Im Gegenteil, ausreichende Sicherheitsabstände sind auf jeden Fall einzuhalten. In meinem Prototyp sind deswegen Kontroll- und Leistungsplatine völlig voneinander getrennt und nur durch die Optokopplerplatine verbunden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:VF_Sine_Drive.png|800px]]&lt;br /&gt;
&lt;br /&gt;
== Fazit ==&lt;br /&gt;
Der gezeigte Frequenzumrichter ist durch die Einfachheit des Steuerungsteils und die Verwendung von Standardbauteilen ein dankbares Projekt, um die Möglichkeiten moderner Mikrocontroller zu demonstrieren und mit Motorsteuerungen zu experimentieren. Je nach verwendeter Endstufe können auch andere Motoren, z.B. E-Autos, Festplattenmotoren oder Spiegelmotoren aus Laserdruckern angesteuert werden. &lt;br /&gt;
Ändern der PWM Frequenz z.B. ist in der Routine TimersInit() vorgesehen. Andere Vorteiler Einstellungen für alle 3 Timer können hier mit Hilfe der im Moment auf 0 stehenden &#039;CSx1&#039; eingefügt werden. Natürlich kann der MC auch mit dem internen 8 MHz Oszillator betrieben werden. Man erhält dadurch zwei weitere Portpins für z.B. Überstromrückmeldung oder Drehzahlüberwachung für den Preis einer niedrigeren PWM Frequenz. &lt;br /&gt;
  &lt;br /&gt;
Die externe Kontrolle über Analogeingänge ermöglicht z.B. auch, Servomotoren wie Capstan anzusteuern und mittels z.B. PLL zu stabilisieren. Der integrierte PID Regler mit einstellbaren Parametern erlaubt die Anpassung an verschiedenste Problemstellungen. &lt;br /&gt;
Ich bin jedenfalls gespannt, was dem Leser an Anwendungen einfällt und freue mich über Anregungen und Ideen.&lt;br /&gt;
Noch ein Hinweis: Es lohnt sich, die Preise der verschiedenen möglichen Controller zu vergleichen. Oft ist mittlerweile der &#039;grosse&#039; ATMega328 billiger erhältlich als seine kleinen Brüder. Sollte ein anderer Controller verwendet werden (Mega88, 168 und 328 sind ja alle untereinander kompatibel), sollte das in den Projekteinstellungen berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
Der Download enthält neben dem Source Code für AVR Studio 4 mit WinAVR auch ein Schaltbild und die Schaltung im Eagle Format (*.sch). Da die Anwendungen sich erheblich unterscheiden können, wurde auf die Erstellung eines Platinenlayouts verzichtet, ist aber mit dem Schaltplan für Eagle duchaus möglich. Die gewählten Bauformen für Teile sind nicht unbedingt die optimalsten, etwas Handarbeit und Anpassung könnten also nötig werden. &lt;br /&gt;
AVR Studio 4 wurde wegen seiner Stabilität benutzt, der Code sollte mit evtl. geringen Anpassungen aber natürlich auch unter AS5, AS6 oder AS7 kompilieren. Wer auf jegliche Codeveränderungen verzichten möchte, findet fertige HEX Dateien im Sourcecode Ordner in Unterordner &#039;default&#039;, welche für Mega88, 168 und 328 vorkompiliert wurden, jeweils für Motorkurven- oder Sinuskurven Erzeugung.&lt;br /&gt;
&lt;br /&gt;
* [[Datei:VF_Sine_Drive.zip Sourcecode]]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/288205 Diskussion zum Artikel]&lt;br /&gt;
* [http://www.zum.de/dwu/depotan/apem112.htm Flash Animation zu Drehstrom bei der DWU - Zentrale für Unterrichtsmedien e.V.]&lt;br /&gt;
* [http://www.atmel.com Atmel Application Note AVR447 - BLDC Motor Driver with Hallsensors and Sine Modulation] &lt;br /&gt;
* [http://www.atmel.com Atmel Application Note AVR221 - A universal PID Controller with AVR] &lt;br /&gt;
* [http://www.peterfleury.epizy.com/ Peter Fleurys Homepage mit der verwendeten LCD Bibliothek] &lt;br /&gt;
* [http://www.infineon.com/ Infineon - Datenblätter und Applikationen für Treiber, MOSFet und IGBT]&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:Spannungsversorgung und Energiequellen]]&lt;br /&gt;
[[Kategorie:Wettbewerb]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=IR-LCD&amp;diff=101259</id>
		<title>IR-LCD</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=IR-LCD&amp;diff=101259"/>
		<updated>2019-10-30T21:17:46Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Universeller Infrarot Decoder und Sender mit LCD Anzeige&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
von &#039;&#039;&#039;[http://www.mikrocontroller.net/user/show/f_klee Frank Klee]&#039;&#039;&#039;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Das ist ein kleines Projekt das auf der [[IRMP]] und [[IRSND]] Software von Frank Mayer basiert. Es dient als kleines Testgerät, um Infrarotfernsteuerungen zu untersuchen. Das Projekt wurde ursprünglich von Klaus Leidinger entwickelt und ist von mir um die Möglichkeit, IR-Telegramme zu senden erweitert worden. Ein vorhandenes IR-LCD nach Klaus Leidinger kann an die neue Version angepasst werden. Wie beschreibe ich weiter [[IR-LCD#.C3.84nderungen_an_IR-LCD_von_Klaus_Leidinger|unten]].&lt;br /&gt;
[[Bild:IR-LCD.jpg|300px|thumb]]&lt;br /&gt;
&lt;br /&gt;
Man kann mit dem Gerät nicht nur Infrarotcodes anzeigen lassen. Ich habe z.B. für meinen Receiver einen Code (durch ausprobieren) entdeckt und ihn dann in eine programmierbare Fernbedienung programmiert.&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
Die Schaltung ist sehr einfach aufgebaut und besteht eigentlich nur aus einem Mikrocontroler mit LC-Display, einem IR-Empfängermodul und einer IR-Senderdiode mit Treiber. Der Aufbau erfolgt auf einer einseitigen Platine und benötigt nur 7 Brücken. Die Platine ist also für Selberätzer gut zu verwenden. Aufgrund der teilweise engen Leiterbahnführung ist etwas Umsicht beim löten angesagt.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Bild:ir-lcd-v2Schaltplan.png]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Layout ist einseitig und enthält sieben Brücken. Daher ist die Platine sicher auch für Selbstätzer geeignet, Der Programmierstecker K1 ist optional. Lässt man ihn weg entfallen drei Brücken. An den Steckverbinder K2 wird ein USB &amp;lt;-&amp;gt; TTL Wandlerkabel angeschlossen. Ich nutze dafür eines, das die Fa. Reichelt für den Raspberry Pi für wenig Geld anbietet. Dieses kabel sorgt auch für die Stromversorgung, entweder über den PC oder im Standalone-Betrieb über ein USB-Netzteil. &amp;lt;br /&amp;gt;&lt;br /&gt;
Der Kondensator C12 stammt aus dem Originalschaltplan von Klaus Leidinger. Bei mir hat er zu Schwierigkeiten geführt und ich habe ihn ersatzlos gestrichen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Software ===&lt;br /&gt;
Basis ist die IRMP und IRSND Software von Frank Mayer, bei der ich nur den Teil zur Anzeige der Daten auf dem LCD und dem seriellen Port bzw. die Befehlsauswertung der Befehle vom seriellen Port bzw. die Befehlseingabe per Drehimpulsgeber dazu gebaut habe. Ich verwende dabei die LCD- und UART-Library von Peter Fleury. Vielen Dank an dieser Stelle. Originaldateien siehe in den Links. Die Encoder-Library habe ich geschrieben, nachdem ich diese Dinger fast in jedem Projekt einsetze. Wer einen anderen Encoder einsetzen möchte muss eventuell die encoder.h entsprechend anpassen. Hat man einen Two-Step oder Single-Step Encoder erwischt, muss die Auskommentierung der entsprechenden defines gelöscht oder gesetzt werden.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Programm basiert auf der IRMP-Version vom 07.05.2015 und IRSND-Version vom 26.01.2015. Die serielle Schnittstelle arbeitet mit 38400 Baud, 8N1. Zur Kommunikation kann ein einfaches Terminalprogramm genutzt werden. Da es ziemlich mühselig ist, die Befehle Zeichen für Zeichen in die Tastatur zu tippen, habe ich ein kleine VB6-Programm geschrieben, das die Kommunikation übernimmt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Ich nutze AVR-Studio 6.2 zur Entwicklung meiner Software. Die entsprechenden Projektdateien findet ihr im Download.&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Bootloader ===&lt;br /&gt;
Um die Software bei eventuellen Änderungen bequem auf den Mega328 laden zu können habe ich ihm einen Bootloader spendiert. Um den Bootloader zu aktivieren muß der Taster während des Powerup gedrückt gehalten werden. Die Parameter des Bootloaders sind: 38400 Baud, Protokoll avr109.&amp;lt;br /&amp;gt;&lt;br /&gt;
Im Download die an den Mega328 angepasste Version. Zur Programmierung nutze ich Avr-Osp II von Mike Henning, den ich in AVR-Studio eingebunden habe. Es kann natürlich auch jede andere Software verwendet werden, die das AVR109-Protokoll unterstützt.&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Fusebits einstellen ===&lt;br /&gt;
Wenn der Bootloader benutzt wird, müssen die Fusebits entsprechend  eingestellt sein:&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:FusesBootloader.png]]&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Ohne Bootloader sieht es so aus:&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Bild:Fuses.png]]&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Bei anderen Programmern dementsprechend... anders&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IR-Tester ===&lt;br /&gt;
Das Programm IR-Tester ist eigentlich selbsterklärend. Zur Sicherheit habe ich aber noch eine kleine Hilfedatei geschrieben. Das Programm scannt automatisch die ersten 16 COM-Ports, ob sich daran IR-LCD befindet. Der Port wird gespeichert, so dass beim nächsten mal nicht mehr gescannt werden muss. Man kann den Port aber auch manuell auswählen. In einem Fenster werden daraufhin alle empfangenen, decodierten Frames und auch die gesendeten Befehle angezeigt. Durch klicken auf einen Frame kann man diesen auch wieder senden oder vorher verändern.&amp;lt;br /&amp;gt;&lt;br /&gt;
=== Änderungen an IR-LCD von Klaus Leidinger ===&lt;br /&gt;
Wer bereits einen IR-LCD gebaut hat möchte diesen natürlich weiter benutzen. Das ist ohne weiteres Möglich, es sind nur ein paar kleine Änderungen erforderlich. Das Programm ist größer als 16k und passt deshalb nicht mehr in einen ATmega168. Dieser muss gegen einen ATmega328 getauscht werden. Um Eingaben tätigen zu können war eine entsprechende Eingabeeinheit erforderlich. Der verwendete Drehimpulsgeber kann einfach nachgerüstet werden mit dem Nachteil, dass das vorhandene Gehäuse dann zu klein ist. Will man auf den Standalone-Betrieb verzichten kann der Drehimpulsgeber auch weggelassen werden. Alle anderen Änderungen beruhen darauf, dass ich auf den V24-Wandler verzichtet und statt dessen einen USB &amp;lt;-&amp;gt; TTL Wandler mit Spannungsversorgung benutzt habe. Dadurch war der Spannungsversorgungsteil ebenfalls überflüssig.&lt;br /&gt;
=== Downloads ===&lt;br /&gt;
==== Hardware: ==== &lt;br /&gt;
Target Datei: [[Datei:ir-lcd-v2.T3001|ir-lcd-v2.T3001]]&amp;lt;br /&amp;gt;&lt;br /&gt;
Schaltplan als PDF: [[Datei:Ir-lcd-v2_Schaltplan.pdf|Schaltplan.pdf]]&amp;lt;br /&amp;gt;&lt;br /&gt;
Schaltplan und Layout nur zur privaten Verwendung. Gewerbliche Nutzung nur mit meiner Zustimmung.&lt;br /&gt;
&lt;br /&gt;
==== Software: ====&lt;br /&gt;
Bootloader passend für IR-LCD: [[Datei:Bootloaderm328.zip|bootloaderm328.zip]]&amp;lt;br /&amp;gt;&lt;br /&gt;
IR-LCD Software V2.0: [[Datei:ir-lcd-v2.zip|ir-lcd-v2.zip]]&amp;lt;br /&amp;gt;&lt;br /&gt;
IR-Tester: [[Datei:irtester-setup.zip|irtester-setup]]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Links ===&lt;br /&gt;
Beschreibung IRMP (Frank Mayer) :&amp;lt;br /&amp;gt;&lt;br /&gt;
* [[IRMP]]&amp;lt;br /&amp;gt;&lt;br /&gt;
* [[IRSND]]&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diskussionsforum zu IRMP/IRSND auf microcontroller.net :&amp;lt;br /&amp;gt;&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/162119 Beitrag: Infrared Multi Protocol Decoder]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bootloader: &amp;lt;br /&amp;gt;&lt;br /&gt;
* [http://atmel.com/dyn/resources/prod_documents/doc1644.pdf AVR109: Using Self Programming on tinyAVR and megaAVR devices]&amp;lt;br /&amp;gt;&lt;br /&gt;
* Software: [http://atmel.com/dyn/resources/prod_documents/AVR109.zip AVR109.zip]&lt;br /&gt;
&lt;br /&gt;
LCD Library von Peter Fleury (original):&amp;lt;br /&amp;gt;&lt;br /&gt;
* http://www.peterfleury.epizy.com/avr-software.html&lt;br /&gt;
&lt;br /&gt;
aktuelle Target Layout Software (kostenlose Demo):&amp;lt;br /&amp;gt;&lt;br /&gt;
* http://ibfriedrich.com/index.htm&lt;br /&gt;
&lt;br /&gt;
=== Danksagung ===&lt;br /&gt;
Neben allen anderen, die an der Erstellung der Librarys für dieses Projekt mitgewirkt haben, möchte ich mich bei Klaus Leidinger bedanken, der mich zu diesem Wiki-Artikel ermuntert hat.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Infrarot]]&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=I%C2%B2C&amp;diff=101258</id>
		<title>I²C</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=I%C2%B2C&amp;diff=101258"/>
		<updated>2019-10-30T21:15:28Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Bibliotheken */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;I²C&#039;&#039;&#039; (gesprochen &amp;quot;I quadrat C&amp;quot;) ist ein synchroner serieller Zweidraht-[[Bus]], der jeweils eine bidirektionale Daten- und Taktleitung verwendet und für die Kommunikation zwischen [[IC]]s über kleine Distanzen geeignet ist. Die Bezeichnung steht für IIC, Inter-Integrated Circuit. Der Bus wurde Anfang der 80er Jahre von Philips entwickelt.&lt;br /&gt;
&lt;br /&gt;
Aus Lizenzgründen heißt der I²C Bus bei manchen Herstellern auch &#039;&#039;&#039;TWI&#039;&#039;&#039;, two wire interface (siehe [[AVR TWI]]). Im PC wird ein dem I²C-Bus sehr ähnliches System benutzt, um z.&amp;amp;nbsp;B. die Daten eines SDRAM-Modules auszulesen. Dieser nennt sich [[SMBus]] (System Management Bus).&lt;br /&gt;
&lt;br /&gt;
=== Busstruktur und Adressierung ===&lt;br /&gt;
In einem I²C-Bus gibt es mindestens einen Master sowie bis zu 127 Slaves. Ein I²C-Bus mit mehreren Mastern wird als &amp;quot;Multi-Master-Bus&amp;quot; bezeichnet. &lt;br /&gt;
&lt;br /&gt;
Die Slaves in einem Verbund müssen alle mit einer eigenen, individuellen Adresse codiert werden. Somit sind sie für jedem Master individuell anzusprechen. Darüber hinaus existiert ein sogenannter Broadcastkanal, mit dem alle Slaves gleichzeitig angesprochen werden können. &lt;br /&gt;
&lt;br /&gt;
Ein Problem mit manchen Slaves ist der eingeschränkte Adressbereich, d.h. sie liegen auf einem definierten Bereich und haben darin z.b. nur 3 Bit als Programmieroption. Somit schließen diese Slaves indirekt andere Adressen aus.&lt;br /&gt;
&lt;br /&gt;
=== Adressierung ===&lt;br /&gt;
Der (oder die) Master sprechen die Slaves jeweils aktiv an. Slaves können &#039;&#039;&#039;nie&#039;&#039;&#039; selbstständig beginnen, Daten zu senden. Um eine Kommunikation zu etablieren, übernimmt der Master, der Daten senden oder empfangen möchte, den Bus und gibt die (7-bit- bzw. 10-bit-)Adresse des Slaves aus, mit dem er kommunizieren möchte. &lt;br /&gt;
Nach der Adresse teilt der Master dem entsprechenden Slave mit, ob er Daten senden oder empfangen möchte. Danach werden die eigentlichen Daten (entweder vom Master oder Slave) auf den Bus gelegt. Hat der Master den Lese- oder Schreibvorgang abgeschlossen, so gibt er den Bus wieder frei.&lt;br /&gt;
Sofern mehrere Master vorhanden sind, stellt ein logisches Protokoll sicher, dass sich diese nicht gegenseitig stören. Im Fall einer broad cast Operation kann ein time slot System sicherstellen, dass nie mehr als ein slave gleichzeitig antwortet.&lt;br /&gt;
&lt;br /&gt;
=== Handshaking / Quittierung ===&lt;br /&gt;
Die Kommunikation auf Bitebene erfolgt seitens des Masters durch Senden von Start / Stopp Bedingungen, die sich aus der Kombination der Zustände von Takt- und Datenleitung ergeben. Um die erfolgreiche Kommunikation zwischen Master und Slave sicherzustellen, senden Slaves - ausser im broadcast Betrieb - nach erfolgreicher Dekodierung ihrer Adresse sowie im Schreibfall ein Acknowledge, in dem sie die Datenleitung ziehen. Diese muss dazu vom Master rechtzeitig freigegeben worden sein und beobachtet werden. Umgekehrt quittiert der Master in ähnlicher Weise den Empfang eines Datenbytes vom Slave und signalisiert somit weitere Empfangsbereitschaft. Der Takt wird immer vom Master getrieben.&lt;br /&gt;
&lt;br /&gt;
=== Übertragungsraten  ===&lt;br /&gt;
Die genormte Übertragungsrate beträgt beim sogenannten &amp;quot;standard mode&amp;quot; 100 kbit/s, beim &amp;quot;fast mode&amp;quot; 400 kbit/s und beim &amp;quot;fast mode+&amp;quot; 1000kbit/sec. Im sogenannten high-speed mode, der mit etwas anderen Spannungs- und Stromrandbedingungen arbeitet sind es zu 3,4 MBit/s. Die Raten beziehen sich auf die festgelegten Taktraten. Andere sind möglich, jedoch nicht genormt.&lt;br /&gt;
&lt;br /&gt;
Die reale maximale Datenrate ist infolge des asynchronen Betriebs und der Pausen meistens etwas niedriger. Zudem kann - falls die Taktrate für einen Slave zu hoch ist - die Clock-Leitung auf Null durch ihn auf GND gezogen- und damit die Übertragung verlangsamt werden, was als sog. &amp;quot;Clock Stretching&amp;quot; bezeichnet wird. Dies ist auf Bit- wie auf Byte-Ebene möglich; ersteres allerdings nicht im high-speed mode.&lt;br /&gt;
&lt;br /&gt;
==== Reale Beispiele ====&lt;br /&gt;
Um sich an längere Übertragungswege anzupassen, kann man die Taktrate theoretisch beliebig vermindern, jedoch erzeugen einige Bausteine aber irgendwann ein Time-Out. Manche interpretieren einen lange niedergehaltenen Takt auch als Reset. Daher sind Taktraten von unter 1kbps in der Regel nicht denkbar.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel können jedoch mit einer Taktfrequenz von nur 5 kbit/s durchaus mehrere Meter überbrückt werden:&lt;br /&gt;
&lt;br /&gt;
Erfahrungen mit den Atmegas (gemessene I2C-Taktfrequenzen):&lt;br /&gt;
* 16,000MHz - schafft sauber bis zu 700KHz&lt;br /&gt;
* 18,432MHz - schafft sauber bis zu 950KHz&lt;br /&gt;
&lt;br /&gt;
Selbst unter schlechten Bedingungen wie...&lt;br /&gt;
&lt;br /&gt;
* 50cm 6-adriges Spiralkabel, frei schwingend verbaut in einem Rennwagen als Verbindung zwischen Lenkraddisplay und Amaturenbrett&lt;br /&gt;
* Nähe zu nicht geschirmten Leitungen mit 12V, GND und 5V PWM zum Dimmen von LEDs&lt;br /&gt;
* grausamen Umgebungsbedingungen, Fahrten im Regen, Schnee oder bei glühender Sonne und gefühlten 100° C ;-) auf der Rennstrecke.&lt;br /&gt;
&lt;br /&gt;
... hat der Bus ohne Probleme funktioniert.&lt;br /&gt;
&lt;br /&gt;
Um anders herum hohe Bandbreiten zu erreichen, lassen sich krumme I2C-Frequenzen ausserhalb der SPEC verwenden, damit sie besser zu den Controllern und deren Taktfrequenzen passen.&lt;br /&gt;
&lt;br /&gt;
=== Bausteine ===&lt;br /&gt;
&lt;br /&gt;
Neben Mikrocontrollern gibt es eine Reihe von Peripheriebausteinen, die per I²C angeschlossen werden können. Eine gute Anlaufstelle bei der Suche ist die unten angegebene Seite des &amp;quot;Erfinders&amp;quot; Philips, heute als [http://www.nxp.com NXP] bekannt.&lt;br /&gt;
&lt;br /&gt;
*serielle [[Speicher#EEPROM | EEPROM]]s &lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?text=24C01&amp;amp;action=Search 24Cxx]&lt;br /&gt;
** [http://ics.nxp.com/products/pcf/seeproms/ PCF85xx von NXP, 256-2048 Byte]&lt;br /&gt;
&lt;br /&gt;
*I/O-Portexpander&lt;br /&gt;
** [[Port-Expander_PCF8574|PCF8574]]&lt;br /&gt;
** [http://www.mikrocontroller.net/part/MCP23008 MCP23008] (8-bit) von Microchip&lt;br /&gt;
** [http://www.mikrocontroller.net/part/MCP23017 MCP23017](16-bit) von Microchip&lt;br /&gt;
&lt;br /&gt;
* I2C MUX, zum Anschluss von ICs mit gleicher, fester Adresse&lt;br /&gt;
** [http://www.datasheetcatalog.org/cgi-bin/helo.pl?field=Nume&amp;amp;type=C&amp;amp;text=PCA9545A&amp;amp;producedby=&amp;amp;action=Search PCA9545A]&lt;br /&gt;
&lt;br /&gt;
* [[AD-Wandler]]&lt;br /&gt;
** [http://www.mikrocontroller.net/topic/182614 12x12 Bit ADC MAX1238]&lt;br /&gt;
** [http://www.mikrocontroller.net/topic/182614 12x10 Bit ADC MAX1138]&lt;br /&gt;
** [http://www.maxim-ic.com/datasheet/index.mvp/id/1890 MAX127 und MAX128 von Maxim, 12bit x8, PDIP24+SSOP28]&lt;br /&gt;
** [http://www.jtronics.de/platinen.html 12x8  Bit ADC MAX1038]&lt;br /&gt;
&lt;br /&gt;
* [[DA-Wandler]]&lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?field=Nume&amp;amp;type=C&amp;amp;text=TDA8444&amp;amp;producedby=&amp;amp;action=Search TDA8444, 8x6Bit]&lt;br /&gt;
&lt;br /&gt;
* Uhrenbausteine&lt;br /&gt;
** [http://ww1.microchip.com/downloads/en/DeviceDoc/20005010F.pdf MCP7940N]&lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?field=Nume&amp;amp;type=C&amp;amp;text=PCF8583&amp;amp;producedby=&amp;amp;action=Search PCF8583, mit 256 Bytes RAM]&lt;br /&gt;
** DS1307&lt;br /&gt;
&lt;br /&gt;
* [[LCD]]-Treiber&lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?field=Nume&amp;amp;type=C&amp;amp;text=PCF8577&amp;amp;producedby=&amp;amp;action=Search PCF8577, 2x32 Segmente]&lt;br /&gt;
&lt;br /&gt;
*[[LED]]-Treiber&lt;br /&gt;
** [http://www.mikrocontroller.net/part/HT16K33 HT16K33] (RAM Mapping 16*8 LED) von Holtek&lt;br /&gt;
** SAA1064 (seit 2005 abgekündigt! Restposten sehr teuer)&lt;br /&gt;
&lt;br /&gt;
* [[Temperatursensor|Temperatursensoren]]&lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?text=DS1621&amp;amp;action=Search DS1621]&lt;br /&gt;
** [http://www.datasheetcatalog.net/cgi-bin/helo.pl?text=lm75&amp;amp;action=Search LM75]&lt;br /&gt;
** [http://www.sensirion.com/en/pdf/product_information/Datasheet-humidity-sensor-SHT21.pdf SHT21]&lt;br /&gt;
** TMP101 von TexasInstruments&lt;br /&gt;
** TMP175 von TI (mehr als 8 Bausteine im gleichen Bus möglich)&lt;br /&gt;
&lt;br /&gt;
* Drucksensoren&lt;br /&gt;
** SMD500&lt;br /&gt;
** BMP085&lt;br /&gt;
&lt;br /&gt;
* Beschleunigungssensor&lt;br /&gt;
** [http://www.mikrocontroller.net/articles/BMA020 BMA020]&lt;br /&gt;
&lt;br /&gt;
* Audioverstärker&lt;br /&gt;
** TPA2016&lt;br /&gt;
** TPA2026&lt;br /&gt;
** TDA8594&lt;br /&gt;
&lt;br /&gt;
* TPMs (Trusted Platform Module)&lt;br /&gt;
** [http://www.infineon.com/cms/de/product/security-and-smart-card-solutions/optiga-embedded-security-solutions/optiga-tpm/channel.html?channel=5546d462503812bb015066de24291768 SLB 9645]&lt;br /&gt;
&lt;br /&gt;
=== Galvanische Trennung ===&lt;br /&gt;
* http://www.analog.com/static/imported-files/application_notes/AN_913.pdf&lt;br /&gt;
* http://www.analog.com/en/interface/digital-isolators/products/index.html#Isolated_I2C_Isolators&lt;br /&gt;
* http://www.silabs.com/Support%20Documents/TechnicalDocs/Si840x.pdf &#039;&#039;&#039;(Not Recommended for New Designs)&#039;&#039;&#039;&lt;br /&gt;
* http://www.silabs.com/applications/industrial/Pages/i2c-isolation.aspx&lt;br /&gt;
* http://www.mikrocontroller.net/attachment/5670/Galvanische_Trennung_fuer_I2C-Bus.pdf&lt;br /&gt;
* http://www.esacademy.com/en/library/technical-articles-and-documents/miscellaneous/i2c-bus/frequently-asked-questions/i2c-faq.html&lt;br /&gt;
* http://www.mikrocontroller.net/topic/17425#125464&lt;br /&gt;
* https://www.mikrocontroller.net/articles/Modulares_Board#I2C_Bus_Isolator&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AT91-TWI]]&lt;br /&gt;
* [[AVR TWI]]&lt;br /&gt;
* [[I2C als Hausbus]]&lt;br /&gt;
* [[Modulares Board]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/181115#1748880 Forumsbeitrag]: I2C-Adressen mit 7 oder 8 Bit&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/385874#4410598 Forumsbeitrag]: I2C über lange Entfernungen als differentielles Signal übertragen.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://www.nxp.com/products/interfaces/ic-bus:MC_41735 NXP (ehemals Philips) Produktseite]&lt;br /&gt;
* [http://www.nxp.com/acrobat/usermanuals/UM10204_3.pdf I2C Spezifikation 3.0]&lt;br /&gt;
* [http://www.i2c-bus.org I²C Bus FAQ, Einführung, Hintergrundinformationen zum I2C Bus auf Deutsch und Englisch]&lt;br /&gt;
* [http://www.robotikhardware.de/download/rn_pc_i2c.pdf robotikhardware.de]&lt;br /&gt;
* [http://www.elektronikwissen.net/pegelwandler/7-bidirektionaler-levelshifter-fuer-i2c.html Betrieb von 3V3 und 5V-Bausteinen am I2C-Bus]&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken ===&lt;br /&gt;
* [http://www.jtronics.de/avr-projekte.html AVR TWI Slave]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/235733#2398750 TWI MASTER in ASM]&lt;br /&gt;
* [http://www.peterfleury.epizy.com/avr-software.html AVR TWI/I2C MASTER in C (Peter Fleury)]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/87597 AVR TWI Master und Slave Funktionen in C]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/266642 universelle I2C Master Prozedur für AVR]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/103600 I2C (TWI) Sniffer mit AVR]&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/334653#3697582 I2CLCD], HD44780 LCD Ansteuerung über I2C Bus&lt;br /&gt;
&lt;br /&gt;
===I²C-Interface===&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/155136#1462320 Beitrag &amp;quot;i2c usb interface&amp;quot;] Diskussion zu Interfaces für Linux&lt;br /&gt;
* [http://sprut.de/electronic/pic/projekte/usb4all/usb4all.htm USB4ALL bei sprut.de] universeller USB-Baustein, auch für I2C (5V, CDC/Virtual-Comport)&lt;br /&gt;
* [http://www.totalphase.com/products/aardvark-i2cspi/ Aardvark I2C/SPI Host Adapter], Universeller Adapter für I2C und SPI&lt;br /&gt;
* [http://www.elv.de/usb-i2c-interface-komplettbausatz-inkl-gehaeuse-bearbeitet-und-bedruckt-usb-kabel-3-anschlusskabel.html USB-I2C-Interface] Komplettbausatz von ELV (5V, Virtual-Comport)&lt;br /&gt;
* [https://github.com/harbaum/I2C-Tiny-USB i2c-tiny-usb] Open Source/Hardware (5V, Linux-Kernel-Treiber, C/C++ Beispiele)&lt;br /&gt;
* [https://www.fischl.de/i2c-mp-usb/ I2C-MP-USB] Open Source (3,3V und 5V, Linux-Kernel-Treiber, Python-Bibliothek für Windows/MacOS/Linux)&lt;br /&gt;
&lt;br /&gt;
===I²C-Monitor===&lt;br /&gt;
*http://realterm.sourceforge.net/#I2C%20Bus&lt;br /&gt;
*http://www.avrfreaks.net/index.php?module=FreaksTools&amp;amp;func=viewItem&amp;amp;item_id=411&lt;br /&gt;
* [http://www.telos.info/p/hw/conniimm20/ telos Connii MM 2.0 - I²C Monitor with USB Interface]&lt;br /&gt;
* [http://www.telos.info/p/hw/traciixl20/ telos Tracii XL 2.0 - High-End I²C Monitor]&lt;br /&gt;
*http://i2cchip.com/&lt;br /&gt;
* [http://www.ullihome.de/index.php/Hauptseite#USB_AVR-Lab I2C Logger Firmware für USB AVR-ISP]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/41535 I2C Monitor mit Mega8 (Firmware)]&lt;br /&gt;
* [http://warmcat.com/milksop/cheapi2c.html Cheapi2c] by Numbnut (Andy Green) uses your PC CPU (Linux only!) and printer port to perform realtime snooping of a standard 100kHz I2C bus with 100% capture.&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/103600#905495 I2C (TWI) Sniffer]. Basierend auf einem ATtiny85 mit Ausgabe auf RS232 (C und ASM).&lt;br /&gt;
* [http://en.radzio.dxp.pl/i2c-sniffer/ I2C/TWI bus sniffer/analyzer] (ATTiny2313 @ 20 MHz und USB über FTDI FT245RL oder UM245R)&lt;br /&gt;
* [http://www.i2cchip.com/start_bit_detector1.jpg I2C Startbit-Detector] auf [http://www.i2cchip.com/ www.i2cchip.com]&lt;br /&gt;
&lt;br /&gt;
[[Category:I2C| ]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=101257</id>
		<title>HD44780</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=101257"/>
		<updated>2019-10-30T21:11:58Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der HD44780 ist ein Steuer-IC für Textdisplays (siehe [[LCD]]). Praktisch alle Textdisplays werden mit diesem oder einem kompatiblen Controller (z.&amp;amp;nbsp;B. KS0066) angesteuert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Pinbelegung ===&lt;br /&gt;
&lt;br /&gt;
Die Pinbelegung des ICs selber ist für den Anwender praktisch uninteressant. Hier ist die häufigste Anschluss-Belegung der damit ausgestatteten LCD-Module angegeben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;ACHTUNG: Es gibt Displays mit abweichender Anschluss-Belegung, falscher Anschluss kann zur Zerstörung führen! Daher immer das zugehörige Datenblatt zu Rate ziehen.&amp;lt;/span&amp;gt; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!style=&amp;quot;width:5%&amp;quot; | Pin || style=&amp;quot;width:10%&amp;quot; | Funktion || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|1 || | U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt; || Bezugspotenzial    0 Volt&lt;br /&gt;
|-&lt;br /&gt;
|2 || | U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || Speisespannung  + 5 Volt, ≈ 1 mA&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|3 || | U&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; ||Kontrastspannung, zwischen U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; und U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt;, typisch 0..1 V, kann auf U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt; gelegt oder via Potenziometer (Richtwert 10 kΩ) angeschlossen werden.&amp;lt;BR&amp;gt;Bei großflächigen LCDs oder LCDs für den erweiterten Temperaturbereich kann auch eine negative Kontrastspannung im Bereich von 0..–5 V nötig sein.&lt;br /&gt;
|-&lt;br /&gt;
|4 || | RS || Registerauswahl&amp;lt;BR&amp;gt;0 = Befehlsregister, 1 = Datenregister &lt;br /&gt;
|-&lt;br /&gt;
|5 || | R/&amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;W&amp;lt;/span&amp;gt; || Lese- oder Schreibzugriff; kann fest auf GND gelegt werden, wenn Busy-Auswertung durch Warten ersetzt wird&amp;lt;BR&amp;gt;0 = Schreiben, 1 = Lesen &lt;br /&gt;
|-&lt;br /&gt;
|6 || | E || Freigabe- und Taktleitung (Achtung! Diese Leitung hat im Gegensatz zu den anderen bei einigen Displays keinen internen Pullup, man muss also einen externen vorsehen, falls man mit Open-Drain-Ausgängen arbeitet.)&lt;br /&gt;
|-&lt;br /&gt;
|7 || | DB0 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|8 || |DB1 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|9 || |DB2 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|10 || |DB3 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|11 || |DB4 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|12 || |DB5 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|13 || |DB6 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|14 || |DB7 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|15 || A || Anode der LED-Hintergrundbeleuchtung (fakultativ)&lt;br /&gt;
|-&lt;br /&gt;
|16 || K || Katode der LED-Hintergrundbeleuchtung (fakultativ)&lt;br /&gt;
|-&lt;br /&gt;
|   ||   || Es gibt auch Displays ohne die Datenleitungen DB0-3 (werden nur im 4-Bit-Modus angesteuert), dann rutschen die Bezeichnungen dann entsprechend nach unten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Speicher===&lt;br /&gt;
&lt;br /&gt;
Ein HD44780 besitzt mehrere Speicher. In ihnen wird der Inhalt des Displays sowie das Aussehen von Sonderzeichen gespeichert.&lt;br /&gt;
&lt;br /&gt;
==== CGROM  ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;ROM&#039;&#039;&#039; enthält die Zeichen in Form von 5x8 oder 5x10 Punktmatrizen. Er kann nicht geändert werden, ausser man sendet einen EEPROM zu Hitachi für eine Massenproduktion. Es sind viele verschiedene ROMs (Zeichensätze, engl. Fonts) verfügbar. Er ist für den Anwender nicht zugänglich.&lt;br /&gt;
&lt;br /&gt;
==== CGRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;RAM&#039;&#039;&#039; können acht 5x8 Pixel oder vier 5x10 Pixel große benutzerdefinierte Zeichen abgelegt werden. Braucht man keine benutzerdefinierten Zeichen, kann man diesen Bereich als Auslagerungsspeicher für den ansteuernden Mikrocontroller benutzen. Dazu muss aber die &#039;&#039;&#039;R/W&#039;&#039;&#039; Leitung angesteuert werden.&lt;br /&gt;
&lt;br /&gt;
==== DDRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;D&#039;&#039;&#039;isplay &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;RAM&#039;&#039;&#039; ist der Inhalt des LCDs gespeichert. Die Kodierung orientiert sich weitestgehend am ASCII Zeichensatz.&lt;br /&gt;
&lt;br /&gt;
Das DDRAM hat üblicherweise mehr Speicherstellen als das Display gleichzeitig anzeigt. Durch Anwenden der Display-Shift-Befehle können die nicht sichtbaren Zeichen zur Anzeige gebracht werden.  &lt;br /&gt;
&lt;br /&gt;
Benachbarte Zeichen haben im DDRAM nicht zwangsweise auch aufeinanderfolgende Adressen. So ist etwa bei 1x16-Displays (1 Zeile, 16 Zeichen) oft in der Mitte des Displays ein Adresssprung von 0x07 auf 0x40; bei 2x16-Displays ist der Sprung auf 0x40 normalerweise (programmiererfreundlich) zwischen Ende der ersten und Beginn der zweiten Zeile. Näheres ist dem Datenblatt zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
=== Kommandos ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;HD44780 Befehlssatz&#039;&#039;&#039;&lt;br /&gt;
!style=&amp;quot;width:30%&amp;quot; | Befehl&lt;br /&gt;
!RS&lt;br /&gt;
!RW&lt;br /&gt;
!D7&lt;br /&gt;
!D6&lt;br /&gt;
!D5&lt;br /&gt;
!D4&lt;br /&gt;
!D3&lt;br /&gt;
!D2&lt;br /&gt;
!D1&lt;br /&gt;
!D0&lt;br /&gt;
|-&lt;br /&gt;
|Bildschirminhalt löschen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|Cursor auf Startpos&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Modus festlegen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|I/D&lt;br /&gt;
|S&lt;br /&gt;
|-&lt;br /&gt;
|Display/Cursor &lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|D&lt;br /&gt;
|C&lt;br /&gt;
|B&lt;br /&gt;
|-&lt;br /&gt;
|Cursor/Display schieben&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|S/C&lt;br /&gt;
|R/L&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Funktionen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|DL&lt;br /&gt;
|N&lt;br /&gt;
|F&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|CGRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan =&amp;quot;6&amp;quot; align=&amp;quot;center&amp;quot;|CGRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|DDRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Adresse/Status lesen&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|BF&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|CG-/DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Daten in DDRAM/CGRAM schreiben&lt;br /&gt;
|1&lt;br /&gt;
|0&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|-&lt;br /&gt;
|Daten aus DDRAM/CGRAM lesen&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Kodierung der Steuerbits&#039;&#039;&#039;&lt;br /&gt;
!Bit&lt;br /&gt;
!0&lt;br /&gt;
!1&lt;br /&gt;
|-&lt;br /&gt;
|I/D  &lt;br /&gt;
|Cursorposition dekrementieren &lt;br /&gt;
|Cursorposition inkrementieren  &lt;br /&gt;
|-&lt;br /&gt;
|S&lt;br /&gt;
|Displayinhalt fest&lt;br /&gt;
|Displayinhalt weiterschieben&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|Display aus&lt;br /&gt;
|Display an&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|Cursor aus&lt;br /&gt;
|Cursor an&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|Cursor blinkt nicht&lt;br /&gt;
|Cursor blinkt&lt;br /&gt;
|-&lt;br /&gt;
|S/C&lt;br /&gt;
|Cursor bewegen&lt;br /&gt;
|Displayinhalt schieben&lt;br /&gt;
|-&lt;br /&gt;
|R/L&lt;br /&gt;
|Nach links schieben&lt;br /&gt;
|Nach rechts schieben&lt;br /&gt;
|-&lt;br /&gt;
|DL&lt;br /&gt;
|4-Bit Interface&lt;br /&gt;
|8-Bit Interface&lt;br /&gt;
|-&lt;br /&gt;
|N&lt;br /&gt;
|1-zeiliges Display&lt;br /&gt;
|2/4-zeiliges Display&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|5&amp;amp;times;7-Font&lt;br /&gt;
|5&amp;amp;times;10-Font&lt;br /&gt;
|-&lt;br /&gt;
|BF&lt;br /&gt;
|Kann Kommandos annehmen&lt;br /&gt;
|Ist beschäftigt &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung per Microcontroller ==&lt;br /&gt;
&lt;br /&gt;
Die Ansteuerung eines HD44780-basierten Displays gestaltet sich sowohl in Bezug auf den Hardware- als auch den Softwareaufwand recht einfach. Sie eignet sich somit sehr gut als Übungsprojekt für Anfänger.&lt;br /&gt;
&lt;br /&gt;
Das HD44780-Interface besteht aus acht Datenleitungen (D0-D7) sowie den drei Steuerleitungen RS (&#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;S&#039;&#039;&#039;elect), R/W (&#039;&#039;&#039;R&#039;&#039;&#039;ead/&#039;&#039;&#039;W&#039;&#039;&#039;rite) und E (&#039;&#039;&#039;E&#039;&#039;&#039;nable). Die Displays werden mit 5V Betriebsspannung versorgt, die Kontrastspannung V0 bekommt man, indem man ein 10k&amp;amp;Omega;-[[Potentiometer]] zwischen VCC und GND anschließt und den mittleren Anschluss als Kontrastspannung verwendet. Bei ca. 0,5 Volt werden dann die Pixel sichtbar. Optional haben viele Displays noch eine LED-Beleuchtung eingebaut, diese kann manchmal direkt an 5V angeschlossen werden, da der Vorwiderstand auf der LCD-Platine integriert ist. Oft ist aber auch ein externer Vorwiderstand nötig! Darum vorher besser im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Möchte man IO-Pins am Mikrocontroller sparen, kann das Display im 4-Bit-Modus betrieben werden, die Dateneingänge D0 bis D3 bleiben dann offen. Durch zusätzliche Bauteile ([[AVR-Tutorial:_Schieberegister]]) kann die Zahl der benötigten Port-Pins noch weiter reduziert werden.&lt;br /&gt;
&lt;br /&gt;
Beispielprogramme zur Ansteuerung findet man zuhauf im Internet, u.a. auch hier: [[AVR-Tutorial: LCD]] und [[AVR-GCC-Tutorial/LCD-Ansteuerung|AVR-GCC-Tutorial: LCD-Ansteuerung]]&lt;br /&gt;
&lt;br /&gt;
=== Fertige Projekte oder Bibliotheken ===&lt;br /&gt;
&lt;br /&gt;
* Peter Fleurys [http://www.peterfleury.epizy.com/avr-software.html LCD library for HD44780 based LCDs]&lt;br /&gt;
* avr-libc [http://www.nongnu.org/avr-libc/user-manual/group__stdiodemo.html Demo-Projekt für stdio]&lt;br /&gt;
* [http://www.ipd.uka.de/~buchmann/microcontroller/lcd.htm Grundlagen und Code für 8051] von Erik Buchmann&lt;br /&gt;
* [http://www.ekenrooi.net/lcd/lcd.shtml &#039;&#039;How to control a HD44780-based Character-LCD&#039;&#039;] von Peter Ouwehand. Beispiele für 8051, PIC16C54, AT90S2313&lt;br /&gt;
* Firma wickenhäuser [http://www.wickenhaeuser.de 8051 Compiler und Treiber für LCDs ]&lt;br /&gt;
&lt;br /&gt;
== Sonderzeichen ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| \40  ! || \41  &amp;quot; || \42  # || \43  $ || \44  % || \45  &amp;amp; || \50  ( || \51  ) || \52  * || \53  Plus-Zeichen  &lt;br /&gt;
|-&lt;br /&gt;
| \54  Komma || \55  Minus-Zeichen || \56  Punkt || \57  Schrägstrich || \72  Doppelpunkt || \73  Semikolon || \74  &amp;lt; || \75  Gleichheitszeichen || \76  &amp;gt; || \77  ? &lt;br /&gt;
|-&lt;br /&gt;
| \100  @ || \134  eckige Klammer links || \136  eckige Klammer rechts || \137  accent circonflexe || \138  Unterstrich || \140  accent grave || \173  geschweifte Klammer links || \174  senkrechter Strich || \175  geschweifte Klammer rechts || \176  Pfeil nach rechts&lt;br /&gt;
|-&lt;br /&gt;
| \177  Pfeil nach links || \260  Minuszeichen || \333  Kastenrahmen || \337  hochgestellter Kastenrahmen(wie Potenz) || \340  gr. alpha || \341  ä || \342  β (als ß brauchbar) || \343  ε klein epsilon || \344  µ || \350  √ Wurzelzeichen  &lt;br /&gt;
|-&lt;br /&gt;
| \351  hoch minus 1 || \353  hoch x || \356  ñ (spanisch) || \357  ö || \363  ∞ Zeichen unendlich || \364  Ohm || \365  ü || \366  Σ gr.Summe || \367  π (klein) || \371  u mit strich rechts unten&lt;br /&gt;
|-&lt;br /&gt;
| \375  geteilt durch || \377  alle Leuchtpunkte eingeschaltet || || || || || || || ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für die 5 Kleinbuchstaben mit Unterlänge &#039;g&#039;, &#039;j&#039;, &#039;p&#039;, &#039;q&#039; und &#039;y&#039; stehen mit Offset 80h solche &#039;&#039;mit&#039;&#039; tatsächlicher Unterlänge zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Im 7-Bit-ASCII-Bereich passen nur die Zeichen &#039;\&#039; (Backslash) und &#039;~&#039; (Tilde) nicht. Zur Wiedergabe portabler ASCII-Daten (typischerweise aus externer Quelle, etwa Dateinamen einer SD-Karte oder SMS-Text) muss man diese beiden Kodepositionen in 2 der 8 möglichen benutzerdefinierten Zeichen transkodieren.&lt;br /&gt;
&lt;br /&gt;
==== Wie nutzt man diese Sonderzeichen in C oder C++? ====&lt;br /&gt;
&lt;br /&gt;
Angegeben sind die &#039;&#039;Oktalnotationen&#039;&#039; der Sonderzeichen. Diese bettet man dreistellig in den String ein: &amp;lt;code&amp;gt;&amp;quot;\3502&amp;quot;&amp;lt;/code&amp;gt; ergibt &amp;lt;kbd&amp;gt;√2&amp;lt;/kbd&amp;gt; auf der Anzeige und belegt 2 Bytes (mit der terminierenden Null 3 Bytes) im Programm- bzw. Datenspeicher.&lt;br /&gt;
&lt;br /&gt;
Ist diese Notation für häufig benutzte Symbole zu unleserlich oder lästig, nutzt man die compilerseitige Stringkonstanten-Verkettung:&lt;br /&gt;
    #define WURZEL &amp;quot;\350&amp;quot;&lt;br /&gt;
    ...&lt;br /&gt;
      emit (WURZEL &amp;quot;2&amp;quot;)&lt;br /&gt;
    // dasselbe wie emit (&amp;quot;\350&amp;quot; &amp;quot;2&amp;quot;) und dasselbe wie emit (&amp;quot;\3502&amp;quot;)&lt;br /&gt;
    // Das Leerzeichen zwischen WURZEL und &amp;quot;2&amp;quot; ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Damit &amp;lt;code&amp;gt;&amp;quot;√2&amp;quot;&amp;lt;/code&amp;gt; &#039;&#039;im Programmquelltext&#039;&#039; funktioniert, ist 1. der Quelltext in UTF-8 zu schreiben und 2. eine UTF-8-zu-HD44780-Konvertierroutine zu schreiben. Das kommt allenfalls für größere Mikrocontroller in Frage.&lt;br /&gt;
&lt;br /&gt;
=== Benutzerdefinierte Sonderzeichen  ===&lt;br /&gt;
* Benutzerspezifische Zeichen für den HD44780 mit dem [http://www.mugui.de/bin/menu.php?link=hd44780_demo&amp;amp;lang=de Font and Bitmap Generator] erstellen&lt;br /&gt;
* http://www.parallax.com/ProductInfo/Microcontrollers/BASICStampSoftware/LCDCharacterCreator/tabid/482/Default.aspx&lt;br /&gt;
&lt;br /&gt;
Mit den benutzerdefinierten Sonderzeichen steht für 8 Zeichenpositionen Vollgrafik zur Verfügung,&lt;br /&gt;
so dass sich bspw. einfache Signalverläufe in einem 40×8-Pixel-Feld darstellen lassen.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[AVR-GCC-Tutorial/LCD-Ansteuerung]]&lt;br /&gt;
* [[Erweiterte LCD-Ansteuerung]]&lt;br /&gt;
* [[Pseudo-Graphische LCD-Ansteuerung]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/316826#3431235 Forumsbeitrag]: Ermittlung der Startadresse der einzelnen Zeilen&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/387596?goto=4438017#4438017 Forumsbeitrag]: HD44780 Extender, transparente Serialisierung der Ansteuerung im 4 oder 8 Bit Modus&lt;br /&gt;
* [https://omerk.github.io/lcdchargen/ Custom Character Generator for HD44780 LCD Modules]&lt;br /&gt;
* [http://blog.riyas.org/2013/12/online-led-matrix-font-generator-with.html Online led matrix font generator with binary and hex codes for Arduino ]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.sprut.de/electronic/lcd/ Ausführliche Beschreibung des HD44780]&lt;br /&gt;
* [http://www.peterfleury.epizy.com/avr-software.html#libs Lib zur HD44780 Ansteuerung (AVR)]&lt;br /&gt;
* [http://pic-projekte.de/wordpress/?p=908 Ansteuern des HD44780 mit den XC8/C18-Libraries - PIC]&lt;br /&gt;
* [http://www.fonts2u.com/hd44780-regular.font TTF Matrix Pixel Font]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;br /&gt;
[[Category:LCD]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=101256</id>
		<title>HD44780</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=101256"/>
		<updated>2019-10-30T21:10:17Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Fertige Projekte oder Bibliotheken */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der HD44780 ist ein Steuer-IC für Textdisplays (siehe [[LCD]]). Praktisch alle Textdisplays werden mit diesem oder einem kompatiblen Controller (z.&amp;amp;nbsp;B. KS0066) angesteuert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Pinbelegung ===&lt;br /&gt;
&lt;br /&gt;
Die Pinbelegung des ICs selber ist für den Anwender praktisch uninteressant. Hier ist die häufigste Anschluss-Belegung der damit ausgestatteten LCD-Module angegeben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;ACHTUNG: Es gibt Displays mit abweichender Anschluss-Belegung, falscher Anschluss kann zur Zerstörung führen! Daher immer das zugehörige Datenblatt zu Rate ziehen.&amp;lt;/span&amp;gt; &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!style=&amp;quot;width:5%&amp;quot; | Pin || style=&amp;quot;width:10%&amp;quot; | Funktion || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|1 || | U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt; || Bezugspotenzial    0 Volt&lt;br /&gt;
|-&lt;br /&gt;
|2 || | U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || Speisespannung  + 5 Volt, ≈ 1 mA&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|3 || | U&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; ||Kontrastspannung, zwischen U&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; und U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt;, typisch 0..1 V, kann auf U&amp;lt;sub&amp;gt;SS&amp;lt;/sub&amp;gt; gelegt oder via Potenziometer (Richtwert 10 kΩ) angeschlossen werden.&amp;lt;BR&amp;gt;Bei großflächigen LCDs oder LCDs für den erweiterten Temperaturbereich kann auch eine negative Kontrastspannung im Bereich von 0..–5 V nötig sein.&lt;br /&gt;
|-&lt;br /&gt;
|4 || | RS || Registerauswahl&amp;lt;BR&amp;gt;0 = Befehlsregister, 1 = Datenregister &lt;br /&gt;
|-&lt;br /&gt;
|5 || | R/&amp;lt;span style=&amp;quot;text-decoration:overline&amp;quot;&amp;gt;W&amp;lt;/span&amp;gt; || Lese- oder Schreibzugriff; kann fest auf GND gelegt werden, wenn Busy-Auswertung durch Warten ersetzt wird&amp;lt;BR&amp;gt;0 = Schreiben, 1 = Lesen &lt;br /&gt;
|-&lt;br /&gt;
|6 || | E || Freigabe- und Taktleitung (Achtung! Diese Leitung hat im Gegensatz zu den anderen bei einigen Displays keinen internen Pullup, man muss also einen externen vorsehen, falls man mit Open-Drain-Ausgängen arbeitet.)&lt;br /&gt;
|-&lt;br /&gt;
|7 || | DB0 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|8 || |DB1 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|9 || |DB2 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|10 || |DB3 || Datenleitung (bleibt im 4-Bit-Modus offen)&lt;br /&gt;
|-&lt;br /&gt;
|11 || |DB4 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|12 || |DB5 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|13 || |DB6 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|14 || |DB7 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|15 || A || Anode der LED-Hintergrundbeleuchtung (fakultativ)&lt;br /&gt;
|-&lt;br /&gt;
|16 || K || Katode der LED-Hintergrundbeleuchtung (fakultativ)&lt;br /&gt;
|-&lt;br /&gt;
|   ||   || Es gibt auch Displays ohne die Datenleitungen DB0-3 (werden nur im 4-Bit-Modus angesteuert), dann rutschen die Bezeichnungen dann entsprechend nach unten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Speicher===&lt;br /&gt;
&lt;br /&gt;
Ein HD44780 besitzt mehrere Speicher. In ihnen wird der Inhalt des Displays sowie das Aussehen von Sonderzeichen gespeichert.&lt;br /&gt;
&lt;br /&gt;
==== CGROM  ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;ROM&#039;&#039;&#039; enthält die Zeichen in Form von 5x8 oder 5x10 Punktmatrizen. Er kann nicht geändert werden, ausser man sendet einen EEPROM zu Hitachi für eine Massenproduktion. Es sind viele verschiedene ROMs (Zeichensätze, engl. Fonts) verfügbar. Er ist für den Anwender nicht zugänglich.&lt;br /&gt;
&lt;br /&gt;
==== CGRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;RAM&#039;&#039;&#039; können acht 5x8 Pixel oder vier 5x10 Pixel große benutzerdefinierte Zeichen abgelegt werden. Braucht man keine benutzerdefinierten Zeichen, kann man diesen Bereich als Auslagerungsspeicher für den ansteuernden Mikrocontroller benutzen. Dazu muss aber die &#039;&#039;&#039;R/W&#039;&#039;&#039; Leitung angesteuert werden.&lt;br /&gt;
&lt;br /&gt;
==== DDRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;D&#039;&#039;&#039;isplay &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;RAM&#039;&#039;&#039; ist der Inhalt des LCDs gespeichert. Die Kodierung orientiert sich weitestgehend am ASCII Zeichensatz.&lt;br /&gt;
&lt;br /&gt;
Das DDRAM hat üblicherweise mehr Speicherstellen als das Display gleichzeitig anzeigt. Durch Anwenden der Display-Shift-Befehle können die nicht sichtbaren Zeichen zur Anzeige gebracht werden.  &lt;br /&gt;
&lt;br /&gt;
Benachbarte Zeichen haben im DDRAM nicht zwangsweise auch aufeinanderfolgende Adressen. So ist etwa bei 1x16-Displays (1 Zeile, 16 Zeichen) oft in der Mitte des Displays ein Adresssprung von 0x07 auf 0x40; bei 2x16-Displays ist der Sprung auf 0x40 normalerweise (programmiererfreundlich) zwischen Ende der ersten und Beginn der zweiten Zeile. Näheres ist dem Datenblatt zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
=== Kommandos ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;HD44780 Befehlssatz&#039;&#039;&#039;&lt;br /&gt;
!style=&amp;quot;width:30%&amp;quot; | Befehl&lt;br /&gt;
!RS&lt;br /&gt;
!RW&lt;br /&gt;
!D7&lt;br /&gt;
!D6&lt;br /&gt;
!D5&lt;br /&gt;
!D4&lt;br /&gt;
!D3&lt;br /&gt;
!D2&lt;br /&gt;
!D1&lt;br /&gt;
!D0&lt;br /&gt;
|-&lt;br /&gt;
|Bildschirminhalt löschen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|Cursor auf Startpos&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Modus festlegen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|I/D&lt;br /&gt;
|S&lt;br /&gt;
|-&lt;br /&gt;
|Display/Cursor &lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|D&lt;br /&gt;
|C&lt;br /&gt;
|B&lt;br /&gt;
|-&lt;br /&gt;
|Cursor/Display schieben&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|S/C&lt;br /&gt;
|R/L&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Funktionen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|DL&lt;br /&gt;
|N&lt;br /&gt;
|F&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|CGRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan =&amp;quot;6&amp;quot; align=&amp;quot;center&amp;quot;|CGRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|DDRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Adresse/Status lesen&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|BF&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|CG-/DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Daten in DDRAM/CGRAM schreiben&lt;br /&gt;
|1&lt;br /&gt;
|0&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|-&lt;br /&gt;
|Daten aus DDRAM/CGRAM lesen&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Kodierung der Steuerbits&#039;&#039;&#039;&lt;br /&gt;
!Bit&lt;br /&gt;
!0&lt;br /&gt;
!1&lt;br /&gt;
|-&lt;br /&gt;
|I/D  &lt;br /&gt;
|Cursorposition dekrementieren &lt;br /&gt;
|Cursorposition inkrementieren  &lt;br /&gt;
|-&lt;br /&gt;
|S&lt;br /&gt;
|Displayinhalt fest&lt;br /&gt;
|Displayinhalt weiterschieben&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|Display aus&lt;br /&gt;
|Display an&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|Cursor aus&lt;br /&gt;
|Cursor an&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|Cursor blinkt nicht&lt;br /&gt;
|Cursor blinkt&lt;br /&gt;
|-&lt;br /&gt;
|S/C&lt;br /&gt;
|Cursor bewegen&lt;br /&gt;
|Displayinhalt schieben&lt;br /&gt;
|-&lt;br /&gt;
|R/L&lt;br /&gt;
|Nach links schieben&lt;br /&gt;
|Nach rechts schieben&lt;br /&gt;
|-&lt;br /&gt;
|DL&lt;br /&gt;
|4-Bit Interface&lt;br /&gt;
|8-Bit Interface&lt;br /&gt;
|-&lt;br /&gt;
|N&lt;br /&gt;
|1-zeiliges Display&lt;br /&gt;
|2/4-zeiliges Display&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|5&amp;amp;times;7-Font&lt;br /&gt;
|5&amp;amp;times;10-Font&lt;br /&gt;
|-&lt;br /&gt;
|BF&lt;br /&gt;
|Kann Kommandos annehmen&lt;br /&gt;
|Ist beschäftigt &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung per Microcontroller ==&lt;br /&gt;
&lt;br /&gt;
Die Ansteuerung eines HD44780-basierten Displays gestaltet sich sowohl in Bezug auf den Hardware- als auch den Softwareaufwand recht einfach. Sie eignet sich somit sehr gut als Übungsprojekt für Anfänger.&lt;br /&gt;
&lt;br /&gt;
Das HD44780-Interface besteht aus acht Datenleitungen (D0-D7) sowie den drei Steuerleitungen RS (&#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;S&#039;&#039;&#039;elect), R/W (&#039;&#039;&#039;R&#039;&#039;&#039;ead/&#039;&#039;&#039;W&#039;&#039;&#039;rite) und E (&#039;&#039;&#039;E&#039;&#039;&#039;nable). Die Displays werden mit 5V Betriebsspannung versorgt, die Kontrastspannung V0 bekommt man, indem man ein 10k&amp;amp;Omega;-[[Potentiometer]] zwischen VCC und GND anschließt und den mittleren Anschluss als Kontrastspannung verwendet. Bei ca. 0,5 Volt werden dann die Pixel sichtbar. Optional haben viele Displays noch eine LED-Beleuchtung eingebaut, diese kann manchmal direkt an 5V angeschlossen werden, da der Vorwiderstand auf der LCD-Platine integriert ist. Oft ist aber auch ein externer Vorwiderstand nötig! Darum vorher besser im Datenblatt nachschauen. &lt;br /&gt;
&lt;br /&gt;
Möchte man IO-Pins am Mikrocontroller sparen, kann das Display im 4-Bit-Modus betrieben werden, die Dateneingänge D0 bis D3 bleiben dann offen. Durch zusätzliche Bauteile ([[AVR-Tutorial:_Schieberegister]]) kann die Zahl der benötigten Port-Pins noch weiter reduziert werden.&lt;br /&gt;
&lt;br /&gt;
Beispielprogramme zur Ansteuerung findet man zuhauf im Internet, u.a. auch hier: [[AVR-Tutorial: LCD]] und [[AVR-GCC-Tutorial/LCD-Ansteuerung|AVR-GCC-Tutorial: LCD-Ansteuerung]]&lt;br /&gt;
&lt;br /&gt;
=== Fertige Projekte oder Bibliotheken ===&lt;br /&gt;
&lt;br /&gt;
* Peter Fleurys [http://www.peterfleury.epizy.com/avr-software.html LCD library for HD44780 based LCDs]&lt;br /&gt;
* avr-libc [http://www.nongnu.org/avr-libc/user-manual/group__stdiodemo.html Demo-Projekt für stdio]&lt;br /&gt;
* [http://www.ipd.uka.de/~buchmann/microcontroller/lcd.htm Grundlagen und Code für 8051] von Erik Buchmann&lt;br /&gt;
* [http://www.ekenrooi.net/lcd/lcd.shtml &#039;&#039;How to control a HD44780-based Character-LCD&#039;&#039;] von Peter Ouwehand. Beispiele für 8051, PIC16C54, AT90S2313&lt;br /&gt;
* Firma wickenhäuser [http://www.wickenhaeuser.de 8051 Compiler und Treiber für LCDs ]&lt;br /&gt;
&lt;br /&gt;
== Sonderzeichen ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| \40  ! || \41  &amp;quot; || \42  # || \43  $ || \44  % || \45  &amp;amp; || \50  ( || \51  ) || \52  * || \53  Plus-Zeichen  &lt;br /&gt;
|-&lt;br /&gt;
| \54  Komma || \55  Minus-Zeichen || \56  Punkt || \57  Schrägstrich || \72  Doppelpunkt || \73  Semikolon || \74  &amp;lt; || \75  Gleichheitszeichen || \76  &amp;gt; || \77  ? &lt;br /&gt;
|-&lt;br /&gt;
| \100  @ || \134  eckige Klammer links || \136  eckige Klammer rechts || \137  accent circonflexe || \138  Unterstrich || \140  accent grave || \173  geschweifte Klammer links || \174  senkrechter Strich || \175  geschweifte Klammer rechts || \176  Pfeil nach rechts&lt;br /&gt;
|-&lt;br /&gt;
| \177  Pfeil nach links || \260  Minuszeichen || \333  Kastenrahmen || \337  hochgestellter Kastenrahmen(wie Potenz) || \340  gr. alpha || \341  ä || \342  β (als ß brauchbar) || \343  ε klein epsilon || \344  µ || \350  √ Wurzelzeichen  &lt;br /&gt;
|-&lt;br /&gt;
| \351  hoch minus 1 || \353  hoch x || \356  ñ (spanisch) || \357  ö || \363  ∞ Zeichen unendlich || \364  Ohm || \365  ü || \366  Σ gr.Summe || \367  π (klein) || \371  u mit strich rechts unten&lt;br /&gt;
|-&lt;br /&gt;
| \375  geteilt durch || \377  alle Leuchtpunkte eingeschaltet || || || || || || || ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für die 5 Kleinbuchstaben mit Unterlänge &#039;g&#039;, &#039;j&#039;, &#039;p&#039;, &#039;q&#039; und &#039;y&#039; stehen mit Offset 80h solche &#039;&#039;mit&#039;&#039; tatsächlicher Unterlänge zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Im 7-Bit-ASCII-Bereich passen nur die Zeichen &#039;\&#039; (Backslash) und &#039;~&#039; (Tilde) nicht. Zur Wiedergabe portabler ASCII-Daten (typischerweise aus externer Quelle, etwa Dateinamen einer SD-Karte oder SMS-Text) muss man diese beiden Kodepositionen in 2 der 8 möglichen benutzerdefinierten Zeichen transkodieren.&lt;br /&gt;
&lt;br /&gt;
==== Wie nutzt man diese Sonderzeichen in C oder C++? ====&lt;br /&gt;
&lt;br /&gt;
Angegeben sind die &#039;&#039;Oktalnotationen&#039;&#039; der Sonderzeichen. Diese bettet man dreistellig in den String ein: &amp;lt;code&amp;gt;&amp;quot;\3502&amp;quot;&amp;lt;/code&amp;gt; ergibt &amp;lt;kbd&amp;gt;√2&amp;lt;/kbd&amp;gt; auf der Anzeige und belegt 2 Bytes (mit der terminierenden Null 3 Bytes) im Programm- bzw. Datenspeicher.&lt;br /&gt;
&lt;br /&gt;
Ist diese Notation für häufig benutzte Symbole zu unleserlich oder lästig, nutzt man die compilerseitige Stringkonstanten-Verkettung:&lt;br /&gt;
    #define WURZEL &amp;quot;\350&amp;quot;&lt;br /&gt;
    ...&lt;br /&gt;
      emit (WURZEL &amp;quot;2&amp;quot;)&lt;br /&gt;
    // dasselbe wie emit (&amp;quot;\350&amp;quot; &amp;quot;2&amp;quot;) und dasselbe wie emit (&amp;quot;\3502&amp;quot;)&lt;br /&gt;
    // Das Leerzeichen zwischen WURZEL und &amp;quot;2&amp;quot; ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Damit &amp;lt;code&amp;gt;&amp;quot;√2&amp;quot;&amp;lt;/code&amp;gt; &#039;&#039;im Programmquelltext&#039;&#039; funktioniert, ist 1. der Quelltext in UTF-8 zu schreiben und 2. eine UTF-8-zu-HD44780-Konvertierroutine zu schreiben. Das kommt allenfalls für größere Mikrocontroller in Frage.&lt;br /&gt;
&lt;br /&gt;
=== Benutzerdefinierte Sonderzeichen  ===&lt;br /&gt;
* Benutzerspezifische Zeichen für den HD44780 mit dem [http://www.mugui.de/bin/menu.php?link=hd44780_demo&amp;amp;lang=de Font and Bitmap Generator] erstellen&lt;br /&gt;
* http://www.parallax.com/ProductInfo/Microcontrollers/BASICStampSoftware/LCDCharacterCreator/tabid/482/Default.aspx&lt;br /&gt;
&lt;br /&gt;
Mit den benutzerdefinierten Sonderzeichen steht für 8 Zeichenpositionen Vollgrafik zur Verfügung,&lt;br /&gt;
so dass sich bspw. einfache Signalverläufe in einem 40×8-Pixel-Feld darstellen lassen.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[AVR-GCC-Tutorial/LCD-Ansteuerung]]&lt;br /&gt;
* [[Erweiterte LCD-Ansteuerung]]&lt;br /&gt;
* [[Pseudo-Graphische LCD-Ansteuerung]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/316826#3431235 Forumsbeitrag]: Ermittlung der Startadresse der einzelnen Zeilen&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/387596?goto=4438017#4438017 Forumsbeitrag]: HD44780 Extender, transparente Serialisierung der Ansteuerung im 4 oder 8 Bit Modus&lt;br /&gt;
* [https://omerk.github.io/lcdchargen/ Custom Character Generator for HD44780 LCD Modules]&lt;br /&gt;
* [http://blog.riyas.org/2013/12/online-led-matrix-font-generator-with.html Online led matrix font generator with binary and hex codes for Arduino ]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.sprut.de/electronic/lcd/ Ausführliche Beschreibung des HD44780]&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-software.html#libs Lib zur HD44780 Ansteuerung (AVR)]&lt;br /&gt;
* [http://pic-projekte.de/wordpress/?p=908 Ansteuern des HD44780 mit den XC8/C18-Libraries - PIC]&lt;br /&gt;
* [http://www.fonts2u.com/hd44780-regular.font TTF Matrix Pixel Font]&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;br /&gt;
[[Category:LCD]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Erweiterte_LCD-Ansteuerung&amp;diff=101255</id>
		<title>Erweiterte LCD-Ansteuerung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Erweiterte_LCD-Ansteuerung&amp;diff=101255"/>
		<updated>2019-10-30T21:09:15Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Die folgende Software beruht zum größten Teil auf dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|Tutorial zur LCD-Ansteuerung]]. Eine Anpassung war jedoch nötig, weil in der Tutorial-Vorlage die vier LCD-Datenleitungen DB4 bis DB7 immer mit den unteren vier Portleitungen PIN0 bis PIN3 des Ausgabeports verbunden sein müssen. Die hier vorgestellte Lösung ermöglicht es, die als Datenleitung verwendeten Pins innerhalb des Ausgabeports freier zu konfigurieren. D.h.&lt;br /&gt;
&lt;br /&gt;
* Die vier Datenleitungen können als Bus beliebig an einem Port gelegt werden&lt;br /&gt;
* Allerdings muss die Reihenfolge immer noch eingehalten werden, sprich, DB4-DB7 in aufsteigender Reihenfolge!&lt;br /&gt;
* Die beiden anderen Steuersignale &#039;&#039;E&#039;&#039; und &#039;&#039;RS&#039;&#039; sind auf den übrigen vier Pins eines Ports frei wählbar&lt;br /&gt;
* Das Signal &#039;&#039;RW&#039;&#039; muss am LCD fest auf GND gelegt sein, es werden nur Schreibzugriffe ausgeführt&lt;br /&gt;
* Das Busy-Flag wird &#039;&#039;&#039;nicht&#039;&#039;&#039; genutzt&lt;br /&gt;
* Wer die ultimativ freie Zuordnung von Pins über Portgrenzen hinweg haben will, muss die bekannte Bibliothek von Peter Fleury nutzen, siehe Links am Ende des Artikels&lt;br /&gt;
&lt;br /&gt;
Ansonsten sind einige [[Strukturierte Programmierung auf Mikrocontrollern|kosmetische Änderungen]] gemacht worden, die den Code etwas leichter lesbar machen sollen. So sind sämtliche Kommandos und Kommandooptionen als #defines ausgeführt. Gegenüber dem Original sind folgende Änderungen bzw. Erweiterungen gemacht worden:&lt;br /&gt;
&lt;br /&gt;
* konfigurierbare Datenleitungen, z.B. zum Anschluss von DB4-DB7 an PD4-PD7&lt;br /&gt;
* Funktionsname lcd_setcursor() statt set_cursor() (einheitliche Namenskonvention)&lt;br /&gt;
* einheitliche Verwendung von uint8_t&lt;br /&gt;
* neue Funktion lcd_out() für eine 4bit-Operation&lt;br /&gt;
* alle Kommandos und Kommandooptionen des LCD als #defines ausgeführt&lt;br /&gt;
* neue Funktion lcd_generatechar() und lcd_generatechar_P() zum [[Pseudo-Graphische_LCD-Ansteuerung|Generieren eines Zeichens im Character Generator RAM]]&lt;br /&gt;
* ein Demoprogramm, in dem alle Funktionen der Bibliotek in ihrer Nutzung dargestellt sind&lt;br /&gt;
&lt;br /&gt;
Alle Dateien sind als [[media:LCD_Tutorial.zip | ZIP-Archiv]] hier verfügbar. Das Programm wurde komplett auf einem ATmega8515 getestet. Es ist auf ein 4x16 LCD ausgelegt. Wenn man das Programm auf seiner eigenen Hardware testen will, müssen folgende Dinge angepasst werden&lt;br /&gt;
&lt;br /&gt;
* in main.h das #define F_CPU für die Taktfrequenz&lt;br /&gt;
* in lcd.h die Port- und Pinzuordnung mit LCD_PORT, LCD_DDR etc.&lt;br /&gt;
&lt;br /&gt;
Der Quelltext ist ausgiebig kommentiert und sollte die Funktionen gut erklären.&lt;br /&gt;
&lt;br /&gt;
== Die erweiterten LCD-Routinen ==&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung&lt;br /&gt;
//&lt;br /&gt;
 &lt;br /&gt;
#ifndef LCD_ROUTINES_H&lt;br /&gt;
#define LCD_ROUTINES_H&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
// alle Pins müssen in einem Port liegen&lt;br /&gt;
// die 4 Bit für die Daten müssen zusammenliegen, können aber an einer&lt;br /&gt;
// beliebigen Postion anfangen  &lt;br /&gt;
 &lt;br /&gt;
#ifndef LCD_PORTS&lt;br /&gt;
#define LCD_PORTS&lt;br /&gt;
&lt;br /&gt;
#define LCD_PORT      PORTC&lt;br /&gt;
#define LCD_DDR       DDRC&lt;br /&gt;
 &lt;br /&gt;
// 4 Bit LCD Datenbus DB4-DB7, das unterste Bit DB4 kann auf den Portbits 0..4 liegen&lt;br /&gt;
 &lt;br /&gt;
//  LCD DB4-DB7 &amp;lt;--&amp;gt;  PORTC Bit PC0-PC3&lt;br /&gt;
#define LCD_DB        PC0&lt;br /&gt;
 &lt;br /&gt;
// LCD Steuersignale RS und EN&lt;br /&gt;
 &lt;br /&gt;
//  LCD RS      &amp;lt;--&amp;gt;  PORTC Bit PC4     (RS: 0=Data, 1=Command)&lt;br /&gt;
#define LCD_RS        PC4&lt;br /&gt;
 &lt;br /&gt;
//  LCD EN      &amp;lt;--&amp;gt;  PORTC Bit PC5     (EN: 1-Impuls für Daten)&lt;br /&gt;
#define LCD_EN        PC5&lt;br /&gt;
&lt;br /&gt;
#endif // LCD_PORTS&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)&lt;br /&gt;
 &lt;br /&gt;
#ifndef LCD_TIMINGS&lt;br /&gt;
#define LCD_TIMINGS&lt;br /&gt;
&lt;br /&gt;
#define LCD_BOOTUP_MS           15&lt;br /&gt;
#define LCD_ENABLE_US           1&lt;br /&gt;
#define LCD_WRITEDATA_US        46&lt;br /&gt;
#define LCD_COMMAND_US          42&lt;br /&gt;
 &lt;br /&gt;
#define LCD_SOFT_RESET_MS1      5&lt;br /&gt;
#define LCD_SOFT_RESET_MS2      1&lt;br /&gt;
#define LCD_SOFT_RESET_MS3      1&lt;br /&gt;
#define LCD_SET_4BITMODE_MS     5&lt;br /&gt;
 &lt;br /&gt;
#define LCD_CLEAR_DISPLAY_MS    2&lt;br /&gt;
#define LCD_CURSOR_HOME_MS      2&lt;br /&gt;
&lt;br /&gt;
#endif // LCD_TIMINGS&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Zeilendefinitionen des verwendeten LCD&lt;br /&gt;
// die Einträge hier sollten für ein LCD mit einer Zeilenlänge von 16 Zeichen passen&lt;br /&gt;
// bei anderen Zeilenlängen müssen diese Einträge angepasst werden&lt;br /&gt;
 &lt;br /&gt;
#define LCD_DDADR_LINE1         0x00&lt;br /&gt;
#define LCD_DDADR_LINE2         0x40&lt;br /&gt;
#define LCD_DDADR_LINE3         0x10&lt;br /&gt;
#define LCD_DDADR_LINE4         0x50&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
void lcd_init( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD löschen&lt;br /&gt;
void lcd_clear( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Cursor in die erste Zeile, erste Spalte (Position 0,0)&lt;br /&gt;
void lcd_home( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Cursor an eine beliebige Position &lt;br /&gt;
void lcd_setcursor( uint8_t x, uint8_t y );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition &lt;br /&gt;
void lcd_data( uint8_t data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Strings an der aktuellen Cursorposition &lt;br /&gt;
// String liegt im RAM&lt;br /&gt;
void lcd_string( const char *data );&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Strings an einer bestimmten Cursorposition &lt;br /&gt;
// String liegt im RAM&lt;br /&gt;
void lcd_string_xy( uint8_t x, uint8_t y, const char *data );&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe einer Zahl an der aktuellen Cursorposition &lt;br /&gt;
// Zahl liegt im RAM&lt;br /&gt;
void lcd_number( uint8_t number, uint8_t len, uint8_t fill );&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe einer Zahl an einer bestimmten Cursorposition &lt;br /&gt;
// Zahl liegt im RAM&lt;br /&gt;
void lcd_number_xy( uint8_t x, uint8_t y, uint8_t number, uint8_t len, uint8_t fill );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Strings an der aktuellen Cursorposition&lt;br /&gt;
// String liegt im Flash &lt;br /&gt;
void lcd_string_P( PGM_P data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Definition eines benutzerdefinierten Sonderzeichens.&lt;br /&gt;
// data muss auf ein Array mit den Zeilencodes des zu definierenden Zeichens&lt;br /&gt;
// zeigen, Daten liegen im RAM&lt;br /&gt;
void lcd_generatechar( uint8_t code, const uint8_t *data, uint8_t lines );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Definition eines benutzerdefinierten Sonderzeichens.&lt;br /&gt;
// data muss auf ein Array mit den Zeilencodes des zu definierenden Zeichens&lt;br /&gt;
// zeigen, Daten liegen im FLASH&lt;br /&gt;
void lcd_generatechar_P( uint8_t code, PGM_P data, uint8_t lines );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Kommandos an das LCD.&lt;br /&gt;
void lcd_command( uint8_t data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD Befehle und Argumente.&lt;br /&gt;
// zur Verwendung in lcd_command&lt;br /&gt;
 &lt;br /&gt;
// Clear Display -------------- 0b00000001&lt;br /&gt;
#define LCD_CLEAR_DISPLAY       0x01&lt;br /&gt;
 &lt;br /&gt;
// Cursor Home ---------------- 0b0000001x&lt;br /&gt;
#define LCD_CURSOR_HOME         0x02&lt;br /&gt;
 &lt;br /&gt;
// Set Entry Mode ------------- 0b000001xx&lt;br /&gt;
#define LCD_SET_ENTRY           0x04&lt;br /&gt;
 &lt;br /&gt;
#define LCD_ENTRY_DECREASE      0x00&lt;br /&gt;
#define LCD_ENTRY_INCREASE      0x02&lt;br /&gt;
#define LCD_ENTRY_NOSHIFT       0x00&lt;br /&gt;
#define LCD_ENTRY_SHIFT         0x01&lt;br /&gt;
 &lt;br /&gt;
// Set Display ---------------- 0b00001xxx&lt;br /&gt;
#define LCD_SET_DISPLAY         0x08&lt;br /&gt;
 &lt;br /&gt;
#define LCD_DISPLAY_OFF         0x00&lt;br /&gt;
#define LCD_DISPLAY_ON          0x04&lt;br /&gt;
#define LCD_CURSOR_OFF          0x00&lt;br /&gt;
#define LCD_CURSOR_ON           0x02&lt;br /&gt;
#define LCD_BLINKING_OFF        0x00&lt;br /&gt;
#define LCD_BLINKING_ON         0x01&lt;br /&gt;
 &lt;br /&gt;
// Set Shift ------------------ 0b0001xxxx&lt;br /&gt;
#define LCD_SET_SHIFT           0x10&lt;br /&gt;
 &lt;br /&gt;
#define LCD_CURSOR_MOVE         0x00&lt;br /&gt;
#define LCD_DISPLAY_SHIFT       0x08&lt;br /&gt;
#define LCD_SHIFT_LEFT          0x00&lt;br /&gt;
#define LCD_SHIFT_RIGHT         0x01&lt;br /&gt;
 &lt;br /&gt;
// Set Function --------------- 0b001xxxxx&lt;br /&gt;
#define LCD_SET_FUNCTION        0x20&lt;br /&gt;
 &lt;br /&gt;
#define LCD_FUNCTION_4BIT       0x00&lt;br /&gt;
#define LCD_FUNCTION_8BIT       0x10&lt;br /&gt;
#define LCD_FUNCTION_1LINE      0x00&lt;br /&gt;
#define LCD_FUNCTION_2LINE      0x08&lt;br /&gt;
#define LCD_FUNCTION_5X7        0x00&lt;br /&gt;
#define LCD_FUNCTION_5X10       0x04&lt;br /&gt;
 &lt;br /&gt;
#define LCD_SOFT_RESET          0x30&lt;br /&gt;
 &lt;br /&gt;
// Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)&lt;br /&gt;
#define LCD_SET_CGADR           0x40&lt;br /&gt;
 &lt;br /&gt;
#define LCD_GC_CHAR0            0&lt;br /&gt;
#define LCD_GC_CHAR1            1&lt;br /&gt;
#define LCD_GC_CHAR2            2&lt;br /&gt;
#define LCD_GC_CHAR3            3&lt;br /&gt;
#define LCD_GC_CHAR4            4&lt;br /&gt;
#define LCD_GC_CHAR5            5&lt;br /&gt;
#define LCD_GC_CHAR6            6&lt;br /&gt;
#define LCD_GC_CHAR7            7&lt;br /&gt;
 &lt;br /&gt;
// Set DD RAM Address --------- 0b1xxxxxxx  (Display Data RAM)&lt;br /&gt;
#define LCD_SET_DDADR           0x80&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#endif // LCD_ROUTINES_H&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;lcd.c&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/HD44780&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd.h einstellbar&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;main.h&amp;quot;&lt;br /&gt;
#include &amp;quot;lcd.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
/////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Erzeugt einen Enable-Puls&lt;br /&gt;
 &lt;br /&gt;
static void lcd_enable( void ) {&lt;br /&gt;
    LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);            // Enable auf 1 setzen&lt;br /&gt;
    _delay_us( LCD_ENABLE_US );         // kurze Pause&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);           // Enable auf 0 setzen&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet eine 4-bit Ausgabeoperation an das LCD&lt;br /&gt;
static void lcd_out( uint8_t data ) {&lt;br /&gt;
    data &amp;amp;= 0xF0;                       // obere 4 Bit maskieren&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(0xF0&amp;gt;&amp;gt;(4-LCD_DB));    // Maske löschen&lt;br /&gt;
    LCD_PORT |= (data&amp;gt;&amp;gt;(4-LCD_DB));     // Bits setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
void lcd_init( void ) {&lt;br /&gt;
    // verwendete Pins auf Ausgang schalten&lt;br /&gt;
    uint8_t pins = (0x0F &amp;lt;&amp;lt; LCD_DB) |   // 4 Datenleitungen&lt;br /&gt;
                   (1&amp;lt;&amp;lt;LCD_RS) |        // R/S Leitung&lt;br /&gt;
                   (1&amp;lt;&amp;lt;LCD_EN);         // Enable Leitung&lt;br /&gt;
    LCD_DDR |= pins;&lt;br /&gt;
 &lt;br /&gt;
    // initial alle Ausgänge auf Null&lt;br /&gt;
    LCD_PORT &amp;amp;= ~pins;&lt;br /&gt;
 &lt;br /&gt;
    // warten auf die Bereitschaft des LCD&lt;br /&gt;
    _delay_ms( LCD_BOOTUP_MS );&lt;br /&gt;
    &lt;br /&gt;
    // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
    lcd_out( LCD_SOFT_RESET );&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS1 );&lt;br /&gt;
 &lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS2 );&lt;br /&gt;
 &lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS3 );&lt;br /&gt;
 &lt;br /&gt;
    // 4-bit Modus aktivieren &lt;br /&gt;
    lcd_out( LCD_SET_FUNCTION |&lt;br /&gt;
             LCD_FUNCTION_4BIT );&lt;br /&gt;
    _delay_ms( LCD_SET_4BITMODE_MS );&lt;br /&gt;
 &lt;br /&gt;
    // 4-bit Modus / 2 Zeilen / 5x7&lt;br /&gt;
    lcd_command( LCD_SET_FUNCTION |&lt;br /&gt;
                 LCD_FUNCTION_4BIT |&lt;br /&gt;
                 LCD_FUNCTION_2LINE |&lt;br /&gt;
                 LCD_FUNCTION_5X7 );&lt;br /&gt;
 &lt;br /&gt;
    // Display ein / Cursor aus / Blinken aus&lt;br /&gt;
    lcd_command(LCD_SET_DISPLAY |&lt;br /&gt;
                LCD_DISPLAY_ON |&lt;br /&gt;
                LCD_CURSOR_OFF |&lt;br /&gt;
                LCD_BLINKING_OFF); &lt;br /&gt;
 &lt;br /&gt;
    // Cursor inkrement / kein Scrollen&lt;br /&gt;
    lcd_command( LCD_SET_ENTRY |&lt;br /&gt;
                 LCD_ENTRY_INCREASE |&lt;br /&gt;
                 LCD_ENTRY_NOSHIFT );&lt;br /&gt;
 &lt;br /&gt;
    lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
  &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet ein Datenbyte an das LCD&lt;br /&gt;
void lcd_data( uint8_t data ) {&lt;br /&gt;
    LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);    // RS auf 1 setzen&lt;br /&gt;
 &lt;br /&gt;
    lcd_out( data );            // zuerst die oberen, &lt;br /&gt;
    lcd_out( data&amp;lt;&amp;lt;4 );         // dann die unteren 4 Bit senden&lt;br /&gt;
 &lt;br /&gt;
    _delay_us( LCD_WRITEDATA_US );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet einen Befehl an das LCD&lt;br /&gt;
void lcd_command( uint8_t data ) {&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);    // RS auf 0 setzen&lt;br /&gt;
 &lt;br /&gt;
    lcd_out( data );             // zuerst die oberen, &lt;br /&gt;
    lcd_out( data&amp;lt;&amp;lt;4);           // dann die unteren 4 Bit senden&lt;br /&gt;
 &lt;br /&gt;
    _delay_us(LCD_COMMAND_US );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
void lcd_clear( void ) {&lt;br /&gt;
    lcd_command( LCD_CLEAR_DISPLAY );&lt;br /&gt;
    _delay_ms( LCD_CLEAR_DISPLAY_MS );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
void lcd_home( void ) {&lt;br /&gt;
    lcd_command( LCD_CURSOR_HOME );&lt;br /&gt;
    _delay_ms( LCD_CURSOR_HOME_MS );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Setzt den Cursor in Zeile y (0..3) Spalte x (0..15)&lt;br /&gt;
 &lt;br /&gt;
void lcd_setcursor( uint8_t x, uint8_t y ) {&lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    switch (y) {&lt;br /&gt;
        case 0:    // 1. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;&lt;br /&gt;
            break;&lt;br /&gt;
        case 1:    // 2. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:    // 3. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:    // 4. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;&lt;br /&gt;
            break;&lt;br /&gt;
        default:    &lt;br /&gt;
            return; // für den Fall einer falschen Zeile&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    lcd_command( data );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
 &lt;br /&gt;
void lcd_string( const char *data ) {&lt;br /&gt;
    while( *data != &#039;\0&#039; )&lt;br /&gt;
        lcd_data( *data++ );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void lcd_string_xy( uint8_t x, uint8_t y, const char *data ) {&lt;br /&gt;
    lcd_setcursor( x, y );&lt;br /&gt;
    lcd_string( data );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt eine Zahl auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_number( uint8_t number, uint8_t len, uint8_t fill ) {&lt;br /&gt;
    uint8_t digit1 = 0;&lt;br /&gt;
    uint8_t digit2 = 0;&lt;br /&gt;
    while (number &amp;gt;= 100) {&lt;br /&gt;
        digit1++;&lt;br /&gt;
        number -= 100;&lt;br /&gt;
    }&lt;br /&gt;
    while (number &amp;gt;= 10) {&lt;br /&gt;
        digit2++;&lt;br /&gt;
        number -= 10;&lt;br /&gt;
    }&lt;br /&gt;
    if (len &amp;gt; 2) lcd_data( (digit1 != 0) ? digit1+&#039;0&#039; : fill );&lt;br /&gt;
    if (len &amp;gt; 1) lcd_data( ((digit1 != 0) || (digit2 != 0)) ? digit2+&#039;0&#039; : fill );&lt;br /&gt;
    lcd_data( number+&#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void lcd_number_xy( uint8_t x, uint8_t y, uint8_t number, uint8_t len, uint8_t fill ) {&lt;br /&gt;
    lcd_setcursor( x, y );&lt;br /&gt;
    lcd_number( number, len, fill );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
// String liegt direkt im Flash Speicher&lt;br /&gt;
 &lt;br /&gt;
void lcd_string_P( PGM_P data ) {&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
 &lt;br /&gt;
    tmp = pgm_read_byte( data );&lt;br /&gt;
    while( tmp != &#039;\0&#039; ) {        &lt;br /&gt;
        lcd_data( tmp );&lt;br /&gt;
        data++;&lt;br /&gt;
        tmp = pgm_read_byte( data );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt ein Zeichen in den Character Generator RAM&lt;br /&gt;
// Daten liegen direkt im RAM&lt;br /&gt;
 &lt;br /&gt;
void lcd_generatechar( uint8_t code, const uint8_t *data, uint8_t lines ) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startposition des Zeichens einstellen&lt;br /&gt;
    lcd_command( LCD_SET_CGADR | (code&amp;lt;&amp;lt;3) );&lt;br /&gt;
    // Bitmuster übertragen&lt;br /&gt;
    for ( i=0; i&amp;lt;lines; i++ ) {&lt;br /&gt;
        lcd_data( *data++ );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt ein Zeichen in den Character Generator RAM&lt;br /&gt;
// Daten liegen direkt im Flash-Speicher&lt;br /&gt;
 &lt;br /&gt;
void lcd_generatechar_P( uint8_t code, PGM_P data, uint8_t lines ) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startposition des Zeichens einstellen&lt;br /&gt;
    lcd_command( LCD_SET_CGADR | (code&amp;lt;&amp;lt;3) );&lt;br /&gt;
    // Bitmuster übertragen&lt;br /&gt;
    for ( i=0; i&amp;lt;lines; i++ ) {&lt;br /&gt;
        lcd_data( pgm_read_byte(data) );&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.peterfleury.epizy.com/avr-software.html#libs Sehr flexible Bibliothek zur HD44780 Ansteuerung (AVR)] (P. Fleury)&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/316826#3431235 Ermittlung der Startadresse der einzelnen Zeilen]&lt;br /&gt;
* [http://pic-projekte.de/wordpress/?p=908 Lib zur Ansteuerung des HD44780 (PIC)]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:LCD]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Bootloader_in_C_-_eine_einfache_Anleitung&amp;diff=101254</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=101254"/>
		<updated>2019-10-30T21:03:26Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Schritt 2 - Einbinden der UART Library von Peter Fleury */&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://homepage.hispeed.ch/peterfleury/avr-software.html 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ändnis 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 besonderen 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 entspricht 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://www.peterfleury.epizy.com/avr-software.html UART-Library]  von [http://www.peterfleury.epizy.com 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 wie 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; kennzeichnet 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>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/LCD-Ansteuerung&amp;diff=101253</id>
		<title>AVR-GCC-Tutorial/LCD-Ansteuerung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/LCD-Ansteuerung&amp;diff=101253"/>
		<updated>2019-10-30T20:59:11Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Das LCD und sein Controller==&lt;br /&gt;
&lt;br /&gt;
Die meisten Text-LCDs verwenden den Controller [[HD44780]] oder einen kompatiblen (z.&amp;amp;nbsp;B. KS0070) und haben 14 oder 16 Pins. Hier ist die häufigste Anschluss-Belegung angegeben. &lt;br /&gt;
&lt;br /&gt;
;Achtung: Es gibt Displays mit abweichender Anschluss-Belegung, z.B. TC1602E (Pollin 120420). Falscher Anschluss kann zur Zerstörung führen! Daher immer das zugehörige Datenblatt zu Rate ziehen. Einzelheiten unter Artikel zum Controller [[HD44780]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Pin # || Bezeichnung || Funktion&lt;br /&gt;
|-&lt;br /&gt;
! 1&lt;br /&gt;
| Vss  || GND (beim TC1602E Vdd=Vcc)&lt;br /&gt;
|-&lt;br /&gt;
! 2&lt;br /&gt;
| Vdd/Vcc  || 5V  (beim TC1602E Vss=Gnd)&lt;br /&gt;
|-&lt;br /&gt;
! 3&lt;br /&gt;
| Vee  || Kontrastspannung (0V bis 5V)&lt;br /&gt;
|-&lt;br /&gt;
! 4&lt;br /&gt;
| RS   || Register Select (Befehle/Daten)&lt;br /&gt;
|-&lt;br /&gt;
! 5&lt;br /&gt;
| RW   || Read/Write&lt;br /&gt;
|-&lt;br /&gt;
! 6&lt;br /&gt;
| E&lt;br /&gt;
| Enable&lt;br /&gt;
|-&lt;br /&gt;
! 7&lt;br /&gt;
| DB0  ||rowspan=&amp;quot;8&amp;quot;| Datenbits 0&amp;amp;minus;7&lt;br /&gt;
|-&lt;br /&gt;
! 8&lt;br /&gt;
| DB1&lt;br /&gt;
|-&lt;br /&gt;
! 9&lt;br /&gt;
| DB2&lt;br /&gt;
|-&lt;br /&gt;
! 10&lt;br /&gt;
| DB3&lt;br /&gt;
|-&lt;br /&gt;
! 11&lt;br /&gt;
| DB4&lt;br /&gt;
|-&lt;br /&gt;
! 12&lt;br /&gt;
| DB5&lt;br /&gt;
|-&lt;br /&gt;
! 13&lt;br /&gt;
| DB6&lt;br /&gt;
|-&lt;br /&gt;
! 14&lt;br /&gt;
| DB7&lt;br /&gt;
|-&lt;br /&gt;
! 15&lt;br /&gt;
| A   || LED-Beleuchtung, Anode&lt;br /&gt;
|-&lt;br /&gt;
! 16&lt;br /&gt;
| K   || LED-Beleuchtung, Kathode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Warnung|&lt;br /&gt;
;Achtung: Unbedingt von der richtigen Seite zu zählen anfangen! Meistens ist neben Pin 1 eine kleine 1 auf der LCD-Platine. Ansonsten im Datenblatt nachschauen! Oft ist Pin 1 auch durch ein rechteckiges statt rundes Pad gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
:Bei LCDs mit 16-poligem Anschluss sind die beiden letzten Pins für die Hintergrundbeleuchtung reserviert. Hier unbedingt das Datenblatt zu Rate ziehen, die beiden Anschlüsse sind je nach Hersteller oft anders beschaltet. Falls kein Datenblatt vorliegt, kann man mit einem Durchgangsprüfer feststellen, welcher Anschluss mit Masse (GND) verbunden ist.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Vss wird ganz einfach an GND angeschlossen und Vcc an 5V. Vee kann man testweise auch an GND legen. Wenn das LCD dann zu dunkel sein sollte muss man ein 10k-Potentiometer zwischen GND und 5V schalten, mit dem Schleifer an Vee: &lt;br /&gt;
&lt;br /&gt;
[[Bild:LCD_Vee.gif]]&lt;br /&gt;
&lt;br /&gt;
Es gibt zwei verschiedene Möglichkeiten zur Ansteuerung eines solchen Displays: den &#039;&#039;&#039;8-bit-&#039;&#039;&#039; und den &#039;&#039;&#039;4-bit-&#039;&#039;&#039;Modus.&lt;br /&gt;
* Für den &#039;&#039;&#039;8-bit-Modus&#039;&#039;&#039; werden (wie der Name schon sagt) alle acht Datenleitungen zur Ansteuerung verwendet, somit kann durch einen Zugriff immer ein ganzes Byte übertragen werden.&lt;br /&gt;
* Der &#039;&#039;&#039;4-bit-Modus&#039;&#039;&#039; verwendet nur die oberen vier Datenleitungen (&#039;&#039;&#039;DB4-DB7&#039;&#039;&#039;). Um ein Byte zu übertragen braucht man somit zwei Zugriffe, wobei zuerst das höherwertige &#039;&#039;&#039;&amp;quot;Nibble&amp;quot;&#039;&#039;&#039; (= 4 Bits), also Bit 4 bis Bit 7 übertragen wird und dann das niederwertige, also Bit 0 bis Bit 3. Die unteren Datenleitungen des LCDs, die beim Lesezyklus Ausgänge sind, lässt man offen (siehe Datasheets, z.&amp;amp;nbsp;B. vom KS0070).&lt;br /&gt;
&lt;br /&gt;
Der 4-bit-Modus hat den Vorteil, dass man 4 IO-Pins weniger benötigt als beim 8-bit-Modus, weshalb ich mich hier für eine Ansteuerung mit 4bit entschieden habe. &lt;br /&gt;
&lt;br /&gt;
Neben den vier Datenleitungen (DB4, DB5, DB6 und DB7) werden noch die Anschlüsse &#039;&#039;&#039;RS&#039;&#039;&#039;, &#039;&#039;&#039;RW&#039;&#039;&#039; und &#039;&#039;&#039;E&#039;&#039;&#039; (ist in manchen Unterlagen auch &#039;&#039;&#039;EN&#039;&#039;&#039;  für &#039;&#039;Enable&#039;&#039; abgekürzt) benötigt. &lt;br /&gt;
&lt;br /&gt;
* Über &#039;&#039;&#039;RS&#039;&#039;&#039; wird ausgewählt, ob man einen Befehl oder ein Datenbyte an das LCD schicken möchte. Ist RS Low, dann wird das ankommende Byte als Befehl interpretiert, ist RS high, dann wird das Byte auf dem LCD angezeigt. &lt;br /&gt;
* &#039;&#039;&#039;RW&#039;&#039;&#039; legt fest, ob geschrieben oder gelesen werden soll. High bedeutet lesen, low bedeutet schreiben. Wenn man RW auf lesen einstellt und RS auf Befehl, dann kann man das &#039;&#039;&#039;Busy-Flag&#039;&#039;&#039; an DB7 lesen, das anzeigt, ob das LCD den vorhergehenden Befehl fertig verarbeitetet hat (diese Methode u.a. in der LCD-Library von Peter Fleury verwendet). Ist RS auf Daten eingestellt, dann kann man z.&amp;amp;nbsp;B. den Inhalt des Displays lesen - was jedoch nur in den wenigsten Fällen Sinn macht. Deshalb kann man RW dauerhaft auf low lassen (= an GND anschließen), so dass man noch ein IO-Pin am Controller einspart. Der Nachteil ist, dass man dann das Busy-Flag nicht lesen kann, weswegen man nach jedem Befehl vorsichtshalber ein paar Mikrosekunden warten sollte, um dem LCD Zeit zum Ausführen des Befehls zu geben. Dummerweise schwankt die Ausführungszeit von Display zu Display und ist auch von der Betriebsspannung abhängig. Für professionellere Sachen also lieber den IO-Pin opfern und Busy abfragen.&lt;br /&gt;
* Der &#039;&#039;&#039;E&#039;&#039;&#039; Anschluss schließlich signalisiert dem LCD, dass die übrigen Datenleitungen jetzt korrekte Pegel angenommen haben und es die gewünschten Daten von den Datenleitungen bzw. Kommandos von den Datenleitungen übernehmen kann.&lt;br /&gt;
&lt;br /&gt;
== Anschluss an den Controller ==&lt;br /&gt;
&lt;br /&gt;
Jetzt da wir wissen, welche Anschlüsse das LCDs benötigt, können wir das LCD mit dem Mikrocontroller verbinden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;ACHTUNG: Es gibt Displays mit abweichender Anschluss-Belegung, falscher Anschluss kann zur Zerstörung führen! Daher immer das zugehörige Datenblatt zu Rate ziehen.&amp;lt;/span&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Einzelheiten unter [http://www.mikrocontroller.net/articles/HD44780 Artikel zum Controller HD44780]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Pin #-LCD  || Bezeichnung-LCD || Pin-µC&lt;br /&gt;
|-&lt;br /&gt;
!1&lt;br /&gt;
| Vss || GND&lt;br /&gt;
|-&lt;br /&gt;
!2&lt;br /&gt;
| Vcc || 5V&lt;br /&gt;
|-&lt;br /&gt;
!3&lt;br /&gt;
| Vee || GND oder Poti (siehe oben)&lt;br /&gt;
|-&lt;br /&gt;
!4&lt;br /&gt;
| RS  || PD4 am AVR&lt;br /&gt;
|-&lt;br /&gt;
!5&lt;br /&gt;
| RW  || GND&lt;br /&gt;
|-&lt;br /&gt;
!6&lt;br /&gt;
| E   || PD5 am AVR&lt;br /&gt;
|-&lt;br /&gt;
!7&lt;br /&gt;
| DB0 ||rowspan=&amp;quot;4&amp;quot;| offen (unbenutzt)&lt;br /&gt;
|-&lt;br /&gt;
!8&lt;br /&gt;
| DB1&lt;br /&gt;
|-&lt;br /&gt;
!9&lt;br /&gt;
| DB2&lt;br /&gt;
|-&lt;br /&gt;
!10&lt;br /&gt;
| DB3&lt;br /&gt;
|-&lt;br /&gt;
!11&lt;br /&gt;
| DB4 || PD0 am AVR&lt;br /&gt;
|-&lt;br /&gt;
!12&lt;br /&gt;
| DB5 || PD1 am AVR&lt;br /&gt;
|-&lt;br /&gt;
!13&lt;br /&gt;
| DB6 || PD2 am AVR&lt;br /&gt;
|-&lt;br /&gt;
!14&lt;br /&gt;
| DB7 || PD3 am AVR&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn man die Steuerleitungen EN und RS auf Pins an einem anderen Port legen möchte, kann man so wie in diesem [http://www.mikrocontroller.net/topic/88543#751982 Forumsbeitrag] oder wie im Artikel [[Erweiterte LCD-Ansteuerung]] vorgehen.&lt;br /&gt;
&lt;br /&gt;
Ok, alles ist verbunden, wenn man jetzt den Strom einschaltet sollten ein oder zwei schwarze Balken auf dem Display angezeigt werden. Erscheint trotz korrektem Anschluss nichts auf dem Display, so kann das auch am Kontrast des LCDs liegen. Die Balken werden dann zwar theoretisch angezeigt, sind aber nicht sichtbar, weil die Kontrastspannung zu hoch ist. Abhilfe schafft es hier, wenn man die Spannung am Schleifer des Potis nachmisst und in Richtung 0V verstellt. Zwischen 1V und 0V treten die Balken dann meist hervor.&lt;br /&gt;
Doch wie bekommt man jetzt die Befehle und Daten in das Display?&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Die LCD Routinen ===&lt;br /&gt;
&lt;br /&gt;
Der folgende Satz von Ansteuerroutinen für ein Text-LCD ist in der Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039; zusammengefasst. Diese Datei muss man beim Einrichten zusätzlich zum eigenen Hauptprogramm in sein Projekt aufnehmen. Dies geschieht beim AVR Studio unter Source Files im Fenster AVR GCC oder bei WinAVR im Makefile (z.&amp;amp;nbsp;B. durch SRC += lcd-routines.c). &lt;br /&gt;
&lt;br /&gt;
Wichtig ist außerdem, dass die Optimierung bei der Compilierung eingeschaltet ist, sonst stimmen die Zeiten der Funktionen _delay_us() und _delay_ms() nicht und der Code wird wesentlich länger (Siehe Dokumentation der libc im WinAVR).&lt;br /&gt;
&lt;br /&gt;
Als weitere Datei ist die Includedatei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039; notwendig, die im Hauptprogramm und in &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039; eingebunden wird. Die Anpassung der Pinbelegung etc. macht man in dieser Datei.&lt;br /&gt;
&lt;br /&gt;
==== Datei &#039;&#039;&#039;lcd-routines.h&#039;&#039;&#039; ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung&lt;br /&gt;
//&lt;br /&gt;
 &lt;br /&gt;
#ifndef LCD_ROUTINES_H&lt;br /&gt;
#define LCD_ROUTINES_H&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
 &lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
// Alle LCD Pins müssen an einem Port angeschlossen sein und die 4&lt;br /&gt;
// Datenleitungen müssen auf aufeinanderfolgenden Pins liegen&lt;br /&gt;
 &lt;br /&gt;
//  LCD DB4-DB7 &amp;lt;--&amp;gt;  PORTD Bit PD0-PD3&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_DB        PD0&lt;br /&gt;
 &lt;br /&gt;
//  LCD RS      &amp;lt;--&amp;gt;  PORTD Bit PD4     (RS: 1=Data, 0=Command)&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
 &lt;br /&gt;
//  LCD EN      &amp;lt;--&amp;gt;  PORTD Bit PD5     (EN: 1-Impuls für Daten)&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)&lt;br /&gt;
 &lt;br /&gt;
#define LCD_BOOTUP_MS           15&lt;br /&gt;
#define LCD_ENABLE_US           20&lt;br /&gt;
#define LCD_WRITEDATA_US        46&lt;br /&gt;
#define LCD_COMMAND_US          42&lt;br /&gt;
 &lt;br /&gt;
#define LCD_SOFT_RESET_MS1      5&lt;br /&gt;
#define LCD_SOFT_RESET_MS2      1&lt;br /&gt;
#define LCD_SOFT_RESET_MS3      1&lt;br /&gt;
#define LCD_SET_4BITMODE_MS     5&lt;br /&gt;
 &lt;br /&gt;
#define LCD_CLEAR_DISPLAY_MS    2&lt;br /&gt;
#define LCD_CURSOR_HOME_MS      2&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Zeilendefinitionen des verwendeten LCD&lt;br /&gt;
// Die Einträge hier sollten für ein LCD mit einer Zeilenlänge von 16 Zeichen passen&lt;br /&gt;
// Bei anderen Zeilenlängen müssen diese Einträge angepasst werden&lt;br /&gt;
 &lt;br /&gt;
#define LCD_DDADR_LINE1         0x00&lt;br /&gt;
#define LCD_DDADR_LINE2         0x40&lt;br /&gt;
#define LCD_DDADR_LINE3         0x10&lt;br /&gt;
#define LCD_DDADR_LINE4         0x50&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
void lcd_init( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD löschen&lt;br /&gt;
void lcd_clear( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Cursor in die 1. Zeile, 0-te Spalte&lt;br /&gt;
void lcd_home( void );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Cursor an eine beliebige Position &lt;br /&gt;
void lcd_setcursor( uint8_t spalte, uint8_t zeile );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines einzelnen Zeichens an der aktuellen Cursorposition &lt;br /&gt;
void lcd_data( uint8_t data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Strings an der aktuellen Cursorposition &lt;br /&gt;
void lcd_string( const char *data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Definition eines benutzerdefinierten Sonderzeichens.&lt;br /&gt;
// data muss auf ein Array[8] mit den Zeilencodes des zu definierenden Zeichens&lt;br /&gt;
// zeigen. Später können diese mit lcd_data(0-7) aufgerufen werden&lt;br /&gt;
void lcd_generatechar( uint8_t startadresse, const uint8_t *data );&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Ausgabe eines Kommandos an das LCD.&lt;br /&gt;
void lcd_command( uint8_t data );&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// LCD Befehle und Argumente.&lt;br /&gt;
// Zur Verwendung in lcd_command&lt;br /&gt;
 &lt;br /&gt;
// Clear Display -------------- 0b00000001&lt;br /&gt;
#define LCD_CLEAR_DISPLAY       0x01&lt;br /&gt;
 &lt;br /&gt;
// Cursor Home ---------------- 0b0000001x&lt;br /&gt;
#define LCD_CURSOR_HOME         0x02&lt;br /&gt;
 &lt;br /&gt;
// Set Entry Mode ------------- 0b000001xx&lt;br /&gt;
#define LCD_SET_ENTRY           0x04&lt;br /&gt;
 &lt;br /&gt;
#define LCD_ENTRY_DECREASE      0x00&lt;br /&gt;
#define LCD_ENTRY_INCREASE      0x02&lt;br /&gt;
#define LCD_ENTRY_NOSHIFT       0x00&lt;br /&gt;
#define LCD_ENTRY_SHIFT         0x01&lt;br /&gt;
 &lt;br /&gt;
// Set Display ---------------- 0b00001xxx&lt;br /&gt;
#define LCD_SET_DISPLAY         0x08&lt;br /&gt;
 &lt;br /&gt;
#define LCD_DISPLAY_OFF         0x00&lt;br /&gt;
#define LCD_DISPLAY_ON          0x04&lt;br /&gt;
#define LCD_CURSOR_OFF          0x00&lt;br /&gt;
#define LCD_CURSOR_ON           0x02&lt;br /&gt;
#define LCD_BLINKING_OFF        0x00&lt;br /&gt;
#define LCD_BLINKING_ON         0x01&lt;br /&gt;
 &lt;br /&gt;
// Set Shift ------------------ 0b0001xxxx&lt;br /&gt;
#define LCD_SET_SHIFT           0x10&lt;br /&gt;
 &lt;br /&gt;
#define LCD_CURSOR_MOVE         0x00&lt;br /&gt;
#define LCD_DISPLAY_SHIFT       0x08&lt;br /&gt;
#define LCD_SHIFT_LEFT          0x00&lt;br /&gt;
#define LCD_SHIFT_RIGHT         0x04&lt;br /&gt;
 &lt;br /&gt;
// Set Function --------------- 0b001xxxxx&lt;br /&gt;
#define LCD_SET_FUNCTION        0x20&lt;br /&gt;
 &lt;br /&gt;
#define LCD_FUNCTION_4BIT       0x00&lt;br /&gt;
#define LCD_FUNCTION_8BIT       0x10&lt;br /&gt;
#define LCD_FUNCTION_1LINE      0x00&lt;br /&gt;
#define LCD_FUNCTION_2LINE      0x08&lt;br /&gt;
#define LCD_FUNCTION_5X7        0x00&lt;br /&gt;
#define LCD_FUNCTION_5X10       0x04&lt;br /&gt;
 &lt;br /&gt;
#define LCD_SOFT_RESET          0x30&lt;br /&gt;
 &lt;br /&gt;
// Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)&lt;br /&gt;
#define LCD_SET_CGADR           0x40&lt;br /&gt;
 &lt;br /&gt;
#define LCD_GC_CHAR0            0&lt;br /&gt;
#define LCD_GC_CHAR1            1&lt;br /&gt;
#define LCD_GC_CHAR2            2&lt;br /&gt;
#define LCD_GC_CHAR3            3&lt;br /&gt;
#define LCD_GC_CHAR4            4&lt;br /&gt;
#define LCD_GC_CHAR5            5&lt;br /&gt;
#define LCD_GC_CHAR6            6&lt;br /&gt;
#define LCD_GC_CHAR7            7&lt;br /&gt;
 &lt;br /&gt;
// Set DD RAM Address --------- 0b1xxxxxxx  (Display Data RAM)&lt;br /&gt;
#define LCD_SET_DDADR           0x80&lt;br /&gt;
 &lt;br /&gt;
#endif &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Datei &#039;&#039;&#039;lcd-routines.c&#039;&#039;&#039;: ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/HD44780&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung&lt;br /&gt;
//&lt;br /&gt;
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Erzeugt einen Enable-Puls&lt;br /&gt;
static void lcd_enable( void )&lt;br /&gt;
{&lt;br /&gt;
    LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);     // Enable auf 1 setzen&lt;br /&gt;
    _delay_us( LCD_ENABLE_US );  // kurze Pause&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);    // Enable auf 0 setzen&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet eine 4-bit Ausgabeoperation an das LCD&lt;br /&gt;
static void lcd_out( uint8_t data )&lt;br /&gt;
{&lt;br /&gt;
    data &amp;amp;= 0xF0;                       // obere 4 Bit maskieren&lt;br /&gt;
 &lt;br /&gt;
    LCD_PORT &amp;amp;= ~(0xF0&amp;gt;&amp;gt;(4-LCD_DB));    // Maske löschen&lt;br /&gt;
    LCD_PORT |= (data&amp;gt;&amp;gt;(4-LCD_DB));     // Bits setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
void lcd_init( void )&lt;br /&gt;
{&lt;br /&gt;
    // verwendete Pins auf Ausgang schalten&lt;br /&gt;
    uint8_t pins = (0x0F &amp;lt;&amp;lt; LCD_DB) |           // 4 Datenleitungen&lt;br /&gt;
                   (1&amp;lt;&amp;lt;LCD_RS) |                // R/S Leitung&lt;br /&gt;
                   (1&amp;lt;&amp;lt;LCD_EN);                 // Enable Leitung&lt;br /&gt;
    LCD_DDR |= pins;&lt;br /&gt;
 &lt;br /&gt;
    // initial alle Ausgänge auf Null&lt;br /&gt;
    LCD_PORT &amp;amp;= ~pins;&lt;br /&gt;
 &lt;br /&gt;
    // warten auf die Bereitschaft des LCD&lt;br /&gt;
    _delay_ms( LCD_BOOTUP_MS );&lt;br /&gt;
    &lt;br /&gt;
    // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
    lcd_out( LCD_SOFT_RESET );&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS1 );&lt;br /&gt;
 &lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS2 );&lt;br /&gt;
 &lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms( LCD_SOFT_RESET_MS3 );&lt;br /&gt;
 &lt;br /&gt;
    // 4-bit Modus aktivieren &lt;br /&gt;
    lcd_out( LCD_SET_FUNCTION |&lt;br /&gt;
             LCD_FUNCTION_4BIT );&lt;br /&gt;
    _delay_ms( LCD_SET_4BITMODE_MS );&lt;br /&gt;
 &lt;br /&gt;
    // 4-bit Modus / 2 Zeilen / 5x7&lt;br /&gt;
    lcd_command( LCD_SET_FUNCTION |&lt;br /&gt;
                 LCD_FUNCTION_4BIT |&lt;br /&gt;
                 LCD_FUNCTION_2LINE |&lt;br /&gt;
                 LCD_FUNCTION_5X7 );&lt;br /&gt;
 &lt;br /&gt;
    // Display ein / Cursor aus / Blinken aus&lt;br /&gt;
    lcd_command( LCD_SET_DISPLAY |&lt;br /&gt;
                 LCD_DISPLAY_ON |&lt;br /&gt;
                 LCD_CURSOR_OFF |&lt;br /&gt;
                 LCD_BLINKING_OFF); &lt;br /&gt;
 &lt;br /&gt;
    // Cursor inkrement / kein Scrollen&lt;br /&gt;
    lcd_command( LCD_SET_ENTRY |&lt;br /&gt;
                 LCD_ENTRY_INCREASE |&lt;br /&gt;
                 LCD_ENTRY_NOSHIFT );&lt;br /&gt;
 &lt;br /&gt;
    lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
  &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet ein Datenbyte an das LCD&lt;br /&gt;
void lcd_data( uint8_t data )&lt;br /&gt;
{&lt;br /&gt;
    LCD_PORT |= (1&amp;lt;&amp;lt;LCD_RS);    // RS auf 1 setzen&lt;br /&gt;
 &lt;br /&gt;
    lcd_out( data );            // zuerst die oberen, &lt;br /&gt;
    lcd_out( data&amp;lt;&amp;lt;4 );         // dann die unteren 4 Bit senden&lt;br /&gt;
 &lt;br /&gt;
    _delay_us( LCD_WRITEDATA_US );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet einen Befehl an das LCD&lt;br /&gt;
void lcd_command( uint8_t data )&lt;br /&gt;
{&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);    // RS auf 0 setzen&lt;br /&gt;
 &lt;br /&gt;
    lcd_out( data );             // zuerst die oberen, &lt;br /&gt;
    lcd_out( data&amp;lt;&amp;lt;4 );           // dann die unteren 4 Bit senden&lt;br /&gt;
 &lt;br /&gt;
    _delay_us( LCD_COMMAND_US );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
void lcd_clear( void )&lt;br /&gt;
{&lt;br /&gt;
    lcd_command( LCD_CLEAR_DISPLAY );&lt;br /&gt;
    _delay_ms( LCD_CLEAR_DISPLAY_MS );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
void lcd_home( void )&lt;br /&gt;
{&lt;br /&gt;
    lcd_command( LCD_CURSOR_HOME );&lt;br /&gt;
    _delay_ms( LCD_CURSOR_HOME_MS );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Setzt den Cursor in Spalte x (0..15) Zeile y (1..4) &lt;br /&gt;
 &lt;br /&gt;
void lcd_setcursor( uint8_t x, uint8_t y )&lt;br /&gt;
{&lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    switch (y)&lt;br /&gt;
    {&lt;br /&gt;
        case 1:    // 1. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE1 + x;&lt;br /&gt;
            break;&lt;br /&gt;
 &lt;br /&gt;
        case 2:    // 2. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE2 + x;&lt;br /&gt;
            break;&lt;br /&gt;
 &lt;br /&gt;
        case 3:    // 3. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE3 + x;&lt;br /&gt;
            break;&lt;br /&gt;
 &lt;br /&gt;
        case 4:    // 4. Zeile&lt;br /&gt;
            data = LCD_SET_DDADR + LCD_DDADR_LINE4 + x;&lt;br /&gt;
            break;&lt;br /&gt;
 &lt;br /&gt;
        default:&lt;br /&gt;
            return;                                   // für den Fall einer falschen Zeile&lt;br /&gt;
    }&lt;br /&gt;
 &lt;br /&gt;
    lcd_command( data );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
 &lt;br /&gt;
void lcd_string( const char *data )&lt;br /&gt;
{&lt;br /&gt;
    while( *data != &#039;\0&#039; )&lt;br /&gt;
        lcd_data( *data++ );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
// Schreibt ein Zeichen in den Character Generator RAM&lt;br /&gt;
 &lt;br /&gt;
void lcd_generatechar( uint8_t startadresse, const uint8_t *data )&lt;br /&gt;
{&lt;br /&gt;
    // Startposition des Zeichens einstellen&lt;br /&gt;
    lcd_command( LCD_SET_CGADR | (startadresse&amp;lt;&amp;lt;3) ); //Startadressen: 0;1;2;3;4;5;6;7&lt;br /&gt;
 &lt;br /&gt;
    // Bitmuster übertragen&lt;br /&gt;
    for ( uint8_t i=0; i&amp;lt;8; i++ )&lt;br /&gt;
    {&lt;br /&gt;
        lcd_data( data[i] );&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(LCD_SET_DDADR); //DRAM auf 0 setzen&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== LCD Beispiel 1 ===&lt;br /&gt;
Ein Hauptprogramm, welches die LCD Funktionen benutzt, sieht zb. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen&lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  // Initialisierung des LCD&lt;br /&gt;
  // Nach der Initialisierung müssen auf dem LCD vorhandene schwarze Balken&lt;br /&gt;
  // verschwunden sein&lt;br /&gt;
  lcd_init();&lt;br /&gt;
&lt;br /&gt;
  // Text in einzelnen Zeichen ausgeben&lt;br /&gt;
  lcd_data( &#039;T&#039; );&lt;br /&gt;
  lcd_data( &#039;e&#039; );&lt;br /&gt;
  lcd_data( &#039;s&#039; );&lt;br /&gt;
  lcd_data( &#039;t&#039; );&lt;br /&gt;
&lt;br /&gt;
  // Die Ausgabemarke in die 2te Zeile setzen&lt;br /&gt;
  lcd_setcursor( 0, 2 );&lt;br /&gt;
&lt;br /&gt;
  // erneut Text ausgeben, aber diesmal komfortabler als String&lt;br /&gt;
  lcd_string(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== LCD Beispiel 2 ===&lt;br /&gt;
Ein Hauptprogramm, welches eine Variable ausgibt, sieht zb. so aus.&lt;br /&gt;
Mittels der itoa() Funktion (itoa = &amp;lt;b&amp;gt;I&amp;lt;/b&amp;gt;nteger &amp;lt;b&amp;gt;To&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;A&amp;lt;/b&amp;gt;scii ) wird von einem Zahlenwert eine textuelle Repräsentierung ermittelt (sprich: ein String erzeugt) und dieser String mit der bereits vorhandenen Funktion lcd_string ausgegeben. Das Einrichten des Projekts ist wie in Beispiel 1.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// &lt;br /&gt;
// Anpassungen im makefile:&lt;br /&gt;
//    ATMega8 =&amp;gt; MCU=atmega8 im makefile einstellen&lt;br /&gt;
//    lcd-routines.c in SRC = ... Zeile anhängen &lt;br /&gt;
// &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
// Beispiel&lt;br /&gt;
int variable = 42;&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  lcd_init();&lt;br /&gt;
&lt;br /&gt;
  // Ausgabe des Zeichens dessen ASCII-Code gleich dem Variablenwert ist&lt;br /&gt;
  // (Im Beispiel entspricht der ASCII-Code 42 dem Zeichen *)&lt;br /&gt;
  // http://www.code-knacker.de/ascii.htm&lt;br /&gt;
  lcd_data( variable );&lt;br /&gt;
&lt;br /&gt;
  lcd_setcursor( 0, 2 );&lt;br /&gt;
 &lt;br /&gt;
  // Ausgabe der Variable als Text in dezimaler Schreibweise&lt;br /&gt;
  {&lt;br /&gt;
     // ... umwandeln siehe FAQ Artikel bei http://www.mikrocontroller.net/articles/FAQ&lt;br /&gt;
     // WinAVR hat eine itoa()-Funktion, das erfordert obiges #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
     char Buffer[20]; // in diesem {} lokal&lt;br /&gt;
     itoa( variable, Buffer, 10 ); &lt;br /&gt;
&lt;br /&gt;
     // ... ausgeben  &lt;br /&gt;
     lcd_string( Buffer );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Erweiterte LCD-Ansteuerung]]&lt;br /&gt;
* [[Pseudo-Graphische LCD-Ansteuerung]]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/316826#3431235 Ermittlung der Startadresse der einzelnen Zeilen]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/350554#3899961 LCD-Ansteuerung mit freier Wahl von Pins und Portregistern am Controller]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.peterfleury.epizy.com/avr-software.html#libs Lib zur HD44780 Ansteuerung (AVR)]&lt;br /&gt;
* [http://pic-projekte.de/wordpress/?p=908 Lib zur HD44780 Ansteuerung (PIC)]&lt;br /&gt;
* [http://www.nongnu.org/avr-libc/user-manual/group__stdiodemo.html Using the standard IO facilities] - Demoprojekt zur Text-LCD Ansteuerung (HD44780 komp.) in der avr-libc&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Avr-gcc Tutorial]]&lt;br /&gt;
[[Kategorie:LCD]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_TWI&amp;diff=101252</id>
		<title>AVR TWI</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_TWI&amp;diff=101252"/>
		<updated>2019-10-30T20:57:36Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: /* Bibliotheken */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von Alexander Starke&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Das TWI (Two Wire Interface), ursprünglich von Philips als [[I2C]] bezeichnet, ist begründet durch seinen Aufbau ideal auf typische Mikrocontroller-Anwendungen zugeschnitten. Durch seinen 7 Bit breiten Adressraum können bis zu 128 (2&amp;lt;sup&amp;gt;7&amp;lt;/sup&amp;gt; = 128) Geräte miteinander verschaltet werden. Im Gegensatz zum SPI ([[Serial Peripheral Interface]]) werden nur zwei Busleitungen benötigt. Eine dient der Bereitstellung des Taktes (SCL) und die andere dem Datentransport (SDA). Die einzige zusätzlich benötigt Hardware besteht aus zwei Pull-Up-Widerständen, welche von jeweils einer Busleitung gegen Vcc (5V) geschaltet werden. Da alle Bustreiber mit offenem Kollektor bzw. Drain am Bus anliegen, ergibt sich eine logische UND-Verknüpfung. D.h., sobald irgendein Gerät auf einer der Busleitungen Low-Pegel anlegt, liegt automatisch die gesamte Busleitung auf Low. Der Bus hat nur High-Pegel, wenn alle angeschlossenen Geräte auch High-Pegel an ihrer entsprechenden Schnittstelle haben.&lt;br /&gt;
&lt;br /&gt;
Genau wie beim SPI initiiert auch beim TWI der Master den Datentransfer und beendet selbigen auch wieder. Gestartet wird eine Übertragung durch eine Start-Bedingung und entsprechend beendet durch eine Stop-Bedingung. Während einer Übertragung gilt der Bus als &amp;quot;busy&amp;quot; (beschäftigt) und kein anderer Master darf versuchen, Kontrolle über ihn zu erlangen. Einzige Ausnahme bildet hier der Fall, dass vor einer Stop-Bedingung durch den gerade aktiven Master noch eine zusätzliche Start-Bedingung auf den Bus gegeben wird. Dieser Fall tritt normalerweise dann ein, wenn der Master noch weitere Daten zu versenden hat und nicht die Kontrolle über den Bus verlieren möchte. Nach solch einem wiederholten Start (Repeated Start) ist der Bus wieder bis zum nächsten Stop beschäftigt.&lt;br /&gt;
Start- und Stop-Bedingungen werden signalisiert, indem der Pegel der SDA-Leitung wechselt, während SCL High-Pegel hat.&lt;br /&gt;
&lt;br /&gt;
Alle Adresspakete auf dem TWI-Bus haben eine Länge von 9 Bit. Davon beinhalten 7 Bit wie bereits eingangs erwähnt die Adresse der angesprochenen Komponente. Ein weiteres Bit bestimmt, ob ein Lese- oder ein Schreibzugriff durchgeführt werden soll (R/W=1 -&amp;gt; Lesen; RW=0 -&amp;gt; Schreiben). Das 9. Bit dient der Bestätigung. Wenn ein Slave bemerkt, dass er angesprochen wird, so bestätigt er dies, indem er mit dem 9. Taktzyklus (ACK) von SCL die SDA-Leitung auf Low-Pegel legt. Sollte der angesprochene Slave aus irgendwelchen Gründen nicht antworten können, so sollte SDA auf High-Pegel bleiben. Der Master hat dann die Wahl, ob er ein Stop sendet oder einen wiederholten Start durchführt.&lt;br /&gt;
&lt;br /&gt;
* SLA +R   --&amp;gt;    Adresspaket mit einer Slave-Adresse und einem Lese-Bit&lt;br /&gt;
* SLA +W  --&amp;gt;    Adresspaket mit einer Slave-Adresse und einem Schreib-Bit&lt;br /&gt;
&lt;br /&gt;
Das höchstwertige Bit wird prinzipiell zuerst übertragen (MSB: Most Significant Bit). Die Slave-Adressen können durch den Anwender frei gewählt werden, nur 0000 000 ist reserviert, hiermit werden alle Slaves angesprochen. Wird ein solcher Zugriff durchgeführt, so sollten alle Slaves im ACK-Zyklus SDA auf Low-Pegel legen.&lt;br /&gt;
Benutzt wird diese Aktion, wenn der Master die selben Daten an mehrere Slaves senden möchte. Sinn ergibt die Verwendung nur in Verbindung mit dem Schreib-Bit. Ein Setzen des Lese-Bits hätte zur Folge, dass verschiedene Slaves ja verschiedene Daten auf den Bus stellen könnten, was zu einem Durcheinander führen würde. Wird das Schreib-Bit gesetzt, so setzen im ACK-Zyklus alle Slaves SDA auf Low-Pegel, welche den allgemeinen Adressaufruf erhalten und ausgewertet haben (also die nicht beschäftigt waren). Die folgenden Datenpakete werden dann von allen Slaves empfangen, welche SDA auf Low-Pegel gelegt hatten.&lt;br /&gt;
&lt;br /&gt;
Alle Datenpakete auf dem TWI-Bus haben analog zu den Adresspaketen 9 Bit. Hiervon sind 8 Datenbits. Das 9. dient der Bestätigung. Während einer Datenübertragung ist es Aufgabe des Masters, den Takt, das Start- und das Stopzeichen bereitzustellen. Aufgabe des Empfängers ist die Bestätigung der erhaltenen Daten, indem er die SDA-Leitung während des 9. SCL-Zyklus auf Low-Pegel legt. Dieses wird als ACK bezeichnet (Acknowledge). Bleibt SDA auf High-Pegel, so wird dem Sender signalisiert, dass die gesendeten Daten nicht empfangen wurden (NACK). Das MSB wird zuerst übertragen.&lt;br /&gt;
&lt;br /&gt;
Wenn man nun Adress- und Datenpakete kombiniert, hat man schon eine fertige Übertragung. Diese besteht aus:&lt;br /&gt;
&lt;br /&gt;
* START    --&amp;gt;    SLA R/W    --&amp;gt;    ein oder mehr Datenpakete    --&amp;gt;    STOP&lt;br /&gt;
&lt;br /&gt;
Ein START gefolgt von einem STOP ist nicht zulässig. Wenn der Takt des Masters für den Slave zu schnell ist oder er noch mehr Zeit zur Bearbeitung braucht, so kann er SCL einfach länger auf Low-Pegel halten. Die Verlängerung der Low-Phase von SCL hat allerdings keinen Einfluss auf die Länge der High-Phase, da diese vom Master zur Verfügung gestellt wird. Dadurch kann es allerdings zu einer Reduzierung des Datentransfers kommen.&lt;br /&gt;
&lt;br /&gt;
== Multi-Master-Bussysteme ==&lt;br /&gt;
&lt;br /&gt;
Das TWI-Bussystem erlaubt den Multi-Master-Betrieb. Es wurden spezielle Vorkehrungen getroffen, so dass auch wenn mehrere Master zur selben Zeit eine Transmission beginnen wollen keine Probleme auftreten und der Bus normal funktioniert. Nur die zwei folgenden Probleme sollten beachtet werden:&lt;br /&gt;
&lt;br /&gt;
# Es ist Sache des Anwenders, seine Algorithmen so auszulegen, dass in diesem Fall nur ein Master seine Transmission fortsetzt. Alle anderen Master sollten ihre Transmission sofort abbrechen. Diesen Auswahlprozess zwischen den Mastern nennt man Arbitrierung (Entscheidung). Wenn ein Master bemerkt, dass er den Entscheidungsprozess (die Arbitrierung) verloren hat, sollte er sofort in den Slave-Modus gehen, da es sein kann, dass der Master, welcher den Prozess gewonnen hat, ihn adressieren will. Die Tatsache, dass mehrere Master versucht hatten, eine Übertragung zu starten, sollte für die Slaves nicht feststellbar sein, die Daten auf dem Bus dürfen nicht beschädigt sein.&lt;br /&gt;
# Da verschiedene Master mit verschiedenen Taktfrequenzen auf dem Bus arbeiten könnten, ist eine Festlegung zu treffen, wie man diese synchronisiert. Dieses erleichtert auch den Arbitrierungsprozess.&lt;br /&gt;
&lt;br /&gt;
Die Lösung dieses Problems stellt die schon oben angesprochene Und-Verknüpfung aller Komponenten auf dem Bus dar. So werden alle seriellen Taktraten der einzelnen Master &amp;quot;und&amp;quot; verknüpft. Somit ergibt sich ein High-Pegel entsprechend dem des Masters mit dem kürzesten High-Pegel, der Low-Pegel ist durch den Master mit der geringsten Taktrate gegeben.&lt;br /&gt;
&lt;br /&gt;
Da der Multi-Master-Betrieb für meine Anwendung keinen Belang hat, werde ich hier nicht näher darauf eingehen.&lt;br /&gt;
&lt;br /&gt;
==Aufbau des TWI==&lt;br /&gt;
&lt;br /&gt;
Der SCL- und der SDA-Pin stellen die Schnittstelle des einzelnen MC mit dem Bus dar. Zusätzlich ist intern vorgesehen, dass Spannungsspitzen, welche kürzer als 50ns sind, herausgefiltert werden. Es besteht bei manchen Konfigurationen die Möglichkeit, die internen Pull-Up Widerstände zu verwenden, dies kann die Verwendung der externen überflüssig machen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich verfügt der ATmega über einen Bitraten-Generator, welcher im Masterbetrieb das entsprechende Signal auf SCL gibt. Die Taktrate wird durch das TWBR (TWI Bit Rate Register) und TWSR (TWI Status Register) festgelegt. Zu beachten ist, dass die interne Taktfrequenz der einzelnen Slaves mindestens 16 mal höher als die Taktrate des Bus ist. Diese ergibt sich nach folgender Formel:&lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/mc-project/Pages/AVR/TWI/twi_formel.gif&lt;br /&gt;
&lt;br /&gt;
* TWBR - TWI Bit Rate Register&lt;br /&gt;
* TWPS - TWI Prescaler Bits im TWI Status Register&lt;br /&gt;
&lt;br /&gt;
Des weiteren verfügt der MC über je ein Daten- und Adressschieberegister, einen START/STOP Controller und die Arbitrierungslogik. Das TWDR (TWI Data Register) beinhaltet entweder die Adresse und Daten welche gesendet werden sollen oder die empfangene Adresse samt Daten. Zusätzlich existiert noch ein Register, welches das (N)ACK-Bit enthält, das aber nicht direkt zugänglich ist. Jedoch kann es im Empfangsmodus durch verändern von TWCR (TWI Control Register) bzw. im Empfangsmodus durch verändern von TWSR manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
Der START/STOP Controller ist wie sein Name bereits sagt, dafür verantwortlich, den anderen Teilnehmern am Bus mitzuteilen, dass der entsprechende Master eine Übertragung plant bzw. beendet. Zusätzlich führt er auch den bereits erwähnten REPEATED START durch. Eine Besonderheit ist, dass er solche START/STOP-Zeichen auf dem Bus sogar detektieren kann, wenn sich der Controller in einem der Sleep-Modi befindet. Er kann somit &amp;quot;aufgeweckt&amp;quot; werden, wenn er von einem Master adressiert wird.&lt;br /&gt;
&lt;br /&gt;
Ein weiterer Bestandteil des TWI ist die &amp;quot;Adress Match Unit&amp;quot;. Diese überwacht, ob die empfangenen Adress-Bytes mit denen im TWAR (TWI Adress Register) übereinstimmen. Wenn TWGCE (TWI General Call Recognition Enable) in TWAR gesetzt ist, werden alle eingehenden Adress-Bytes zusätzlich noch daraufhin überprüft, ob ein allgemeiner Ruf (0000 000) vorliegt. Sollte eine Übereinstimmung vorliegen, wird die Kontrolleinheit informiert, eventuelle Korrekturmaßnahmen können durchgeführt werden. Die TWI-Logik kann nun die Anforderung bestätigen, je nach dem welche Einstellungen in TWCR getroffen wurden. Wie bereits oben angesprochen wurde, arbeitet die Adress-Vergleichs-Logik sogar, wenn sich der MC in einem der Sleep-Modi befindet. Man kann den MC somit per TWI reaktivieren. Einzige Ausnahme bildet der Fall, dass der Controller zuvor durch einen anderen Interrupt reaktiviert wurde. Danach geht die TWI-Logik sofort in den Leerlauf. Sollte das bei der vorgesehenen Problemstellung zu Problemen führen, bleibt nur der Ausweg sämtliche anderen Interrupts in der Hinsicht zu sperren, dass sie den Controller nicht mehr reaktivieren können.&lt;br /&gt;
&lt;br /&gt;
Die Kontrolleinheit des TWI beobachtet ständig den Bus und antwortet auf Anfragen entsprechend den Einstellungen in TWCR. Tritt ein Ereignis ein, auf welches die angesprochene Einheit reagieren soll, so wird automatisch TWINT (TWI Interrupt Flag) gesetzt. Im nächsten Taktzyklus wird dann TWSR aktualisiert, in ihm steht jetzt ein Code mit welchem das Ereignis spezifiziert werden kann. TWSR beinhaltet nur relevante Informationen, wenn TWINT gesetzt wird, andernfalls beinhaltet es bloß einen Statuscode, welcher aussagt, dass keine relevanten Informationen vorliegen. Solange TWINT gesetzt ist, wird SCL auf Low Pegel belassen. Dies gibt dem angesprochenen MC die Chance, alle Anwendungen erst zu beenden, bevor er sich der Transmission widmet. TWINT wird gesetzt wenn:&lt;br /&gt;
&lt;br /&gt;
* wenn das TWI START/REPEATED START gesendet hat&lt;br /&gt;
* wenn das TWI SLA +R/W gesendet hat&lt;br /&gt;
* wenn das TWI ein Adress-Byte gesendet hat&lt;br /&gt;
* wenn das TWI seine &amp;quot;Vormacht&amp;quot; auf dem Bus verloren hat&lt;br /&gt;
* wenn das TWI durch seine eigene Slave-Adresse bzw. einen allgemeinen Ruf (general call) adressiert wurde&lt;br /&gt;
* wenn das TWI ein Datenbyte empfangen hat&lt;br /&gt;
* wenn ein STOP oder REPEATED START empfangen wurde, während es immer noch als Slave angesprochen ist&lt;br /&gt;
* wenn ein Busfehler durch einen illegalen START bzw. STOP aufgetreten ist&lt;br /&gt;
&lt;br /&gt;
== Benutzung des TWI ==&lt;br /&gt;
&lt;br /&gt;
Das TWI ist wie bereits ausführlich dargestellt wurde, byte- bzw. interruptbasiert. Dadurch ist es der Anwendersoftware möglich, parallel zum Datenversand bzw. -empfang auf dem TWI Bus noch weitere Anwendungen (Operationen) auszuführen. Die Ausführung einer ISR ist allein durch das Setzen von TWIE (TWI Interrupt Enable) in TWCR bedingt (die Interrupts müssen natürlich auch global freigegeben sein). Es ist Sache des Anwenders, ob das Setzen von TWINT einen Interrupt verursachen soll. Ist TWIE nicht gesetzt, so ist es dem Programm selbst überlassen, ständig TWINT zu prüfen, um sämtliche Ereignisse auf dem Bus mit zu bekommen. &lt;br /&gt;
&lt;br /&gt;
Bei jedem Setzen von TWINT ist eine Operation auf dem Bus abgeschlossen worden und es wird eine Antwort erwartet. In diesem Fall beinhaltet TWSR einen Wert, welcher ein direktes Maß für den aktuellen Status des Bus ist. Die Software legt nun fest, welche Aktion mit dem nächsten Buszyklus durchgeführt werden soll, indem sie TWCR und TWDR entsprechend verändert. Im folgenden wird schrittweise ein Beispiel für den Ablauf einer Übertragung gegeben.&lt;br /&gt;
&lt;br /&gt;
Um die entsprechenden Statusmakros (&#039;&#039;TW_*&#039;&#039;) verwenden zu können wird die entsprechende Headerdatei eingebunden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;util/twi.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zuerst muss vom Master ein START-Signal auf den Bus gegeben werden. Dies erfolgt durch Schreiben eines speziellen Wertes in TWCR. Nach Abschluss der Operation wird TWINT gesetzt. Die Logik wartet nun so lange, bis durch den Anwender das Flag wieder gelöscht wurde (durch Schreiben einer &#039;1&#039;). Unmittelbar nach dem Löschen von TWINT beginnt die Logik damit das START-Signal zu senden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);                        //TWSTA = TWI START Condition Bit&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Senden des START wird sofort wieder TWINT in TWCR gesetzt. Zusätzlich beinhaltet TWSR jetzt den entsprechenden Status Code, welcher mitteilt, dass START erfolgreich gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while  (!(TWCR &amp;amp; (1&amp;lt;&amp;lt;TWINT)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Nachdem durch die Applikation TWSR daraufhin überprüft wurde, dass START erfolgreich gesendet wurde, kann es nun weitergehen. Im Fehlerfall könnte beispielsweise eine vom Anwender definierte Fehlerroutine greifen. &lt;br /&gt;
&lt;br /&gt;
Nun muss SLA_W in TWDR geladen werden. Ist dies abgeschlossen, muss wiederum ein spezieller Wert in TWCR geschrieben werden, um der TWI-Logik mitzuteilen, dass die Adresse und der Schreibbefehl aus TWDR gesendet werden können. Unmittelbar nachdem durch den Anwender dann TWINT in TWCR gelöscht wurde (Schreiben einer &#039;1&#039;) sendet das TWI das Adresspaket.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
if ((TWSR &amp;amp; 0xF8) != TW_START)&lt;br /&gt;
  ERROR();                                                                //Fehlerbehandlung&lt;br /&gt;
TWDR = SLA_W;&lt;br /&gt;
TWCR = (1&amp;lt;&amp;lt;TWINT) | (1&amp;lt;&amp;lt;TWEN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach Abschluss der Sendung wird wiederum TWINT gesetzt und in TWSR steht der entsprechende Statuscode, welcher über Erfolg bzw. Misserfolg der Sendung urteilt. In ihm ist auch das ACK-Bit des angesprochenen Slaves enthalten (oder auch nicht, was dann einen Fehler bedeuten würde).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while (!(TWCR &amp;amp; (1&amp;lt;&amp;lt;TWINT)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Nach der Prüfung von TWSR ob bei der Sendung keine Fehler aufgetreten sind, kann das Datenpaket in TWDR geladen werden. Erneut muss ein spezieller Wert in TWCR geschrieben werden, welcher der Logik sagt, dass ein Datenpaket aus TWDR versendet werden soll. Nach dem Löschen von TWINT wird dieses automatisch versendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
if ((TWSR &amp;amp; 0xF8) != TW_MT_SLA_ACK)&lt;br /&gt;
  ERROR();&lt;br /&gt;
TWDR = DATA;&lt;br /&gt;
TWCR = (1&amp;lt;&amp;lt;TWINT) | (1&amp;lt;&amp;lt;TWEN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Nachdem TWINT gesetzt wurde kann mit Hilfe von TWSR überprüft werden, ob das Datenpaket erfolgreich gesendet und vom angesprochenen Slave angenommen wurde (ACK-Bit).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
while (!(TWCR &amp;amp; (1&amp;lt;&amp;lt;TWINT)));&lt;br /&gt;
 &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachdem bis hierhin alles funktioniert hat (angenommen), muss nun nur noch ein STOP gesendet werden. Dies erfolgt analog zu den obigen Ausführungen durch Schreiben eines speziellen Wertes in TWCR. Nach dem Löschen von TWINT wird dieses gesendet. Allerdings wird danach TWINT nicht automatisch wieder gesetzt, wie es zuvor immer der Fall war.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
if ((TWSR &amp;amp; 0xF8) != TW_MT_DATA_ACK)&lt;br /&gt;
  ERROR();&lt;br /&gt;
TWCR = (1&amp;lt;&amp;lt;TWINT)|(1&amp;lt;&amp;lt;TWEN)|&lt;br /&gt;
(1&amp;lt;&amp;lt;TWSTO);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Selbstverständlich muss zuvor beim anzusprechenden Slave in TWAR (TWI Adress Register) eine Adresse festgelegt werden, damit er auch gezielt angesprochen werden kann.&lt;br /&gt;
&lt;br /&gt;
== Übertragungsmodi == &lt;br /&gt;
&lt;br /&gt;
Das TWI kann prinzipiell in vier verschiedenen Modi arbeiten: Master Transmitter (MT), Master Receiver (MR), Slave Transmitter (ST), Slave Receiver (SR). Diese können natürlich alle in einer Anwendung verwendet werden, je nachdem, wie sie es erfordert.&lt;br /&gt;
&lt;br /&gt;
=== Master Transmitter Mode ===&lt;br /&gt;
&lt;br /&gt;
Im MTM wird eine Abfolge von Bytes (Daten) an einen SR gesendet. Um ihn zu benutzen, muss das Adresspaket nach dem gesendeten START eine bestimmte Form aufweisen. Wird SLA+W gesendet, liegt der MTM vor, für SLA+R wäre es der MRM.&lt;br /&gt;
Das Senden eines START gestaltet sich wie folgt (TWCR):&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |1    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
* TWEN:    Aktivierung des TWI&lt;br /&gt;
* TWSTA:    Senden des START&lt;br /&gt;
* TWINT:    um TWINT zu löschen&lt;br /&gt;
&lt;br /&gt;
Das TWI wartet nun solange, bis der Bus frei ist und sendet dann START. Danach wird TWINT wieder gesetzt und TWSR aktualisiert. Es sollte jetzt 0x08 enthalten. Beim Auslesen des Status-Code müssen die Prescaler-Bits mit Null maskiert werden. Um den MTM zu benutzen, muss SLA+W in TWDR geschrieben werden. Nachdem TWINT gelöscht wurde, wird dies gesendet. TWCR muss dazu die folgende Form haben:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |0    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Nachdem SLA+W gesendet und das ACK-Bit empfangen wurde, wird TWINT erneut gesetzt. Der Statuscode in TWSR kann nun unter anderem folgende Formen haben: 0x18, 0x20, 0x38. &lt;br /&gt;
&lt;br /&gt;
Nachdem bis hierhin alles erfolgreich funktioniert hat, kann jetzt ein Datenpaket versendet werden. Dies erfolgt durch Schreiben der Daten in TWDR. TWDR kann nur aktualisiert werden, wenn TWINT gesetzt ist, andernfalls kommt es zu einem Fehler, TWWC (TWI Write Collision Bit) wird gesetzt. Um die Daten zu senden, muss TWINT erneut gelöscht werden. TWCR sollte so aussehen:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |1    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Dieses Schema wiederholt sich nun solange, bis alle Daten versendet wurden und ein STOP gesendet werden kann. Dies erfolgt folgendermaßen:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |0    |1    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Ein erneuter Start (REPEATED START) erfolgt durch Schreiben der folgenden Werte in TWCR:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |1    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Nach einem REPEATED START kann der Master denselben Slave erneut kontaktieren oder auch einen anderen, ohne ein STOP zu senden. Der REPEATED START erlaubt dem Master das Wechseln zwischen Slaves, MTM und MRM ohne die Kontrolle über den Bus zu verlieren.&lt;br /&gt;
&lt;br /&gt;
Eine Tabelle mit sämtlichen Status-Codes und den zugehörigen Ereignissen ist dem Datenblatt S.176 zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
=== Master Receiver Mode ===&lt;br /&gt;
&lt;br /&gt;
Im MRM empfängt der Master Daten von einem ST. Zunächst muss wieder START gesendet werden. Das folgende Adresspaket sollte dann SLA+R enthalten.&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |1    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
* TWEN:    Aktivierung des TWI&lt;br /&gt;
* TWSTA:    Senden des START&lt;br /&gt;
* TWINT:    um TWINT zu löschen&lt;br /&gt;
&lt;br /&gt;
Das TWI wartet nun solange, bis der Bus frei ist und sendet dann START. Danach wird TWINT wieder gesetzt und TWSR aktualisiert. Es sollte jetzt 0x08 enthalten. Beim Auslesen des Status-Code müssen die Prescaler-Bits mit Null maskiert werden. Um den MRM zu benutzen, muss SLA+R in TWDR geschrieben werden. Nachdem TWINT gelöscht wurde, wird dies gesendet. TWCR muss dazu die folgende Form haben:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |0    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Nachdem SLA+R gesendet und das ACK-Bit empfangen wurde, wird TWINT erneut gesetzt. Der Statuscode in TWSR kann nun unter anderem folgende Formen haben: 0x38, 0x40, 0x48. &lt;br /&gt;
Die empfangenen Daten können aus TWDR gelesen werden, so denn TWINT gesetzt ist. Dieses Schema kann sich bis zum Empfang des letzten Bytes immer wieder wiederholen. Nachdem das letzte Byte empfangen wurde, kann der Master dem Slave durch senden von NACK klar machen, dass keine Daten mehr erwartet werden. Beendet wird der Transfer durch senden von STOP oder REPEATED START.&lt;br /&gt;
Für STOP muss folgendes in TWCR geschrieben werden:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |0    |1    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Für einen REPEATED START muss folgendes in TWCR geschrieben werden:&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |1    |X   |1    |0    |X   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Nach einem REPEATED START kann der Master denselben Slave erneut kontaktieren oder auch einen anderen, ohne ein STOP zu senden. Der REPEATED START erlaubt dem Master das Wechseln zwischen Slaves, MTM und MRM ohne die Kontrolle über den Bus zu verlieren.&lt;br /&gt;
&lt;br /&gt;
Eine Tabelle mit sämtlichen Status-Codes und den zugehörigen Ereignissen ist dem Datenblatt S.179 zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
=== Slave Receiver Mode ===&lt;br /&gt;
&lt;br /&gt;
Im SRM werden Daten vom MT empfangen. Um einen Atmega8 im SRM zu betreiben, müssen TWAR und TWCR die folgende Form haben:&lt;br /&gt;
&lt;br /&gt;
TWAR:&lt;br /&gt;
Die oberen 7 Bit stellen die Adress dar, an die das TWI antwortet, wenn es von einem Master angesprochen wird. Wenn das LSB (Least Significant Bit) in Form von TWGCE gesetzt ist, antwortet der Slave auch auf allgemeine Rufe (general calls), andernfalls natürlich nicht.&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWA6|TWA5|TWA4|TWA3|TWA2|TWA1|TWA0|TWGCE&lt;br /&gt;
 |Hier|steht|dann|die|Adresse|des|Slave!|X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
TWCR:&lt;br /&gt;
TWEN muss zum aktivieren des TWI gesetzt werden. TWEA muss gesetzt werden, damit der Slave antwortet, falls er von einem Master oder gegeben falls durch einen general call angesprochen wird. TWSTA und TWSTO müssen auf Null gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |0    |1   |0    |0    |0   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Wenn TWAR und TWCR initialisiert wurden, wartet der Slave ab, bis er mit seiner Adresse bzw. gegebenen falls durch einen generall call angesprochen wird gefolgt von einem Datenrichtungs-Bit. Hierbei steht eine &#039;0&#039; für Schreiben (SR) und eine &#039;1&#039; für Lesen (ST).&lt;br /&gt;
Nachdem die Logik ihre eigene Adresse und ein Schreib-Bit erhalten hat, wird TWINT gesetzt und TWSR beinhaltet den entsprechenden Statuscode. Mit seiner Hilfe kann die Software dann eine sinnvolle Reaktion einleiten. Der Eintritt in den SRM kann auch erfolgen, wenn ein Master sein Vorrecht auf dem Bus verloren hat (0x68, 0x78).&lt;br /&gt;
&lt;br /&gt;
Wenn TWEA während eines Transfers zurückgesetzt wird, wird das TWI ein NACK (Not Acknowledge) auf den Bus nach Empfang des nächsten Bytes geben. Dies kann verwendet werden, wenn der Slave nicht mehr in der Lage ist, mehr Bytes zu empfangen. Solange TWEA &#039;0&#039; ist, reagiert das TWI nicht mehr auf seine eigene Slave-Adresse. Jedoch wird der Bus weiterhin beobachtet und das TWI ist nach dem Setzen von TWEA sofort wieder einsatzbereit. Somit kann dieses Feature verwendet werden, um einen Baustein für eine bestimmte Zeitspanne vom Bus zu nehmen.&lt;br /&gt;
&lt;br /&gt;
In sämtlichen Sleep-Modi (außer Idle Mode) ist der Systemtakt des TWI inaktiv. Dessen zu trotz kann das TWI durch setzen von TWEA allein mit Hilfe des Bustaktes erfassen, wenn es mit seiner Adresse bzw. durch einen general call angesprochen wird. Somit kann das TWI den Controller aufwecken. Während der Aufweck-Phase kann SCL auf L-Pegel gehalten werden wenn TWINT gelöscht ist (Schreiben einer &#039;1&#039;). Der Datenempfang kann wieder im Normalbetrieb stattfinden. Zu beachten ist, dass solange SCL auf L-Pegel ist keine anderen Transmissionen stattfinden können. Wichtig ist, dass TWDR nach dem Aufwecken nicht mehr das letzte Byte, welches auf dem Bus war, enthält.&lt;br /&gt;
&lt;br /&gt;
Eine Tabelle mit sämtlichen Status-Codes und den zugehörigen Ereignissen ist dem Datenblatt S.182 zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
=== Slave Transmitter Mode ===&lt;br /&gt;
&lt;br /&gt;
Im STM werden Daten an einen MR gesendet. Um den Controller im STM zu betreiben, müssen TWAR und TWCR wie folgt initialisiert werden.&lt;br /&gt;
&lt;br /&gt;
TWAR:&lt;br /&gt;
Die oberen 7 Bit stellen die Adress dar, an die das TWI antwortet, wenn es von einem Master angesprochen wird. Wenn das LSB (Least Significant Bit) in Form von TWGCE gesetzt ist, antwortet der Slave auch auf allgemeine Rufe (general calls), andernfalls natürlich nicht.&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWA6|TWA5|TWA4|TWA3|TWA2|TWA1|TWA0|TWGCE&lt;br /&gt;
 |Hier|steht|dann|die|Adresse|des|Slave!|X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
TWCR:&lt;br /&gt;
TWEN muss zum aktivieren des TWI gesetzt werden. TWEA muss gesetzt werden, damit der Slave antwortet, falls er von einem Master oder gegeben falls durch einen general call angesprochen wird. TWSTA und TWSTO müssen auf Null gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
{{ByteWithValues|&lt;br /&gt;
 |TWINT|TWEA|TWSTA|TWSTO|TWWC|TWEN|&amp;amp;ndash;|TWIE&lt;br /&gt;
 |0    |1   |0    |0    |0   |1   |0      |X&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Nach der Initialisierung wartet das TWI auf eine Anfrage gefolgt von einem Datenrichtungsbit. Wird das TWI angesprochen wird TWINT gesetzt und TWSR beinhaltet den Statuscode. Mit seiner Hilfe kann das weitere Vorgehen festgelegt werden. Ein Controller kann auch in den STM gelangen, wenn er die Hoheit auf dem Bus an einen anderen Master verliert (0xB0). &lt;br /&gt;
&lt;br /&gt;
Wird TWEA während eines Transfers gesetzt, wird das aktuelle Byte noch fertig gesendet und Status 0xC0 oder 0xC8 tritt ein, je nachdem ob der Master ACK oder NACK nach dem letzten Byte gesendet hatte. Das TWI wird in einen Blockierzustand gehen, unabhängig davon, ob der Master den Transfer fortsetzt. So erhält der Master nur noch &#039;1&#039; auf dem seriellen Bus. Status 0xC8 tritt ein, wenn der Master weitere Daten fordert (durch Senden von ACK), obwohl der Slave bereits das letzte Byte gesendet hat (TWEA &#039;0&#039; und erwartet NACK).&lt;br /&gt;
&lt;br /&gt;
Trotz des Blockierzustandes wird der Bus weiter beobachtet und das TWI kann durch setzen von TWEA jederzeit wieder daran teilnehmen. Somit kann der Slave mit Hilfe von TWEA kurzzeitig vom Bus isoliert werden.&lt;br /&gt;
&lt;br /&gt;
In sämtlichen Sleep-Modi (außer Idle Mode) ist der Systemtakt des TWI inaktiv. Dessen zu trotz kann das TWI durch setzen von TWEA allein mit Hilfe des Bustaktes erfassen, wenn es mit seiner Adresse bzw. durch einen general call angesprochen wird. Somit kann das TWI den Controller aufwecken. Während der Aufweck-Phase kann SCL auf L-Pegel gehalten werden wenn TWINT gelöscht ist (Schreiben einer &#039;1&#039;). Der Datenversand kann wieder im Normalbetrieb stattfinden. Zu beachten ist, dass solange SCL auf L-Pegel ist keine anderen Transmissionen stattfinden können. Wichtig ist, dass TWDR nach dem Aufwecken nicht mehr das letzte Byte, welches auf dem Bus war, enthält.&lt;br /&gt;
&lt;br /&gt;
Eine Tabelle mit sämtlichen Status-Codes und den zugehörigen Ereignissen ist dem Datenblatt S.185 zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zu den Betriebsart bezogenen Statuscodes gibt es noch zwei, welche davon unabhängig sind.&lt;br /&gt;
Status 0xF8 bedeutet, dass keine relevanten Informationen vorhanden sind, da TWINT nicht gesetzt ist. Dieser Fall tritt bei einem Statuswechsel ein oder wenn TWI im Moment nicht am Bus beteiligt ist.&lt;br /&gt;
&lt;br /&gt;
Status 0x00 steht für einen Busfehler während einer Übertragung. Ursache kann das Senden von START oder STOP zu einem nicht zulässigem Zeitpunkt sein. Bei solch einem Fehler wird TWINT automatisch gesetzt. Um die Fehlerroutine zu verlassen, muss TWSTO gesetzt und TWINT gelöscht werden. Dieses führt dazu, dass das TWI in den nicht adressierten Slave-Modus geht und SDA und SCL wieder freigegeben werden. Es wird kein STOP gesendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Wem das alles zuviel Theorie war und wer das I2C-Interface einmal im Einsatz erleben möchte, dem lege ich die Rubrik ICs &amp;amp; Co ans Herz. Dort wird die Verwendung eines I2C-EEPROMS demonstriert.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bekannte Bugs ==&lt;br /&gt;
Bei einigen ATMEGA Varianten (z.B. ATMEGA8) hat die TWI Implementierung einen Bug. Nach dem Aufwachen aus einem Sleep Modus funktioniert der TWI nicht mehr. Erst ein Reset mit &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
TWCR &amp;amp;= ~((1 &amp;lt;&amp;lt; TWSTO) | (1 &amp;lt;&amp;lt; TWEN));&lt;br /&gt;
TWCR |= (1 &amp;lt;&amp;lt; TWEN);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
nach dem Aufwachen löst das Problem.&lt;br /&gt;
&lt;br /&gt;
Nach dem senden eines &amp;quot;Stop&amp;quot; muß vor erneutem &amp;quot;Start&amp;quot; das TWSTO Bit im TWCR Register auf &amp;quot;0&amp;quot; abgefragt werden.&lt;br /&gt;
Wenn dies noch eins enthält ist der Stop nicht am Bus wirksam. &lt;br /&gt;
Ein erneuter Start innerhalb dieser Situation bringt statt der &amp;quot;Start&amp;quot; Rückmeldung &amp;quot;$08&amp;quot; die &amp;quot;Restart&amp;quot; Rückmeldung &amp;quot;$10&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[I2C]]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
Weitere gefundene Probleme :&lt;br /&gt;
* [https://www.mikrocontroller.net/topic/456827#5518419]&lt;br /&gt;
=== Application notes ===&lt;br /&gt;
&lt;br /&gt;
* [https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591792 AVR311: Using the TWI module as I2C slave (AN_2565) und Beispielcode]&lt;br /&gt;
&lt;br /&gt;
* [https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591794  AVR315: Using the TWI module as I2C master (AN2480) und Beispielcode]&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.peterfleury.epizy.com/avr-software.html Peter Fleury&#039;s AVR I2C Library]&lt;br /&gt;
&lt;br /&gt;
* http://www.mikrocontroller.net/topic/87597&lt;br /&gt;
&lt;br /&gt;
* [http://www.jtronics.de/avr-projekte.html AVR TWI Slave]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/235733#2388116 Hardware TWI-MASTER Interrupt basierend für Mega AVR] (ASM)&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]] &lt;br /&gt;
[[Category:Mc-project.de]]&lt;br /&gt;
[[Category:I2C]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=85087</id>
		<title>AVR HV-Programmer</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_HV-Programmer&amp;diff=85087"/>
		<updated>2014-10-05T08:09:54Z</updated>

		<summary type="html">&lt;p&gt;Pfleury: Link zu ATtiny Fuse Restore Projekt hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein &amp;quot;Hochspannungs&amp;quot;-Programmierer für AVR legt eine Spannung von +12V an den Reset-Eingang. Damit lassen sich auch Fusebits programmieren, die mit dem normalen ISP-Programmer nicht zugänglich sind.&lt;br /&gt;
&lt;br /&gt;
Zu unterscheiden sind das High Voltage Serial Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_HVSP_Description.htm HVSP]) und das Parallel Programming ([http://support.atmel.no/knowledgebase/avrstudiohelp/mergedProjects/AVRDragon/AVRDragon_PP_Description.htm PP]). Welche Methode(n) ein AVR unterstützt, steht im jeweiligen Datenblatt des AVRs.&lt;br /&gt;
&lt;br /&gt;
Bekannte HV Programmer sind:&lt;br /&gt;
* [[STK500]] (HVSP, PP)&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3891 AVR Dragon] (HVSP, PP)&lt;br /&gt;
* [http://www.der-hammer.info/hvprog/ HVProg von Tobias Hammer] (PP, HVSP)&lt;br /&gt;
* [http://www.b-redemann.de/produkte-programmer.shtml Bausatz und Anleitung von Bernhard Redemann] (PP)&lt;br /&gt;
* [http://www.obdev.at/products/avrusb/avrdoper.html AVR-Doper] High Voltage &#039;&#039;&#039;Serial&#039;&#039;&#039; Programmer (HVSP)&lt;br /&gt;
* [http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/ Arduino-based AVR High Voltage Programmer] (PP)&lt;br /&gt;
* [http://elm-chan.org/works/avrx/report_e.html &#039;&#039;&#039;Parallel&#039;&#039;&#039; HV-Programmer von ElmChan (ChaN)] (HVSP, PP)&lt;br /&gt;
* [http://diy.elektroda.eu/atmega-fusebit-doctor-hvpp/?lang=en Atmega fusebit doctor]  (HVSP, PP)&lt;br /&gt;
* [https://guloshop.de/shop/Mikrocontroller-Programmierung/HVSP-Fusebit-Programmer::63.html HVSP Fusebit Programmer] (HVSP, auch stand-alone ohne PC nutzbar)&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html ATtiny Fuse Restore] Anleitung für einen einfach aufzubauenden ATtiny Fuse Resetter mit AVR-GCC Software von Peter Fleury (HVSP)&lt;br /&gt;
&lt;br /&gt;
Hochvolt-Programmer sind &#039;&#039;nur dann&#039;&#039; als &#039;&#039;&#039;In-System&#039;&#039;&#039;-Programmer verwendbar, wenn die Schaltung mit den 12 V an der Resetleitung zurechtkommt und auch alle anderen Pins genügend hochohmig und ohne schaltungstechnische Nebenwirkungen (etwa einem Brückenkurzschluss) angeschlossen sind. Da dies bei der Menge anzuschließender Pins [im Vergleich zur Gesamtanzahl der Pins des Gehäuses] eher selten der Fall sein wird, werden die Chips fast immer „solo“ (sozusagen „Out-System“) gebrannt. Am ehesten bietet sich HVSP-ISP für 14-beinige SMD-AVRs an; dazu müssen in der Einsatzschaltung ggf. entsprechende Vorkehrungen getroffen werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Platinenlayout für ElmChans Hochvolt-Programmer ===&lt;br /&gt;
&lt;br /&gt;
Dieser benötigt einen PC mit Parallelport sowie eine externe Stromversorgung von 6..24 V=. USB-Parallel-Konverter (für Drucker) funktionieren &#039;&#039;nicht&#039;&#039;. Vorteil: Ein Mikrocontroller wird nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Original beherrscht dieser PP (parallel programming) als auch HVSP (serial programming).&lt;br /&gt;
Für den rein seriellen Gebrauch (HVSP der 8- und 14-Beiner) kann man auf den 74HC299 verzichten.&lt;br /&gt;
&lt;br /&gt;
Schaltplan im Eagle-Format, Bestückungspläne und Platinenlayout: [[Benutzer:Christoph kessler|Christoph Kessler]] &lt;br /&gt;
&lt;br /&gt;
ElmChans Konzept ist eine ausgesprochene „Billiglösung“, die 20-poligen AVRs bevorzugend, dem scheint eine einseitige Platine mit Drahtbrücken im Eagle-Light-Format 80×100 mm² angemessen. Jener verzichtet auf Spannungsregler; einen 78L05 hinzuzusetzen und das Ganze mit stabilisierten 12 V zu speisen sollte jedoch nicht allzu schwer fallen.&lt;br /&gt;
&lt;br /&gt;
Das Foto (unten) zeigt schon die „Luxusversion“, Teilbestückung ist entsprechend denkbar.&lt;br /&gt;
&lt;br /&gt;
[[Media:AVR_HV_Programmer_Eagle.zip | Eagle board / schematic files]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schematic.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Components.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Layout.png|600px]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_HV_Programmer_Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
===siehe auch===&lt;br /&gt;
[http://xray37.de/?Bastelbuch:Lima-SDR:RX Miniatur-Version von ElmChans HV-Programmer]&lt;br /&gt;
&lt;br /&gt;
[[AVR In System Programmer]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Programmer und -Bootloader]]&lt;br /&gt;
[[Kategorie:AVR-Boards]]&lt;/div&gt;</summary>
		<author><name>Pfleury</name></author>
	</entry>
</feed>