<?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=145.225.60.4</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=145.225.60.4"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/145.225.60.4"/>
	<updated>2026-04-10T21:37:03Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pollin_Funk-AVR-Evaluationsboard&amp;diff=82653</id>
		<title>Pollin Funk-AVR-Evaluationsboard</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Pollin_Funk-AVR-Evaluationsboard&amp;diff=82653"/>
		<updated>2014-04-15T10:18:08Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: Änderung 82652 von 159.224.160.42 (Diskussion) rückgängig gemacht.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Motivation ==&lt;br /&gt;
&lt;br /&gt;
Auf dieser Seite geht es um Erfahrungen eines Einsteigers mit dem Bausatz Funk-AVR-Evaluationsboard v1.1 von der Firma Pollin und um die C-Programmierung mithilfe des Boards und [[AVR-GCC]]/[[WinAVR]].     &lt;br /&gt;
&lt;br /&gt;
In den Beispielprogrammen wird nach Möglichkeit ein ATmega8 mit 12 MHz Quarz Taktquelle verwendet. Die Hardware auf dem Board ist sehr überschaubar (ein Taster, zwei LEDs, s. PDF unter Weblinks), so dass die Beispiele vielleicht zum eigenen Experimentieren anregen z.&amp;amp;nbsp;B. mit einem Steckbrettaufbau wie im [[AVR Tutorial]].&lt;br /&gt;
&lt;br /&gt;
Bitte nutzt die Diskussionsseite oder das Forum, wenn beim Lesen Fragen, Anregungen oder Kritik auftauchen, &lt;br /&gt;
&lt;br /&gt;
== Aufbau des Bausatzes ==&lt;br /&gt;
&lt;br /&gt;
Alle Angaben hier beziehen sich auf die Version v1.1 des Bausatzes. &lt;br /&gt;
&lt;br /&gt;
Mittlerweile (Stand 11/2008) verkauft Pollin die Version v1.2 und in dieser Version ist u.a. der Optokoppler nicht mehr vorhanden bzw. dessen Funktion übernehmen Jumper. Ebenso sind die anderen Leitungen von und zu den Funkmodulen über Jumper verbunden, d.h. man kann die Module elektrisch vom Board trennen (i.U. wichtig beim Fuse-Programmieren s.unten).&lt;br /&gt;
&lt;br /&gt;
Praktisch ist bei dem Aufbau des Bausatzes nichts besonderes zu vermelden. Für einen Ungeübten (mich) hat der Aufbau ca. 2 h gedauert. Gegen Ende der Löterei liess meine Konzentration nach und ich musste ein paar unsaubere Lötstellen mit Entlötlitze nachbehandeln. Besser eine Pause machen.&lt;br /&gt;
&lt;br /&gt;
Technisch/elektrisch siehe unter Weblinks der Erfahrungsbericht von Marco Schmoll (www.controller-designs.de).&lt;br /&gt;
&lt;br /&gt;
Nach der genauen Betrachtung mit einer Lupe und keinen Auffälligkeiten wurde eine 9V Gleichspannung an die Klemme J5 angelegt. LED NETZ leuchtet. LED1 und LED2 sind aus. Mein Netzteil kann den Strom anzeigen. Folgende Werte wurden beobachtet:&lt;br /&gt;
&lt;br /&gt;
* ca. 20 mA - AVR nicht eingesetzt, MAX232 nicht eingesetzt&lt;br /&gt;
* ca. 22 mA - AVR nicht eingesetzt, MAX232 nicht eingesetzt, RESET gedrückt&lt;br /&gt;
* ca. 25 mA - AVR nicht eingesetzt, MAX232 eingesetzt&lt;br /&gt;
* ca. 26 mA - AVR nicht eingesetzt, MAX232 eingesetzt, RESET gedrückt&lt;br /&gt;
* ca. 30 mA - &#039;&#039;&#039;ATmega8&#039;&#039;&#039; eingesetzt, MAX232 eingesetzt&lt;br /&gt;
&lt;br /&gt;
Mein Board v1.1 hat ein &#039;&#039;&#039;Problem mit PD7 bei der 28-Pol Fassung&#039;&#039;&#039; (Atmega8 u.a.). Da fehlt - nachgemessen mit Durchgangsprüfer - eine Verbindung zur PD7 an der 40-Pol-Fassung und zu dem entsprechenden Pin an der 40-Pol Wannenbuchse. Die Verbindung PD7-40-Pol zu der 40-pol Wannenbuchse ist vorhanden. Möglicherweise wurde das schon vorher beobachtet: http://robotikportal.de/phpBB2/viewtopic.php?p=321115#321115&lt;br /&gt;
&lt;br /&gt;
Wenn ein &#039;&#039;&#039;40-poliges IDE-Kabel&#039;&#039;&#039; in eigenen Schaltungen zum Verbinden mit J4 benutzt wird, vorher kontrollieren, ob im Kabel die benötigten Pins 1:1 verbunden/vorhanden sind (Danke an Markus [http://www.mikrocontroller.net/topic/167829#1604209], [http://www.mikrocontroller.net/topic/202999#1998317], [http://www.mikrocontroller.net/topic/207674#2053105])&lt;br /&gt;
&lt;br /&gt;
== Einstellen der Taktquelle ==&lt;br /&gt;
&lt;br /&gt;
Die folgenden Kommandozeilen zur Einstellung der Taktquelle in den [[AVR Fuses]] beziehen sich auf einen Windows PC und die ISP Programmiersoftware [[AVRDUDE]]. Kann nützlich sein, dafür kleine Batchdateien zu schreiben. &lt;br /&gt;
&lt;br /&gt;
Wenn ein [[RFM12|Funkmodul]] installiert ist, kann es zu Problemen beim ISP-Programmieren kommen [http://www.mikrocontroller.net/topic/102101]. Dies kann sich auch beim &#039;&#039;&#039;Ändern&#039;&#039;&#039; der Fuses auswirken. Im schlimmsten Fall wird der AVR unbrauchbar. Vorher kontrollieren!&lt;br /&gt;
&lt;br /&gt;
=== Serieller ISP auf dem Board ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 Fuses lesen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Serieller ISP auf dem Board&lt;br /&gt;
echo Lese ATmega8 Fuses&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schnittstelle COM1 ist an die verwendete Schnittstelle auf dem PC anzupassen. Wichtig ist, dass kein zusätzlicher Parallelport-ISP angeschlossen ist. Wenn doch, wird der Atmega8 nicht erkannt!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 1 MHz interner RC-Oszillator&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Serieller ISP auf dem Board&lt;br /&gt;
echo Setze ATmega8 Fuses auf 1 MHz interner RC-Oszillator&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1 -U lfuse:w:0xC1:m -U hfuse:w:0xD9:m&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 12 MHz Quarz&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn das Board nach Anleitung aufgebaut wurde, d.h. in Q2 der 12,000 MHz Quarz eingesetzt wurde, kann man den ATmega8 auf max. 12 MHz einstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Serieller ISP auf dem Board&lt;br /&gt;
echo Setze ATmega8 Fuses auf 12 MHz Quarz&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c ponyser -P com1 -U lfuse:w:0x2F:m -U hfuse:w:0xD9:m&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parallelport [[ISP]] Typ STK200 ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 Fuses lesen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Parallelport ISP Typ STK200&lt;br /&gt;
echo Lese ATmega8 Fuses&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Atmega8_Fuses_Werkseinstellung.png|thumb|right|256px|Werkseinstellung Fuses Atmega8 (Anzeige im AVR Fuse Calculator)]]&lt;br /&gt;
&lt;br /&gt;
Die Schnittstelle LPT1 ist an die verwendete Schnittstelle auf dem PC anzupassen. Ebenso der Pfad zu dem Programm avrdude.exe.&lt;br /&gt;
&lt;br /&gt;
Die ausgelesenen Fuses bei einem Fabrikneuen Atmega8 sollten dem Bild rechts entsprechen.&lt;br /&gt;
&lt;br /&gt;
Siehe auch&lt;br /&gt;
* [http://palmavr.sourceforge.net/cgi-bin/fc.cgi AVR Fuse Calculator] von Mark Hämmerling. In den dortigen Default-Einstellungen ist der [[Watchdog]] aktiviert. Lässt man das so, funktioniert das Programm Blinky (s.u.) nicht wie erwartet: Nur LED2 zappelt, LED1 ist meist aus, weil vor dem Umschalten von LED1 der Watchdog den Atmega8 resettet, d.h. das Programm von neuem starten lässt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 1 MHz interner RC-Oszillator&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Parallelport ISP Typ STK200&lt;br /&gt;
echo Setze ATmega8 Fuses auf 1 MHz interner RC-Oszillator&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1 -U lfuse:w:0xC1:m -U hfuse:w:0xD9:m&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ATmega8 12 MHz Quarz&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn das Board nach Anleitung aufgebaut wurde, d.h. in Q2 der 12,000 MHz Quarz eingesetzt wurde, kann man den ATmega8 auf max. 12 MHz einstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Parallelport ISP Typ STK200&lt;br /&gt;
echo Setze ATmega8 Fuses auf 12 MHz Quarz&lt;br /&gt;
d:\winavr\bin\avrdude -v -p atmega8 -c stk200 -P lpt1 -U lfuse:w:0x2F:m -U hfuse:w:0xD9:m&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Programm ins Flash-ROM schreiben ==&lt;br /&gt;
&lt;br /&gt;
Im folgenden wird angenommen, dass die zu programmierende Datei im iHEX-Format unter dem Namen atmega8.hex im aktuellen Verzeichnis befindet. Wenn ein Funkmodul installiert ist, kann es zu Problemen beim Flashen kommen [http://www.mikrocontroller.net/topic/102101].&lt;br /&gt;
&lt;br /&gt;
=== Serieller ISP auf dem Board ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Serieller ISP auf dem Board&lt;br /&gt;
echo Programmiere Atmega8 Flash-ROM mit Datei atmega8.hex&lt;br /&gt;
d:\winavr\bin\avrdude -p atmega8 -c ponyser -P com1 -e -U flash:w:atmega8.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parallelport ISP Typ STK200 ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo Parallelport ISP Typ STK200&lt;br /&gt;
echo Programmiere Atmega8 Flash-ROM mit Datei atmega8.hex&lt;br /&gt;
d:\winavr\bin\avrdude -p atmega8 -c stk200 -P lpt1 -e -U flash:w:atmega8.hex&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispielprogramme ==&lt;br /&gt;
&lt;br /&gt;
=== Blinky ===&lt;br /&gt;
&lt;br /&gt;
Die beiden [[LED]] LED1 und LED2 auf dem Board sollen im 1s Takt wechselweise An und Aus gehen. &lt;br /&gt;
&lt;br /&gt;
Die beiden On-board-LEDs sind &#039;&#039;active-high&#039;&#039; geschaltet, d.h. wenn am Pin des AVR ein logische 1 (HIGH Pegel) ausgegeben wird, leuchtet die LED. Wird eine logische 0 (LOW Pegel) ausgegeben, leuchtet die LED nicht. Das ist andersrum als im [[AVR-Tutorial: IO-Grundlagen#Hardware|AVR Tutorial]].&lt;br /&gt;
&lt;br /&gt;
[[Bild:Atmega8_Taster_LEDs.png|Beschaltung des Tasters und der LEDs]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
&lt;br /&gt;
    Project -&amp;gt; Configuration Options in AVR Studio:&lt;br /&gt;
    Frequency:    1000000 bzw. 12000000&lt;br /&gt;
    Optimization: -Os&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// LEDs sind active-high geschaltet&lt;br /&gt;
#define LED_AN(LED)	(PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)	(PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED)	(PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1		PD6&lt;br /&gt;
#define LED2		PD5&lt;br /&gt;
#define TASTER	        PB1&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);          // Port B: Eingang für Taster&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2); // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
  // Anfangseinstellung&lt;br /&gt;
  LED_AN(LED1);&lt;br /&gt;
  LED_AUS(LED2);&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    _delay_ms(1000);  // Wert 1000 erlaubt ab avr-libc 1.6&lt;br /&gt;
    LED_TOGGLE(LED1);&lt;br /&gt;
    LED_TOGGLE(LED2);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tasty ===&lt;br /&gt;
&lt;br /&gt;
Wenn der On-board-Taster TASTER1 nicht gedrückt ist (Ruhezustand), soll die LED1 leuchten und LED2 soll nicht leuchten. Solange der User den Taster TASTER1 gedrückt hält, soll sich der Zustand der LEDs umkehren. &lt;br /&gt;
&lt;br /&gt;
Der On-board-Taster TASTER1 ist ebenfalls &#039;&#039;active-high&#039;&#039; (siehe [[AVR-GCC-Tutorial#Tasten_und_Schalter|AVR-GCC-Tutorial]]) geschaltet, d.h. wenn der Taster geschlossen ist, liegt am Pin PB1 des AVR eine logische 1 (HIGH Pegel) an. Ist der Taster offen, liegt eine eine logische 0 (LOW Pegel) an. Das ist andersrum als im [[AVR-Tutorial: IO-Grundlagen#Hardware|AVR Tutorial]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// LEDs sind high-active geschaltet&lt;br /&gt;
#define LED_AN(LED)	(PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)	(PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED)	(PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1		PD6&lt;br /&gt;
#define LED2		PD5&lt;br /&gt;
&lt;br /&gt;
// TASTER ist high-active geschaltet &lt;br /&gt;
#define TASTER	PB1&lt;br /&gt;
#define TASTER_GEDRUECKT()	(PINB &amp;amp; (1&amp;lt;&amp;lt;TASTER))&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);	         // Port B: Eingang für Taster&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2); // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    if (!TASTER_GEDRUECKT())&lt;br /&gt;
    {&lt;br /&gt;
      // Taster ist nicht (!) gedrückt&lt;br /&gt;
      LED_AN(LED1);&lt;br /&gt;
      LED_AUS(LED2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      // Taster ist gedrückt&lt;br /&gt;
      LED_AUS(LED1);&lt;br /&gt;
      LED_AN(LED2);&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;
=== 2-Bit Zähler ===&lt;br /&gt;
&lt;br /&gt;
Jeder Tastendruck auf TASTER1 soll eine Variable um Eins hochzählen. Der Inhalt der unteren beiden Bits der Zählvariable soll mit den beiden LEDs angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Das Hochzählen darf nur erfolgen, wenn ein Wechsel von Offen nach Geschlossen stattfindet. Das Programm muss also berücksichtigen, ob ein Wechsel von &amp;quot;Taster offen&amp;quot; zu &amp;quot;Taster geschlossen&amp;quot; stattfindet und ob der Taster in einer Position gehalten wird. &lt;br /&gt;
&lt;br /&gt;
Mit diesem Beispiel kann man grob feststellen, ob der TASTER1 auf dem Board zum Prellen neigt, d.h. wenn sich der Zählerstand nicht wie gewollt pro Tastendruck um Eins erhöht und ob deshalb eine spezielle Routine zur [[Entprellung]] erforderlich ist. &lt;br /&gt;
&lt;br /&gt;
Mein Board zeigt bei diesem Programm keine Neigung zum Prellen. Die auf dem Board vorhandene [[Entprellung#Hardwareentprellung|Hardwareentprellung]] über einen [[Filter#Tiefpass|Tiefpassfilter]] mit C17 330 nF zwischen Taster und GND erfüllt hier ihren Zweck.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// LEDs sind high-active geschaltet&lt;br /&gt;
#define LED_AN(LED)	(PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)	(PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED)	(PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1		PD6&lt;br /&gt;
#define LED2		PD5&lt;br /&gt;
&lt;br /&gt;
// TASTER ist high-active geschaltet &lt;br /&gt;
#define TASTER	PB1&lt;br /&gt;
#define TASTER_GEDRUECKT()	(PINB &amp;amp; (1&amp;lt;&amp;lt;TASTER))&lt;br /&gt;
#define TASTE_AUF 0&lt;br /&gt;
#define TASTE_ZU  1&lt;br /&gt;
&lt;br /&gt;
void ausgabe(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
  if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
    LED_AN(LED1);&lt;br /&gt;
  else&lt;br /&gt;
    LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
  if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
    LED_AN(LED2);&lt;br /&gt;
  else&lt;br /&gt;
    LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
  uint8_t zaehler = 0;&lt;br /&gt;
&lt;br /&gt;
  DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);			// Port B: Eingang für Taster&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);	// Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    ausgabe(zaehler);&lt;br /&gt;
&lt;br /&gt;
    if (TASTER_GEDRUECKT() &amp;amp;&amp;amp; (alter_tastenzustand == TASTE_AUF))&lt;br /&gt;
    {&lt;br /&gt;
      // Wechsel von OFFEN nach GESCHLOSSEN&lt;br /&gt;
      zaehler++;&lt;br /&gt;
      alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!TASTER_GEDRUECKT())&lt;br /&gt;
      alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 8-Bit Zähler mit RS232-Anschluss ===&lt;br /&gt;
&lt;br /&gt;
Das Beispiel 2-Bit Zähler soll jetzt ausgebaut werden. Im Detail soll der 8-Bit Zählerstand über [[RS232]] auf einen PC ausgegeben werden. Ausserdem soll &amp;quot;ferngesteuert&amp;quot; eine Veränderung des Zählerstands vom PC aus möglich sein.&lt;br /&gt;
&lt;br /&gt;
Auf der PC-Seite wird die Kommunikation mit einem Terminalprogramm z.&amp;amp;nbsp;B. HyperTerm gemacht, so dass hier keine Programmierung notwendig ist. Lediglich die Einstellung der RS232-Schnittstelle (z.&amp;amp;nbsp;B. COM1, 9600/8/N/1) muss passen. Auf der Atmega8-Seite sind zunächst wenige Grundfunktionen für die RS232-Kommunikation zu schreiben. &lt;br /&gt;
&lt;br /&gt;
Die AVR-Grundfunktionen für die RS232-Kommunikation sind eine Funktion für die Initialisierung der µC-eigenen UART-Schnittstelle (Bsp.: UART_init) und eine Funktion für das Senden eines Zeichens (Bsp.: UART_putchar) sowie je eine Funktion für das Warten auf ein Zeichens (Bsp.: UART_getchar) bzw. eine nicht-wartende Funktion um festzustellen, ob ein Zeichen des PCs an der UART-Schnittstelle des µC anliegt (Bsp. UART_peekchar). &lt;br /&gt;
&lt;br /&gt;
Als Programmablauf wurde die UART-Kommunikation mit der technisch relativ einfachen Methode Polling eingerichtet, d.h. das Programm fragt selbst möglichst regelmässig und oft genug für einen sinnvolle Kommunikation die UART Schnittstelle ab, ob Zeichen empfangen wurden oder gesendet werden können. Hier erklärt sich auch der Zweck für die nicht-wartende Funktion UART_peekchar() - wenn kein Zeichen vom PC über RS232 anliegt, soll der µC mit dem bekannten Programmablauf weitermachen, damit die Taster-Eingaben ausgewertet werden. Die technisch meistens vorteilhaftere Alternative wäre die Interruptmethode, d.h. das Hauptprogramm wird automatisch genau dann unterbrochen, wenn ein Zeichen empfangen wurde oder gesendet werden kann und der µC verzweigt in spezielle Grundfunktionen, die sog. Interrupthandler, die sich um die Kommunikation kümmern. Nach dem Abarbeiten der Unterbrechung geht es dann im eigentlichen Hauptprogramm weiter. Ein UART_peekchar-Trick ist bei dieser Programmierweise nicht nötig. Die Programmierweise mit UART-Interrupts könnte Teil eines künftigen Beispiels sein.&lt;br /&gt;
&lt;br /&gt;
Die Grundfunktionen werden von einer allgemeinen Funktion (Bsp.: rs232) verwendet, um den Zählerstand an den PC auszugeben bzw. um Eingaben auf dem PC in einen neuen Zählerstand umzusetzen. Da die Funktion rs232 den Zählerstand ändern soll, wird ihr die Adresse der Zählervariable (&amp;amp;zaehler) übergeben, d.h. es wird hier mit Zeigern (Pointern) gearbeitet.&lt;br /&gt;
&lt;br /&gt;
Per RS232 können komfortabel mehr Informationen ausgegeben werden als über die beiden LEDs. Im Beispiel wird der Zählerstand in drei verschiedenen Zahlensystemen ausgegeben und es wird der Zustand der beiden LEDs übermittelt. Der Clou hinsichtlich Bedienung des Zählers ist, dass per RS232 auch komfortabel mehr Bedienmöglichkeiten eingerichtet werden können als mit dem einfachen Taster: Zusätzlich zum Hochzählen (Plus-Taste) kann z.&amp;amp;nbsp;B. runtergezählt werden (Minus-Taste) oder es kann direkt eine bis zu dreistellige Zahlenfolge eingegeben werden, die zum Setzen des Zählerstands verwendet wird. &lt;br /&gt;
&lt;br /&gt;
Bei der Initialisierung der UART wurden die Einstellungen 9600 Baud, 8 Datenbits, Keine Parity (No Parity) und 1 Stopbit gewählt. 8/N/1 ist ein gängiger Wert für RS232. Bei der Auswahl der Baudrate muss darauf geachtet werden, dass mit der gegebenen Taktrate auf dem Board (hier 12 MHz) nicht jede denkbare RS232-Baudrate gleich gut geeignet ist. Das Register zur Einstellung der Baudrate im µC kann nur mit ganzen Zahlen gefüllt werden, die dann benutzt werden, um durch Teilen die Taktrate der UART aus dem Takt der CPU abzuleiten. Bei einer CPU Taktrate von 12 MHz ergeben folgende Soll-Baudraten (in Baud) die angegebenen  Einstellungen des Baudratenregisters (UBRR) und die Ist-Baudraten sowie die Abweichungen zwischen Soll- und Ist-Baudrate in Prozent: Die Formeln zur Berechnung stehen im Atmega8 Datenblatt im Kapitel UART.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;baudrate-12mhz&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|Soll-Baudrate&lt;br /&gt;
|UBRR&amp;lt;br&amp;gt;(bei U2X=0)&lt;br /&gt;
|Ist-Baudrate&amp;lt;br&amp;gt;(bei U2X=0)&lt;br /&gt;
|Abweichung [%]&amp;lt;br&amp;gt;(bei U2X=0)&lt;br /&gt;
|UBRR&amp;lt;br&amp;gt;(bei U2X=1)&lt;br /&gt;
|Ist-Baudrate&amp;lt;br&amp;gt;(bei U2X=1)&lt;br /&gt;
|Abweichung [%]&amp;lt;br&amp;gt;(bei U2X=1)&lt;br /&gt;
|-&lt;br /&gt;
|300&lt;br /&gt;
|2499&lt;br /&gt;
|300&lt;br /&gt;
|0,0&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
|1200&lt;br /&gt;
|624&lt;br /&gt;
|1200&lt;br /&gt;
|0,0&lt;br /&gt;
|1249&lt;br /&gt;
|1200&lt;br /&gt;
|0,0&lt;br /&gt;
|-&lt;br /&gt;
|2400&lt;br /&gt;
|311&lt;br /&gt;
|2403&lt;br /&gt;
|0,1&lt;br /&gt;
|624&lt;br /&gt;
|2400&lt;br /&gt;
|0,0&lt;br /&gt;
|-&lt;br /&gt;
|4800&lt;br /&gt;
|155&lt;br /&gt;
|4807&lt;br /&gt;
|0,1&lt;br /&gt;
|311&lt;br /&gt;
|4807&lt;br /&gt;
|0,1&lt;br /&gt;
|-&lt;br /&gt;
|9600&lt;br /&gt;
|77&lt;br /&gt;
|9615&lt;br /&gt;
|0,2&lt;br /&gt;
|155&lt;br /&gt;
|9615&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|14,4K&lt;br /&gt;
|51&lt;br /&gt;
|14423&lt;br /&gt;
|0,2&lt;br /&gt;
|103&lt;br /&gt;
|14423&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|19,2K&lt;br /&gt;
|38&lt;br /&gt;
|19230&lt;br /&gt;
|0,2&lt;br /&gt;
|77&lt;br /&gt;
|19230&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|28,8K&lt;br /&gt;
|25&lt;br /&gt;
|28846&lt;br /&gt;
|0,2&lt;br /&gt;
|51&lt;br /&gt;
|28846&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|38,4K&lt;br /&gt;
|18&lt;br /&gt;
|37473&lt;br /&gt;
|2,8&lt;br /&gt;
|38&lt;br /&gt;
|38461&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|57,6K&lt;br /&gt;
|12&lt;br /&gt;
|57692&lt;br /&gt;
|0,2&lt;br /&gt;
|25&lt;br /&gt;
|57692&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|76,8K&lt;br /&gt;
|8&lt;br /&gt;
|83333&lt;br /&gt;
|8,5&lt;br /&gt;
|16&lt;br /&gt;
|78947&lt;br /&gt;
|2,8&lt;br /&gt;
|-&lt;br /&gt;
|115,2K&lt;br /&gt;
|5&lt;br /&gt;
|125000&lt;br /&gt;
|8,5&lt;br /&gt;
|12&lt;br /&gt;
|115384&lt;br /&gt;
|0,2&lt;br /&gt;
|-&lt;br /&gt;
|230,4K&lt;br /&gt;
|2&lt;br /&gt;
|250000&lt;br /&gt;
|8,5&lt;br /&gt;
|5&lt;br /&gt;
|250000&lt;br /&gt;
|8,5&lt;br /&gt;
|-&lt;br /&gt;
|250K&lt;br /&gt;
|2&lt;br /&gt;
|250000&lt;br /&gt;
|0,0&lt;br /&gt;
|5&lt;br /&gt;
|250000&lt;br /&gt;
|0,0&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bestimmte Baudraten können ohne Abweichung oder mit tolerierbar kleiner Abweichung (max. 0,2%) eingestellt werden, während andere Baudraten immer zu grosse Abweichungen für eine fehlerfreie Kommunikation ergeben. Man kann obige Tabelle (von denen es ähnliche im Datenblatt für andere Taktraten gibt) verwenden oder man kann die UBRR Einstellung im Programmcode berechnen lassen (s. Programmcode unten). Im AVR-GCC-Tutorial ist zusätzlich ganz komfortabel eine Berechnung der Abweichung und eine Warnung bei zu grossen Werten angegeben, so dass man seine Wunschbaudrate in Richtung geringe Abweichung optimieren kann. Im folgenden Code wurde die gängige Baudrate 9600 Baud eingestellt.&lt;br /&gt;
&lt;br /&gt;
Eine letzte grössere Änderung gegenüber dem 2-Bit-Zähler fällt an der Stelle der Taster-Eingabe auf: Jetzt mit der genaueren RS232-Ausgabe wurden Prelleffekte als unregelmässige Sprünge im Zählerstand beobachtet. Deshalb wurde Programmcode für eine einfache Entprellung nach dem [[Entprellung#Warteschleifen-Verfahren|Warteschleifenverfahren]] hinzugefügt. Wenn das Makro ENTPRELLUNG mit 0 definiert ist, entfällt die Entprellung, d.h. das Programm reagiert wie beim 2-Bit Zähler auf den Taster. Um die Entprellung zu aktivieren, wird das Makro ENTPRELLUNG z.&amp;amp;nbsp;B. mit 10, 25 oder 30 definiert und neu kompiliert und geflasht. Die Zahl im Makro gibt eine Wartezeit an, nach der der Zustand des Tasters nochmal geprüft wird. Hier kann man etwas experimentieren, was ein geeigneter Wert wäre.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// LEDs sind high-active geschaltet&lt;br /&gt;
#define LED_AN(LED)     (PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)    (PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED) (PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1            PD6&lt;br /&gt;
#define LED2            PD5&lt;br /&gt;
&lt;br /&gt;
// TASTER ist high-active geschaltet&lt;br /&gt;
#define TASTER             PB1&lt;br /&gt;
#define TASTER_GEDRUECKT() (PINB &amp;amp; (1&amp;lt;&amp;lt;TASTER))&lt;br /&gt;
#define TASTE_AUF          0&lt;br /&gt;
#define TASTE_ZU           1&lt;br /&gt;
/* in Millisekunden */&lt;br /&gt;
#define ENTPRELLUNG        10&lt;br /&gt;
&lt;br /&gt;
void UART_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Baudrate setzen&lt;br /&gt;
    // 9600 Baud bei F_CPU 12 MHz&lt;br /&gt;
    // (Baudratenfehler = +0,2%)&lt;br /&gt;
    UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) &amp;gt;&amp;gt; 8);&lt;br /&gt;
    UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);&lt;br /&gt;
&lt;br /&gt;
    //      Empfangen,   Senden erlauben&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL) | ((1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0)) | ((0&amp;lt;&amp;lt;UPM1) | (0&amp;lt;&amp;lt;UPM0)) | (0&amp;lt;&amp;lt;USBS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // sofort zurückkehren und Zustand melden&lt;br /&gt;
    return UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Eingang voll&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Ausgang frei&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)) )&lt;br /&gt;
        ;&lt;br /&gt;
    UDR = z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s)&lt;br /&gt;
{&lt;br /&gt;
    while ( *s )&lt;br /&gt;
    {&lt;br /&gt;
        UART_putchar(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define EINGABE_START 	0&lt;br /&gt;
#define EINGABE_ABBRUCH EINGABE_START&lt;br /&gt;
#define EINGABE_MAX 		3&lt;br /&gt;
#define FORMFEED &#039;\014&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void rs232(uint8_t *wert)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    uint8_t anzahl_ziffern;&lt;br /&gt;
    uint8_t zeichen;&lt;br /&gt;
    char puffer[23];&lt;br /&gt;
&lt;br /&gt;
    // Eingabe über RS232&lt;br /&gt;
&lt;br /&gt;
    // Ohne Warten prüfen, ob Zeichen an UART vorhanden&lt;br /&gt;
    if ( UART_peekchar() )&lt;br /&gt;
    {&lt;br /&gt;
        // Zeichen ist da.&lt;br /&gt;
&lt;br /&gt;
        // Aktuellen Wert ausgeben&lt;br /&gt;
&lt;br /&gt;
        // Neue Seite im Terminal anfordern.&lt;br /&gt;
        // Bewirkt ein Löschen des Bildschirms&lt;br /&gt;
&lt;br /&gt;
        UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
        // Einleitender Text, was im folgenden ausgegeben wird&lt;br /&gt;
        UART_puts(&amp;quot;Alter Z\204hler = &amp;quot;); // \204 ist ä in Oktalschreibweise&lt;br /&gt;
&lt;br /&gt;
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
        itoa((int) *wert, puffer, 10);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Dann die gleiche Zahl in Hexadezimalziffern&lt;br /&gt;
        UART_puts(&amp;quot; 0x&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 16);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Dann die gleiche Zahl in Binärziffern&lt;br /&gt;
        UART_puts(&amp;quot; 0b&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 2);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Schliesslich noch der Zustand der LEDs auf dem Board senden&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 2) ? &amp;quot; LED2-An &amp;quot; : &amp;quot; LED2-Aus&amp;quot;);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 1) ? &amp;quot; LED1-An&amp;quot; : &amp;quot; LED1-Aus&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Zeilenabschluss zur Formatierung der Anzeige&lt;br /&gt;
        UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Neuen Wert eingeben&lt;br /&gt;
&lt;br /&gt;
        // Initialisierung&lt;br /&gt;
        UART_puts(&amp;quot;Eingabe&amp;gt; &amp;quot;);&lt;br /&gt;
        anzahl_ziffern = EINGABE_START;&lt;br /&gt;
&lt;br /&gt;
        // Eingabeschleife&lt;br /&gt;
        do&lt;br /&gt;
        {&lt;br /&gt;
            // 1. bereits Zeichen am UART Eingang abholen&lt;br /&gt;
            // in weiteren Schleifen auf neue Zeichen warten&lt;br /&gt;
            zeichen = UART_getchar();&lt;br /&gt;
&lt;br /&gt;
            // Zeichen auswerten&lt;br /&gt;
            if ( (zeichen == &#039;-&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // MINUS&lt;br /&gt;
                UART_puts(&amp;quot; -1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert -= 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;+&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // PLUS&lt;br /&gt;
                UART_puts(&amp;quot; +1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert += 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;\n&#039;) || (zeichen == &#039;\r&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // ENTER oder RETURN: Abschluss der Eingabe&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen &amp;gt;= &#039;0&#039;) &amp;amp;&amp;amp; (zeichen &amp;lt;= &#039;9&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // Eingabe einer Ziffer&lt;br /&gt;
                UART_putchar(zeichen); // Echo&lt;br /&gt;
                puffer[anzahl_ziffern++] = zeichen;&lt;br /&gt;
                puffer[anzahl_ziffern] = 0;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                // Sonstiges Zeichen ist für uns illegal&lt;br /&gt;
                anzahl_ziffern = EINGABE_ABBRUCH;&lt;br /&gt;
                UART_puts(&amp;quot; Abbruch.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while ( anzahl_ziffern &amp;lt; EINGABE_MAX );&lt;br /&gt;
&lt;br /&gt;
        // Eingabe prüfen&lt;br /&gt;
        if ( anzahl_ziffern != EINGABE_ABBRUCH )&lt;br /&gt;
        {&lt;br /&gt;
            // Ziffern in Zahl umwandeln&lt;br /&gt;
            int i = atoi(puffer);&lt;br /&gt;
&lt;br /&gt;
            // z.&amp;amp;nbsp;B. erlaubter Wertebereich sei 0 bis 255&lt;br /&gt;
            if ( (i &amp;gt;= 0) &amp;amp;&amp;amp; (i &amp;lt; 256) )&lt;br /&gt;
            {&lt;br /&gt;
                UART_puts(&amp;quot; Ok.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert = (uint8_t) i;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
                UART_puts(&amp;quot; Ung\201ltig.\r\n&amp;quot;); // \201 ist ü in Oktalschreibweise&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe auf RS232 (s.oben)&lt;br /&gt;
    // bei nur Änderung des Wertes gegenüber der letzten Ausgabe&lt;br /&gt;
    //&lt;br /&gt;
    // alter_wert wurde mit -1 initialisiert. Das ist ausserhalb&lt;br /&gt;
    // des Wertebereichs von *wert, D.h. spätere Abfrage, ob&lt;br /&gt;
    // (alter_wert != *wert) ist, ist beim ersten Durchlauf wie&lt;br /&gt;
    // gewünscht wahr,&lt;br /&gt;
    //&lt;br /&gt;
    if (alter_wert != *wert)&lt;br /&gt;
    {&lt;br /&gt;
        alter_wert = *wert;&lt;br /&gt;
        UART_puts(&amp;quot;Z\204hler = &amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 10);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts(&amp;quot; 0x&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 16);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts(&amp;quot; 0b&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 2);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 2) ? &amp;quot; LED2-An &amp;quot; : &amp;quot; LED2-Aus&amp;quot;);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 1) ? &amp;quot; LED1-An&amp;quot; : &amp;quot; LED1-Aus&amp;quot;);&lt;br /&gt;
        UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ausgabe_led(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
        LED_AN(LED1);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
        LED_AN(LED2);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
    uint8_t zaehler = 0;&lt;br /&gt;
&lt;br /&gt;
    DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);           // Port B: Eingang für Taster&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);  // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
    UART_init();&lt;br /&gt;
    UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        rs232(&amp;amp;zaehler);&lt;br /&gt;
&lt;br /&gt;
        ausgabe_led(zaehler);&lt;br /&gt;
&lt;br /&gt;
        if ( TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
            {&lt;br /&gt;
                // Wechsel von OFFEN nach GESCHLOSSEN?&lt;br /&gt;
                if ( alter_tastenzustand == TASTE_AUF )&lt;br /&gt;
                {&lt;br /&gt;
                    zaehler++;&lt;br /&gt;
                    alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
                alter_tastenzustand = TASTE_AUF;&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;
=== Zähler mit LCD-Anzeige ===&lt;br /&gt;
&lt;br /&gt;
Als nächstes soll noch eine [[LCD]]-Anzeige installiert werden. &lt;br /&gt;
&lt;br /&gt;
In meiner Bastelkiste lag noch ein Text-LCD mit dem LCD-Controller [[HD44780]]. Erfreulicherweise gibt es für diesen LCD-Controller jede Menge fertigen Beispielcode zur Ansteuerung im Internet u.a. im [[AVR-GCC-Tutorial#LCD-Ansteuerung|AVR-GCC-Tutorial]].&lt;br /&gt;
&lt;br /&gt;
Um den Beispielcode in der Form einzubinden, wie er im Tutorial zu finden ist und um das mittlerweise angewachsene Projekt übersichtlicher zu machen, werden zusammenhängende Codeteile in Unterdateien ausgelagert. Im folgenden Bild ist zu sehen, wie die einzelnen Dateien heissen und wie sie im AVR Studio eingebunden werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA_Projekt_ZLCD.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Datei &#039;&#039;&#039;zaehler_lcd.c&#039;&#039;&#039; enthält die main-Funktion und die neue Ausgabefunktion ausgabe_lcd und die bereits bekannte Funktion ausgabe_led:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;rs232.h&amp;quot;&lt;br /&gt;
#include &amp;quot;lcd_routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* in Millisekunden */&lt;br /&gt;
#define ENTPRELLUNG        10&lt;br /&gt;
&lt;br /&gt;
void ausgabe_lcd(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    char puffer[23];&lt;br /&gt;
&lt;br /&gt;
    if (wert != alter_wert)&lt;br /&gt;
    {&lt;br /&gt;
        // Ausgabe nur bei Änderungen&lt;br /&gt;
        alter_wert = wert;&lt;br /&gt;
        itoa((int) wert, puffer, 10);&lt;br /&gt;
&lt;br /&gt;
        lcd_clear();&lt;br /&gt;
        lcd_string(&amp;quot;Zaehler = &amp;quot;);&lt;br /&gt;
        lcd_string(puffer);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ausgabe_led(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
        LED_AN(LED1);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
        LED_AN(LED2);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
    uint8_t zaehler = 0;&lt;br /&gt;
&lt;br /&gt;
    DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);           // Port B: Eingang für Taster&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);  // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
    UART_init();&lt;br /&gt;
    UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        rs232(&amp;amp;zaehler);			// RS232 Eingabe und Ausgabe&lt;br /&gt;
&lt;br /&gt;
        ausgabe_lcd(zaehler); // LCD Ausgabe&lt;br /&gt;
&lt;br /&gt;
        ausgabe_led(zaehler); // LED Ausgabe&lt;br /&gt;
&lt;br /&gt;
        if ( TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
            {&lt;br /&gt;
                // Wechsel von OFFEN nach GESCHLOSSEN?&lt;br /&gt;
                if ( alter_tastenzustand == TASTE_AUF )&lt;br /&gt;
                {&lt;br /&gt;
                    zaehler++;&lt;br /&gt;
                    alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
                alter_tastenzustand = TASTE_AUF;&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;
Die Datei &#039;&#039;&#039;lcd_routines.c&#039;&#039;&#039; enthält die Grundfunktionen für die LCD-Ansteuerung aus dem AVR-GCC-Tutorial. &lt;br /&gt;
&lt;br /&gt;
In der Funktion lcd_enable musste das Timing leicht angepasst werden, weil bei meinem Display mit der 1 µs Pause Störungen aufgetreten waren.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&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;
// sendet ein Datenbyte an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_data(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char temp2 = temp1;&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;
    temp1 = temp1 &amp;gt;&amp;gt; 4;&lt;br /&gt;
    temp1 = temp1 &amp;amp; 0x0F;&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= temp1;               // setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    temp2 = temp2 &amp;amp; 0x0F;&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= temp2;               // setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// sendet einen Befehl an das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_command(unsigned char temp1)&lt;br /&gt;
{&lt;br /&gt;
    unsigned char temp2 = temp1;&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;
    temp1 = temp1 &amp;gt;&amp;gt; 4;              // oberes Nibble holen&lt;br /&gt;
    temp1 = temp1 &amp;amp; 0x0F;            // maskieren&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= temp1;               // setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    temp2 = temp2 &amp;amp; 0x0F;            // unteres Nibble holen und maskieren&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= temp2;               // setzen&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    _delay_us(42);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// erzeugt den Enable-Puls&lt;br /&gt;
void lcd_enable(void)&lt;br /&gt;
{&lt;br /&gt;
    // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen&lt;br /&gt;
    // http://www.mikrocontroller.net/topic/81974#685882&lt;br /&gt;
    LCD_PORT |= (1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
#if 1&lt;br /&gt;
    _delay_us(4); // kurze Pause, Original aus dem Tutorial ist bei meinem Display zu kurz!&lt;br /&gt;
#else&lt;br /&gt;
    _delay_us(1); // kurze Pause&lt;br /&gt;
#endif&lt;br /&gt;
    // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern&lt;br /&gt;
    // http://www.mikrocontroller.net/topic/80900&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_EN);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Initialisierung:&lt;br /&gt;
// Muss ganz am Anfang des Programms aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
void lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
    LCD_DDR = LCD_DDR | 0x0F | (1&amp;lt;&amp;lt;LCD_RS) | (1&amp;lt;&amp;lt;LCD_EN);   // Port auf Ausgang schalten&lt;br /&gt;
&lt;br /&gt;
    // muss 3mal hintereinander gesendet werden zur Initialisierung&lt;br /&gt;
&lt;br /&gt;
    _delay_ms(15);&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= 0x03;&lt;br /&gt;
    LCD_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;LCD_RS);      // RS auf 0&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    _delay_ms(5);&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
&lt;br /&gt;
    _delay_ms(1);&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
    // 4 Bit Modus aktivieren&lt;br /&gt;
    LCD_PORT &amp;amp;= 0xF0;&lt;br /&gt;
    LCD_PORT |= 0x02;&lt;br /&gt;
    lcd_enable();&lt;br /&gt;
    _delay_ms(1);&lt;br /&gt;
&lt;br /&gt;
    // 4Bit / 2 Zeilen / 5x7&lt;br /&gt;
    lcd_command(0x28);&lt;br /&gt;
&lt;br /&gt;
    // Display ein / Cursor aus / kein Blinken&lt;br /&gt;
    lcd_command(0x0C);&lt;br /&gt;
&lt;br /&gt;
    // inkrement / kein Scrollen&lt;br /&gt;
    lcd_command(0x06);&lt;br /&gt;
&lt;br /&gt;
    lcd_clear();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sendet den Befehl zur Löschung des Displays&lt;br /&gt;
&lt;br /&gt;
void lcd_clear(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_command(CLEAR_DISPLAY);&lt;br /&gt;
    _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Sendet den Befehl: Cursor Home&lt;br /&gt;
&lt;br /&gt;
void lcd_home(void)&lt;br /&gt;
{&lt;br /&gt;
    lcd_command(CURSOR_HOME);&lt;br /&gt;
    _delay_ms(5);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)&lt;br /&gt;
&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t tmp;&lt;br /&gt;
&lt;br /&gt;
    switch (y)&lt;br /&gt;
    {&lt;br /&gt;
    case 1:&lt;br /&gt;
        tmp=0x80+0x00+x;&lt;br /&gt;
        break;    // 1. Zeile&lt;br /&gt;
    case 2:&lt;br /&gt;
        tmp=0x80+0x40+x;&lt;br /&gt;
        break;    // 2. Zeile&lt;br /&gt;
    case 3:&lt;br /&gt;
        tmp=0x80+0x10+x;&lt;br /&gt;
        break;    // 3. Zeile&lt;br /&gt;
    case 4:&lt;br /&gt;
        tmp=0x80+0x50+x;&lt;br /&gt;
        break;    // 4. Zeile&lt;br /&gt;
    }&lt;br /&gt;
    lcd_command(tmp);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Schreibt einen String auf das LCD&lt;br /&gt;
&lt;br /&gt;
void lcd_string(char *data)&lt;br /&gt;
{&lt;br /&gt;
    while (*data)&lt;br /&gt;
    {&lt;br /&gt;
        lcd_data(*data);&lt;br /&gt;
        data++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Includedatei &#039;&#039;&#039;lcd_routines.h&#039;&#039;&#039; enthält die Leitungszuordnung für die LCD-Ansteuerung und die Prototypen für die LCD Funktionen.&lt;br /&gt;
&lt;br /&gt;
Hier wurden die Signalleitungen von PORTD auf PORTC geändert. Und die Definition von F_CPU wurde mit #if/#endif auskommentiert, weil dieser Wert bereits mit den &#039;&#039;Projekt Configuration Options&#039;&#039; im AVR Studio auf 12 MHz gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus&lt;br /&gt;
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial&lt;br /&gt;
//&lt;br /&gt;
void lcd_data(unsigned char temp1);&lt;br /&gt;
void lcd_string(char *data);&lt;br /&gt;
void lcd_command(unsigned char temp1);&lt;br /&gt;
void lcd_enable(void);&lt;br /&gt;
void lcd_init(void);&lt;br /&gt;
void lcd_home(void);&lt;br /&gt;
void lcd_clear(void);&lt;br /&gt;
void set_cursor(uint8_t x, uint8_t y);&lt;br /&gt;
&lt;br /&gt;
#if 0&lt;br /&gt;
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!&lt;br /&gt;
#define F_CPU 8000000&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
// LCD Befehle&lt;br /&gt;
&lt;br /&gt;
#define CLEAR_DISPLAY 0x01&lt;br /&gt;
#define CURSOR_HOME   0x02&lt;br /&gt;
&lt;br /&gt;
// Pinbelegung für das LCD, an verwendete Pins anpassen&lt;br /&gt;
&lt;br /&gt;
#if 1&lt;br /&gt;
// Änderung des LCD Anschlusses&lt;br /&gt;
// Pollin Funk AVR Evaluationsboard v1.1&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PC0 bis PC3 des AVR verbunden&lt;br /&gt;
#define LCD_PORT      PORTC&lt;br /&gt;
#define LCD_DDR       DDRC&lt;br /&gt;
#define LCD_EN        PC5&lt;br /&gt;
#define LCD_RS        PC4&lt;br /&gt;
#define LCD_DB7       PC3&lt;br /&gt;
#define LCD_DB6       PC2&lt;br /&gt;
#define LCD_DB5       PC1&lt;br /&gt;
#define LCD_DB4       PC0&lt;br /&gt;
#else&lt;br /&gt;
#define LCD_PORT      PORTD&lt;br /&gt;
#define LCD_DDR       DDRD&lt;br /&gt;
#define LCD_RS        PD4&lt;br /&gt;
#define LCD_EN        PD5&lt;br /&gt;
// DB4 bis DB7 des LCD sind mit PD0 bis PD3 des AVR verbunden&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Includedatei &#039;&#039;&#039;funkhw.h&#039;&#039;&#039; enthält die gemeinsamen, boardspezifischen Port-Definitionen, die in mehreren Unterdateien benötigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
    Atmega8&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
// LEDs sind high-active geschaltet&lt;br /&gt;
#define LED_AN(LED)     (PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)    (PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED) (PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1            PD6&lt;br /&gt;
#define LED2            PD5&lt;br /&gt;
&lt;br /&gt;
// TASTER ist high-active geschaltet&lt;br /&gt;
#define TASTER             PB1&lt;br /&gt;
#define TASTER_GEDRUECKT() (PINB &amp;amp; (1&amp;lt;&amp;lt;TASTER))&lt;br /&gt;
#define TASTE_AUF          0&lt;br /&gt;
#define TASTE_ZU           1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Includedatei &#039;&#039;&#039;rs232.h&#039;&#039;&#039; enthält die Prototypen für die Funktionen in rs232.c: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef FORMFEED&lt;br /&gt;
#define FORMFEED &#039;\014&#039;&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
void UART_init(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void);&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z);&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s);&lt;br /&gt;
&lt;br /&gt;
void rs232(uint8_t *wert);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und zum Schluss noch die Datei &#039;&#039;&#039;rs232.c&#039;&#039;&#039; mit den ungeänderten UART-Grundfunktionen und der rs232-Funktion. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;rs232.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define EINGABE_START 	0&lt;br /&gt;
#define EINGABE_ABBRUCH EINGABE_START&lt;br /&gt;
#define EINGABE_MAX 		3&lt;br /&gt;
&lt;br /&gt;
void UART_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Bausrate setzen&lt;br /&gt;
    // 9600 Baud bei F_CPU 12 MHz&lt;br /&gt;
    // (Baudratenfehler = +0,2%)&lt;br /&gt;
    UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) &amp;gt;&amp;gt; 8);&lt;br /&gt;
    UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);&lt;br /&gt;
&lt;br /&gt;
    //      Empfangen,   Senden erlauben&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL) | ((1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0)) | ((0&amp;lt;&amp;lt;UPM1) | (0&amp;lt;&amp;lt;UPM0)) | (0&amp;lt;&amp;lt;USBS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // sofort zurückkehren und Zustand melden&lt;br /&gt;
    return UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Eingang voll&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Ausgang frei&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)) )&lt;br /&gt;
        ;&lt;br /&gt;
    UDR = z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s)&lt;br /&gt;
{&lt;br /&gt;
    while ( *s )&lt;br /&gt;
    {&lt;br /&gt;
        UART_putchar(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void rs232(uint8_t *wert)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    uint8_t anzahl_ziffern;&lt;br /&gt;
    uint8_t zeichen;&lt;br /&gt;
    char puffer[23];&lt;br /&gt;
&lt;br /&gt;
    // Eingabe über RS232&lt;br /&gt;
&lt;br /&gt;
    // Ohne Warten prüfen, ob Zeichen an UART vorhanden&lt;br /&gt;
    if ( UART_peekchar() )&lt;br /&gt;
    {&lt;br /&gt;
        // Zeichen ist da.&lt;br /&gt;
&lt;br /&gt;
        // Aktuellen Wert ausgeben&lt;br /&gt;
&lt;br /&gt;
        // Neue Seite im Terminal anfordern.&lt;br /&gt;
        // Bewirkt ein Löschen des Bildschirms&lt;br /&gt;
&lt;br /&gt;
        UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
        // Einleitender Text, was im folgenden ausgegeben wird&lt;br /&gt;
        UART_puts(&amp;quot;Alter Z\204hler = &amp;quot;); // \204 ist ä in Oktalschreibweise&lt;br /&gt;
&lt;br /&gt;
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
        itoa((int) *wert, puffer, 10);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Dann die gleiche Zahl in Hexadezimalziffern&lt;br /&gt;
        UART_puts(&amp;quot; 0x&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 16);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Dann die gleiche Zahl in Binärziffern&lt;br /&gt;
        UART_puts(&amp;quot; 0b&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 2);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
        // Schliesslich noch der Zustand der LEDs auf dem Board senden&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 2) ? &amp;quot; LED2-An &amp;quot; : &amp;quot; LED2-Aus&amp;quot;);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 1) ? &amp;quot; LED1-An&amp;quot; : &amp;quot; LED1-Aus&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Zeilenabschluss zur Formatierung der Anzeige&lt;br /&gt;
        UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Neuen Wert eingeben&lt;br /&gt;
&lt;br /&gt;
        // Initialisierung&lt;br /&gt;
        UART_puts(&amp;quot;Eingabe&amp;gt; &amp;quot;);&lt;br /&gt;
        anzahl_ziffern = EINGABE_START;&lt;br /&gt;
&lt;br /&gt;
        // Eingabeschleife&lt;br /&gt;
        do&lt;br /&gt;
        {&lt;br /&gt;
            // 1. bereits Zeichen am UART Eingang abholen&lt;br /&gt;
            // in weiteren Schleifen auf neue Zeichen warten&lt;br /&gt;
            zeichen = UART_getchar();&lt;br /&gt;
&lt;br /&gt;
            // Zeichen auswerten&lt;br /&gt;
            if ( (zeichen == &#039;-&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // MINUS&lt;br /&gt;
                UART_puts(&amp;quot; -1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert -= 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;+&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // PLUS&lt;br /&gt;
                UART_puts(&amp;quot; +1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert += 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;\n&#039;) || (zeichen == &#039;\r&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // ENTER oder RETURN: Abschluss der Eingabe&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen &amp;gt;= &#039;0&#039;) &amp;amp;&amp;amp; (zeichen &amp;lt;= &#039;9&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // Eingabe einer Ziffer&lt;br /&gt;
                UART_putchar(zeichen); // Echo&lt;br /&gt;
                puffer[anzahl_ziffern++] = zeichen;&lt;br /&gt;
                puffer[anzahl_ziffern] = 0;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                // Sonstiges Zeichen ist für uns illegal&lt;br /&gt;
                anzahl_ziffern = EINGABE_ABBRUCH;&lt;br /&gt;
                UART_puts(&amp;quot; Abbruch.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while ( anzahl_ziffern &amp;lt; EINGABE_MAX );&lt;br /&gt;
&lt;br /&gt;
        // Eingabe prüfen&lt;br /&gt;
        if ( anzahl_ziffern != EINGABE_ABBRUCH )&lt;br /&gt;
        {&lt;br /&gt;
            // Ziffern in Zahl umwandeln&lt;br /&gt;
            int i = atoi(puffer);&lt;br /&gt;
&lt;br /&gt;
            // z.&amp;amp;nbsp;B. erlaubter Wertebereich sei 0 bis 255&lt;br /&gt;
            if ( (i &amp;gt;= 0) &amp;amp;&amp;amp; (i &amp;lt; 256) )&lt;br /&gt;
            {&lt;br /&gt;
                UART_puts(&amp;quot; Ok.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert = (uint8_t) i;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
                UART_puts(&amp;quot; Ung\201ltig.\r\n&amp;quot;); // \201 ist ü in Oktalschreibweise&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe auf RS232 (s.oben)&lt;br /&gt;
    // bei nur Änderung des Wertes gegenüber der letzten Ausgabe&lt;br /&gt;
    //&lt;br /&gt;
    // alter_wert wurde mit -1 initialisiert. Das ist ausserhalb&lt;br /&gt;
    // des Wertebereichs von *wert, D.h. spätere Abfrage, ob&lt;br /&gt;
    // (alter_wert != *wert) ist, ist beim ersten Durchlauf wie&lt;br /&gt;
    // gewünscht wahr,&lt;br /&gt;
    //&lt;br /&gt;
    if (alter_wert != *wert)&lt;br /&gt;
    {&lt;br /&gt;
        alter_wert = *wert;&lt;br /&gt;
        UART_puts(&amp;quot;Z\204hler = &amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 10);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts(&amp;quot; 0x&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 16);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts(&amp;quot; 0b&amp;quot;);&lt;br /&gt;
        itoa((int) *wert, puffer, 2);&lt;br /&gt;
        UART_puts(puffer);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 2) ? &amp;quot; LED2-An &amp;quot; : &amp;quot; LED2-Aus&amp;quot;);&lt;br /&gt;
        UART_puts( (*wert &amp;amp; 1) ? &amp;quot; LED1-An&amp;quot; : &amp;quot; LED1-Aus&amp;quot;);&lt;br /&gt;
        UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Uhr ===&lt;br /&gt;
&lt;br /&gt;
Nachdem die Eingabe- und Ausgaberoutinen vorhanden sind, sollen jetzt die weiteren Innereien des ATmega8 erkundet werden. Als erstes kommt der Timer und damit die erste Interrupt-Programmierung an die Reihe. Und was liegt bei dem Thema näher, als eine Uhr zu programmieren...&lt;br /&gt;
&lt;br /&gt;
Zunächst der Aufbau des Projektes:&lt;br /&gt;
&lt;br /&gt;
* PFA_uhr.c - Das Hauptprogramm&lt;br /&gt;
* PFA_funkhw.h - Definitionen für das PFA ;-) Board&lt;br /&gt;
* PFA_rs232.c - Erweiterte rs232-Funktionen&lt;br /&gt;
* PFA_rs232.h - Prototypen zu den rs232-Funktionen&lt;br /&gt;
* PFA_uart.c - UART Grundfunktionen (aus ehemaligem rs232.c herausgezogen)&lt;br /&gt;
* PFA_uart.h - Prototypen zu den UART Grundfunktionen&lt;br /&gt;
* lcd-routines.c - Unverändert s.o.&lt;br /&gt;
* lcd-routines.h - Unverändert s.o.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_uhr.c&#039;&#039;&#039; - Das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
Drei Funktionen werden für die Uhr benötigt. Selbstverständlich eine Funktion zum Anzeigen der Uhrzeit auf dem LCD (Bsp.: uhrzeit_lcd). Und eine Funktion zum Starten der Uhr (Bsp.: uhr_init). &lt;br /&gt;
&lt;br /&gt;
Sowie eine TickTack-Funktion, die regelmässig die Uhrzeit erhöht und zwar egal was das Programm sonst macht (Tasteneingabe, RS232 Ausgabe...)! Diese letzte Funktion heisst ISR(TIMER0_OVF_vect) und ist eine sog. Interrupt Service Routine für den TIMER0 OVerFlow Interrupt. Die Namen solcher ISR Funktionen sind nicht frei wählbar, sondern den einzelnen Interrupts fest zugeordnet. Wie die ISRs heissen, kann man in der Dokumentation zum jeweiligen AVR nachlesen.&lt;br /&gt;
&lt;br /&gt;
Die Auswahl von TIMER0 ist willkürlich. Der ATmega8 hat insgesamt drei Timer, die unterschiedliche Fähigkeiten haben. TIMER0 ist davon der mit der geringsten Ausstattung, aber selbst die ist noch ausreichend, um damit eine Uhr zu programmieren. So können die anderen Timer für anderes freigehalten werden.&lt;br /&gt;
&lt;br /&gt;
TIMER0 ist ein 8-Bit Timer, d.h. er kann so einstellt werden, dass er ab einem Startwert TCNT0 jedesmal um eins hochzählt, wenn er von seiner Taktquelle angetrieben wird. Die Taktquelle für den TIMER0 kann die Taktquelle des AVR sein oder ein durch den sog. Vorteiler (Prescaler) von 8, 64, 256 oder 1024 geteilte CPU-Takt. Welcher Vorteiler verwendet werden soll, wird im Register TCCR0 eingestellt.&lt;br /&gt;
&lt;br /&gt;
Wenn der Endwert 255 (8-Bit!) überschritten wird und die Hochzählerei wieder bei 0 beginnt, kann eine Überlaufunterbrechung (Overflow Interrupt) ausgelöst werden, wenn das vom Programm aus im Register TIMSK so eingerichtet wurde.&lt;br /&gt;
&lt;br /&gt;
Damit beim ersten Einschalten des Timers nicht sofort ein Interrupt ausgelöst wird, löscht man meistens vor dem Einschalten das betreffende sog. Interruptflag (hier im Register TIFR). Das Interruptflag ist innerhalb der CPU dafür entscheidend, ob beim nächsten abzuarbeitenden Maschinenbefehl das Hauptprogramm ausgeführt wird oder ob in die ISR verzweigt wird. &lt;br /&gt;
&lt;br /&gt;
Das Einschalten selbst ist ein zweistufiger Prozess: zunächst stellt man in dem Register TIMSK ein, welchen Interrupt man erlauben will. Anschliessend werden mit der Funktion &#039;&#039;&#039;sei&#039;&#039;&#039;, alle (d.h. global) erlaubten Interrupts auch von der CPU abgeprüft. Die Gegenfunktion zu sei, d.h. Interrupts global sperren wäre die Funktion &#039;&#039;&#039;cli&#039;&#039;&#039;, aber die wird in diesem Beispiel nicht benötigt.&lt;br /&gt;
&lt;br /&gt;
Jetzt geht&#039;s ans Rechnen, wie der TIMER0 genau eingestellt werden muss, damit man damit auch eine Uhr programmieren kann.&lt;br /&gt;
&lt;br /&gt;
In der folgenden Tabelle ist abhängig vom Prescaler P (Spalte 1) berechnet, welchen Takt T man erhält, wenn die F_CPU 12 MHz beträgt (zweite Spalte). In der dritten Spalte D1 ist berechnet, wie lange es dauert, wenn der Timer um eins hochzählt. Bzw. in der vierten Spalte bei D2, wenn nach 256 Zählschritten (0 bis 255 dann +1 gibt Überlauf zu 0) der Overflow Interrupt auftritt. In der fünften Spalte N256 ist berechnet, wieviele Overflow-Interrupts man mitzählen müsste, um auf 1 Sekunde zu kommen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;timer0-12mhz-1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|P = Prescaler&lt;br /&gt;
|T = Taktrate [Hz]&lt;br /&gt;
|1.000.000/(T/P) = D1 = Dauer für +1 [µs]&lt;br /&gt;
|(1.000/(T/P))*256 = D2 = Dauer bis Overflow [ms]&lt;br /&gt;
|1000/((1.000/(T/P))*256) = N256 = Anzahl Overflows für 1 s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|12.000.000&lt;br /&gt;
|0,083&lt;br /&gt;
|0,021&lt;br /&gt;
|46875&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|1.500.000&lt;br /&gt;
|0,667&lt;br /&gt;
|0,171&lt;br /&gt;
|5859,38&lt;br /&gt;
|-&lt;br /&gt;
|64&lt;br /&gt;
|187.500&lt;br /&gt;
|5,333&lt;br /&gt;
|0,171&lt;br /&gt;
|732,42&lt;br /&gt;
|-&lt;br /&gt;
|256&lt;br /&gt;
|46.875&lt;br /&gt;
|21,333&lt;br /&gt;
|8,333&lt;br /&gt;
|183,11&lt;br /&gt;
|-&lt;br /&gt;
|1024&lt;br /&gt;
|11.718,75&lt;br /&gt;
|85,333&lt;br /&gt;
|21,845&lt;br /&gt;
|45,78&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In diesem Fall wird nur beim Prescaler 1 eine ganze Zahl von Overflow-Interrupts durchlaufen, so dass man nach einer ganzen Zahl von Zählschritten glatt auf 1 Sekunde kommt. Aber wenn man mit diesem Prescaler 1 arbeitet, treten die Interrupts zigtausend Mal auf, so dass das Programm nur noch am sehr schnellen Hochzählen und Aufrufen des Interrupts ist... das ist keine Lösung. &lt;br /&gt;
&lt;br /&gt;
Aber es gibt zwei Auswege für die anderen Prescaler-Werte. Denn man kann ja vorgeben, ab welchem Startwert in Richtung 255 hochgezählt wird und damit kann man festlegen, wieviele Schritte bis zum Overflow vergehen. Vielleicht kann man die Anzahl so geschickt wählen, dass eine ganze Zahl von Overflow Interrupts glatt 1 Sekunde ergibt? Gesucht ist also eine Zahl X, bei der gilt, dass N1/X eine ganze Zahl ergibt. Klar X=1 ist fast immer eine Lösung, nur werden die Zahlen und damit der Zeitverbrauch in der Interruptroutine gigantisch...&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;timer0-12mhz-2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|P = Prescaler&lt;br /&gt;
|T/P = N1 = Anzahl +1 Schritte für 1 s&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|12.000.000&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|1.500.000&lt;br /&gt;
|-&lt;br /&gt;
|64&lt;br /&gt;
|187.500&lt;br /&gt;
|-&lt;br /&gt;
|256&lt;br /&gt;
|46.875&lt;br /&gt;
|-&lt;br /&gt;
|1024&lt;br /&gt;
|11.718,75&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Spielt man es für die interessanten niedrigen Taktraten  durch, kann man beim Prescaler 1024 leider keine Zahl X finden, bei der 11.718,75/X ganzzahlig ist. Aber beim Prescaler 256 gibt es mehrere Lösungen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;timer0-12mhz-3&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|X&lt;br /&gt;
|46875/X = &lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|46875&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|15625&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|9375&lt;br /&gt;
|-&lt;br /&gt;
|15&lt;br /&gt;
|3125&lt;br /&gt;
|-&lt;br /&gt;
|25&lt;br /&gt;
|1875&lt;br /&gt;
|-&lt;br /&gt;
|75&lt;br /&gt;
|625&lt;br /&gt;
|-&lt;br /&gt;
|125&lt;br /&gt;
|375&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wählt man also den Startwert so, dass der TIMER0 125 mal hochzählen muss, bis ein Overflow auftritt, hat man nach 375 Overflows genau eine Sekunde lang abgezählt. Um das Einzurichten muss also der TCNT0 Startwert nicht auf 0 (= 256 = 256-0 Schritte bis Overflow) gesetzt werden, sondern auf 256-125 (= -125 bei 8-Bit Rechnung, da dann 256 = 0 gilt). Und der Startwert muss bei jedem Overflow Interrupt wieder auf den berechneten Startwert gesetzt werden. Das kann man durch geschickte Wahl der Timer-Einstellung automatisch machen lassen und erhält das genauste Verfahren.&lt;br /&gt;
&lt;br /&gt;
Aber es gibt noch eine andere (möglicherweise ungenauere) Lösung und die wird im folgenden Programm benutzt: &lt;br /&gt;
&lt;br /&gt;
Man macht so viele 256 Schritt-Overflows, wie es geht, ohne die Sekunde zu überschreiten und für den letzten Rest verringert man die Anzahl der Schritte bis zum Overflow.&lt;br /&gt;
&lt;br /&gt;
Beim Prescaler 256 und F_CPU 12 MHz würden sich in einer Sekunde &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\frac{12\,000\,000}{256 \cdot 256} = 183{,}10546875&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
256-Schritt Overflow Interrupts ergeben. Das kann man in 183 volle 256-Schritt Overflows plus 0,10546875 * 256 = 27 Stück 1-Schritt-Overflows bzw. ein 27-Schritt Overflow aufteilen. Insgesamt also 184 Overflow-Interrupts. Im 183. Interrupt muss der Startwert geändert werden und im 184. Interrupt kann der Sekundenzähler erhöht werden.&lt;br /&gt;
&lt;br /&gt;
Die Ungenauigkeit kann in das Programm, weil der Timer ja weiterläuft, während sich das Programm in der ISR befindet. Wenn dann der Startwert vom Programm aus gesetzt wird, berücksichtigt das nicht den Wert der schon bis dahin gezählt ist. Mit dem Makro GENAUIGKEIT kann man in der Richtung etwas experimentieren...&lt;br /&gt;
&lt;br /&gt;
Genug Zahlenspielerei. Der Rest des Programmes ist wieder anschaulicher. Die bereits bekannte Funktion rs232 wurde in die Funktion rs232_io geändert, so dass die Eingabe von Stunden, Minuten und Sekunden möglich ist. Was per RS232 eingegeben werden soll, kann mit dem TASTER1 bestimmt werden: 1. Tastendruck Sekunden, 2. Tastendruck Minuten, 3. Tastendruck Stunden. 4. Tastendruck Anzeige Uhrzeit auf RS232. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_uhr.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_rs232.h&amp;quot;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* in Millisekunden */&lt;br /&gt;
#define ENTPRELLUNG        10&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t stunden;&lt;br /&gt;
volatile uint8_t minuten;&lt;br /&gt;
volatile uint8_t sekunden;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// 0: 183x256 + 1x27&lt;br /&gt;
// 1: 183x256 + 1x27 durchlaufendes TCNT0 berücksichtigt&lt;br /&gt;
// 2: 325x125&lt;br /&gt;
//&lt;br /&gt;
#define GENAUIGKEIT 0&lt;br /&gt;
&lt;br /&gt;
#if ( GENAUIGKEIT == 1 )&lt;br /&gt;
volatile uint8_t tcnt0;&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#if ( GENAUIGKEIT == 2 )&lt;br /&gt;
#define RELOAD 	125&lt;br /&gt;
#define COUNT 	375&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect)&lt;br /&gt;
{&lt;br /&gt;
#if ( GENAUIGKEIT == 2 )&lt;br /&gt;
    static uint16_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    TCNT0 = 256 - RELOAD;&lt;br /&gt;
&lt;br /&gt;
    ticks += 1;&lt;br /&gt;
&lt;br /&gt;
    if ( ticks == COUNT )&lt;br /&gt;
    {&lt;br /&gt;
        // 1 Sekunde ist um.&lt;br /&gt;
        sekunden += 1;&lt;br /&gt;
&lt;br /&gt;
        if ( sekunden == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( minuten == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( stunden == 24 )&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
&lt;br /&gt;
        // nächste Runde&lt;br /&gt;
        ticks = 0;&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    static uint8_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    ticks += 1;&lt;br /&gt;
&lt;br /&gt;
    if ( ticks == 183 )&lt;br /&gt;
    {&lt;br /&gt;
        // Letzter Zeitabschnitt kürzer, um auf 1s zu kommen&lt;br /&gt;
#if ( GENAUIGKEIT == 1 )&lt;br /&gt;
        tcnt0 = TCNT0;&lt;br /&gt;
        TCNT0 = 256 - 27 + TCNT0;&lt;br /&gt;
#else&lt;br /&gt;
        TCNT0 = 256 - 27;&lt;br /&gt;
#endif&lt;br /&gt;
    }&lt;br /&gt;
    else if ( ticks == 184 )&lt;br /&gt;
    {&lt;br /&gt;
        // 1 Sekunde ist um.&lt;br /&gt;
        sekunden += 1;&lt;br /&gt;
&lt;br /&gt;
        if ( sekunden == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( minuten == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( stunden == 24 )&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
&lt;br /&gt;
        // nächste Runde&lt;br /&gt;
        ticks = 0;&lt;br /&gt;
    }&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void uhr_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Rrescaler 256&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;CS02) | (0&amp;lt;&amp;lt;CS01) | (0&amp;lt;&amp;lt;CS00);&lt;br /&gt;
&lt;br /&gt;
    // Zählregister&lt;br /&gt;
#if ( GENAUIGKEIT == 2 )&lt;br /&gt;
    TCNT0 = 256 - RELOAD;&lt;br /&gt;
#else&lt;br /&gt;
    TCNT0 = 0;&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
    // Overflow-Flag löschen&lt;br /&gt;
    TIFR = (1&amp;lt;&amp;lt;TOV0);&lt;br /&gt;
&lt;br /&gt;
    // Timer0 Overflow enable&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global einschalten&lt;br /&gt;
    sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void uhrzeit_lcd(void)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    char puffer[4]; // max. 3 Ziffern (uint8_t) plus 1 Nullbyte&lt;br /&gt;
&lt;br /&gt;
    if ( sekunden != alter_wert )&lt;br /&gt;
    {&lt;br /&gt;
        // Ausgabe nur bei Änderungen&lt;br /&gt;
        alter_wert = sekunden;&lt;br /&gt;
&lt;br /&gt;
        lcd_clear();&lt;br /&gt;
&lt;br /&gt;
        itoa((int) stunden+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        lcd_string(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        itoa((int) minuten+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        lcd_string(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        itoa((int) sekunden+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ausgabe_led(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
        LED_AN(LED1);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
        LED_AN(LED2);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
    uint8_t zaehler = 0;&lt;br /&gt;
&lt;br /&gt;
    DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);           // Port B: Eingang für Taster&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);  // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
    UART_init();&lt;br /&gt;
    UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    uhr_init();&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Bekannte Uhrzeit auf LCD ausgeben&lt;br /&gt;
        uhrzeit_lcd();&lt;br /&gt;
&lt;br /&gt;
        // ggf. Kommando von TASTER1 einlesen&lt;br /&gt;
        if ( TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
            {&lt;br /&gt;
                // Wechsel von OFFEN nach GESCHLOSSEN?&lt;br /&gt;
                if ( alter_tastenzustand == TASTE_AUF )&lt;br /&gt;
                {&lt;br /&gt;
                    zaehler++;&lt;br /&gt;
                    alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
                alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Feedback TASTER1 auf LEDs&lt;br /&gt;
        ausgabe_led(zaehler); // LED Ausgabe&lt;br /&gt;
&lt;br /&gt;
        // Je nach TASTER1-Eingabe Uhrzeit per RS232 ändern oder anzeigen&lt;br /&gt;
        switch (zaehler % 4)&lt;br /&gt;
        {&lt;br /&gt;
        case 3:&lt;br /&gt;
            rs232_io(&amp;quot;Stunden = &amp;quot;, &amp;amp;stunden, 0, 23);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 2:&lt;br /&gt;
            rs232_io(&amp;quot;Minuten = &amp;quot;, &amp;amp;minuten, 0, 59);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 1:&lt;br /&gt;
            rs232_io(&amp;quot;Sekunden = &amp;quot;, &amp;amp;sekunden, 0, 59);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 0:&lt;br /&gt;
        default:&lt;br /&gt;
#if ( GENAUIGKEIT == 1 )&lt;br /&gt;
            if ( tcnt0 != 0 )&lt;br /&gt;
            {&lt;br /&gt;
                char puffer[4];&lt;br /&gt;
                UART_puts(&amp;quot;TCNT0 = &amp;quot;);&lt;br /&gt;
                itoa((int) tcnt0, puffer, 10);&lt;br /&gt;
                UART_puts(puffer);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
#endif&lt;br /&gt;
            rs232_uhrzeit_zeigen(stunden, minuten, sekunden);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_uhr.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_funkhw.h&#039;&#039;&#039; - Definitionen für das PFA ;-) Board&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_funkhw.h&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
// LEDs sind high-active geschaltet&lt;br /&gt;
#define LED_AN(LED)     (PORTD |=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_AUS(LED)    (PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED_TOGGLE(LED) (PORTD ^=  (1&amp;lt;&amp;lt;(LED)))&lt;br /&gt;
#define LED1            PD6&lt;br /&gt;
#define LED2            PD5&lt;br /&gt;
&lt;br /&gt;
// TASTER ist high-active geschaltet&lt;br /&gt;
#define TASTER             PB1&lt;br /&gt;
#define TASTER_GEDRUECKT() (PINB &amp;amp; (1&amp;lt;&amp;lt;TASTER))&lt;br /&gt;
#define TASTE_AUF          0&lt;br /&gt;
#define TASTE_ZU           1&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_funkhw.h&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_rs232.c&#039;&#039;&#039; - Erweiterte rs232-Funktionen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_rs232.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_rs232.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void wert_ausgeben(char *s, uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    char puffer[4]; // uint8_t hat max. 3 Dezimalstellen plus 1 Nullbyte = 4 Bytes&lt;br /&gt;
&lt;br /&gt;
    // Einleitender Text, was im folgenden ausgegeben wird&lt;br /&gt;
    UART_puts(s);&lt;br /&gt;
&lt;br /&gt;
    // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
    itoa((int) wert, puffer, 10);&lt;br /&gt;
    UART_puts(puffer);&lt;br /&gt;
&lt;br /&gt;
    // Zeilenabschluss zur Formatierung der Anzeige&lt;br /&gt;
    UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void rs232_io(char *s, uint8_t *wert, uint8_t min, uint8_t max)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    uint8_t anzahl_ziffern;&lt;br /&gt;
    uint8_t zeichen;&lt;br /&gt;
    char puffer[EINGABE_MAX];&lt;br /&gt;
&lt;br /&gt;
    // Eingabe über RS232&lt;br /&gt;
&lt;br /&gt;
    // Ohne Warten prüfen, ob Zeichen an UART vorhanden&lt;br /&gt;
    if ( UART_peekchar() )&lt;br /&gt;
    {&lt;br /&gt;
        // Zeichen ist da.&lt;br /&gt;
&lt;br /&gt;
        // Aktuellen Wert ausgeben&lt;br /&gt;
&lt;br /&gt;
        // Neue Seite im Terminal anfordern.&lt;br /&gt;
        // Bewirkt ein Löschen des Bildschirms&lt;br /&gt;
        UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
        wert_ausgeben(s, *wert);&lt;br /&gt;
&lt;br /&gt;
        // Neuen Wert eingeben&lt;br /&gt;
&lt;br /&gt;
        // Initialisierung&lt;br /&gt;
        UART_puts(&amp;quot;Eingabe&amp;gt; &amp;quot;);&lt;br /&gt;
        anzahl_ziffern = EINGABE_START;&lt;br /&gt;
&lt;br /&gt;
        // Eingabeschleife&lt;br /&gt;
        do&lt;br /&gt;
        {&lt;br /&gt;
            // 1. bereits Zeichen am UART Eingang abholen&lt;br /&gt;
            // in weiteren Schleifen auf neue Zeichen warten&lt;br /&gt;
            zeichen = UART_getchar();&lt;br /&gt;
&lt;br /&gt;
            // Zeichen auswerten&lt;br /&gt;
            if ( (zeichen == &#039;-&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // MINUS&lt;br /&gt;
                UART_puts(&amp;quot; -1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                if (*wert &amp;gt; min)&lt;br /&gt;
                    *wert -= 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;+&#039;) &amp;amp;&amp;amp; (anzahl_ziffern == EINGABE_START) )&lt;br /&gt;
            {&lt;br /&gt;
                // PLUS&lt;br /&gt;
                UART_puts(&amp;quot; +1&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                if (*wert &amp;lt; max)&lt;br /&gt;
                    *wert += 1;&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen == &#039;\n&#039;) || (zeichen == &#039;\r&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // ENTER oder RETURN: Abschluss der Eingabe&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
            else if ( (zeichen &amp;gt;= &#039;0&#039;) &amp;amp;&amp;amp; (zeichen &amp;lt;= &#039;9&#039;) )&lt;br /&gt;
            {&lt;br /&gt;
                // Eingabe einer Ziffer&lt;br /&gt;
                UART_putchar(zeichen); // Echo&lt;br /&gt;
                puffer[anzahl_ziffern++] = zeichen;&lt;br /&gt;
                puffer[anzahl_ziffern] = 0;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                // Sonstiges Zeichen ist für uns illegal&lt;br /&gt;
                anzahl_ziffern = EINGABE_ABBRUCH;&lt;br /&gt;
                UART_puts(&amp;quot; Abbruch.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        while ( anzahl_ziffern &amp;lt; EINGABE_MAX );&lt;br /&gt;
&lt;br /&gt;
        // Eingabe prüfen&lt;br /&gt;
        if ( anzahl_ziffern != EINGABE_ABBRUCH )&lt;br /&gt;
        {&lt;br /&gt;
            // Ziffern in Zahl umwandeln&lt;br /&gt;
            int i = atoi(puffer);&lt;br /&gt;
&lt;br /&gt;
            // z.&amp;amp;nbsp;B. erlaubter Wertebereich&lt;br /&gt;
            if ( (i &amp;gt;= min) &amp;amp;&amp;amp; (i &amp;lt;= max) )&lt;br /&gt;
            {&lt;br /&gt;
                UART_puts(&amp;quot; Ok.&amp;quot;);&lt;br /&gt;
                UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
                *wert = (uint8_t) i;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
                UART_puts(&amp;quot; Ung\201ltig.\r\n&amp;quot;); // \201 ist ü in Oktalschreibweise&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Ausgabe auf RS232 (s.oben)&lt;br /&gt;
    // bei nur Änderung des Wertes gegenüber der letzten Ausgabe&lt;br /&gt;
    //&lt;br /&gt;
    // alter_wert wurde mit -1 initialisiert. Das ist ausserhalb&lt;br /&gt;
    // des Wertebereichs von *wert, D.h. spätere Abfrage, ob&lt;br /&gt;
    // (alter_wert != *wert) ist, ist beim ersten Durchlauf wie&lt;br /&gt;
    // gewünscht wahr,&lt;br /&gt;
    //&lt;br /&gt;
    if (alter_wert != *wert)&lt;br /&gt;
    {&lt;br /&gt;
        alter_wert = *wert;&lt;br /&gt;
        wert_ausgeben(s, *wert);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void rs232_uhrzeit_zeigen(uint8_t stunden, uint8_t minuten, uint8_t sekunden)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alte_sekunden = -1;&lt;br /&gt;
    char puffer[23];&lt;br /&gt;
&lt;br /&gt;
    if ( alte_sekunden != sekunden )&lt;br /&gt;
    {&lt;br /&gt;
        // Neue Seite im Terminal anfordern.&lt;br /&gt;
        // Bewirkt ein Löschen des Bildschirms&lt;br /&gt;
        UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
        itoa((int) stunden+100, puffer, 10);&lt;br /&gt;
        UART_puts(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        UART_puts(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
        itoa((int) minuten+100, puffer, 10);&lt;br /&gt;
        UART_puts(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        UART_puts(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        // Zahl in Ziffernfolge umwandeln. Hier zuerst in Dezimalziffern&lt;br /&gt;
        itoa((int) sekunden+100, puffer, 10);&lt;br /&gt;
        UART_puts(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        // Zeilenabschluss zur Formatierung der Anzeige&lt;br /&gt;
        UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        alte_sekunden = sekunden;&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;
&#039;&#039;&#039;PFA_rs232.h&#039;&#039;&#039; - Prototypen zu den rs232-Funktionen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard&lt;br /&gt;
// PFA_uart.h&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&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;PFA_uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#ifndef FORMFEED&lt;br /&gt;
#define FORMFEED &#039;\014&#039;&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
#define EINGABE_START 	0&lt;br /&gt;
#define EINGABE_ABBRUCH EINGABE_START&lt;br /&gt;
#define EINGABE_MAX 		3&lt;br /&gt;
&lt;br /&gt;
void rs232_io(char * s, uint8_t *wert, uint8_t min, uint8_t max);&lt;br /&gt;
void rs232_uhrzeit_zeigen(uint8_t stunden, uint8_t minuten, uint8_t sekunden);&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_uart.h&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_uart.c&#039;&#039;&#039; - UART Grundfunktionen (aus ehemaligem rs232.c herausgezogen)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_uart.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_uart.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
void UART_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Bausrate setzen&lt;br /&gt;
    // 9600 Baud bei F_CPU 12 MHz&lt;br /&gt;
    // (Baudratenfehler = +0,2%)&lt;br /&gt;
    UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) &amp;gt;&amp;gt; 8);&lt;br /&gt;
    UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);&lt;br /&gt;
&lt;br /&gt;
    //      Empfangen,   Senden erlauben&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL) | ((1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0)) | ((0&amp;lt;&amp;lt;UPM1) | (0&amp;lt;&amp;lt;UPM0)) | (0&amp;lt;&amp;lt;USBS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // sofort zurückkehren und Zustand melden&lt;br /&gt;
    return UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Eingang voll&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Ausgang frei&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)) )&lt;br /&gt;
        ;&lt;br /&gt;
    UDR = z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s)&lt;br /&gt;
{&lt;br /&gt;
    while ( *s )&lt;br /&gt;
    {&lt;br /&gt;
        UART_putchar(*s);&lt;br /&gt;
        s++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_uart.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_uart.h&#039;&#039;&#039; - Prototypen zu den UART Grundfunktionen&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard&lt;br /&gt;
// PFA_uart.h&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void UART_init(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void);&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z);&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s);&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_uart.h&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Uhr mit 7-Segment-Anzeige ===&lt;br /&gt;
&lt;br /&gt;
Die Anzeige auf dem kleinen LCD ist nett, aber eine zusätzliche 7-Segment-Anzeige mit vier Ziffern für Stunden und Minuten wäre schöner!&lt;br /&gt;
&lt;br /&gt;
Aber inzwischen wird es schon knapp mit den freien Pins am ATmega8, weil das LCD am Aufbau installiert bleiben soll und dadurch Port C belegt ist. Daneben sind auch drei I/O-Pins durch Taster und die beiden LEDs.belegt und zwei Pins sind durch die UART weg... Aber zum Glück kann man den Bedarf an Pins durch verschieden Massnahmen senken:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA uhr 7segment foto.jpg|thumb|right|256px|Uhr mit 7-Segment-Anzeige (Pollin Funk-AVR-Evaluationsboard)]] &lt;br /&gt;
&lt;br /&gt;
# Ohne Zusatzmassnahmen bräuchte man 4 Ziffern * 7 Segmente. Gesamtbedarf: 28 Output-Pins ;-(&lt;br /&gt;
# Erste Reduzierungsstufe: 4 Steuerleitungen für die Stromzufuhr der 4 Ziffern und 7 Steuerleitungen für alle Ziffern gemeinsam. Die Stromzufuhr wird zyklisch jeweils einer Ziffer zugeordnet. Wenn die Ziffer an der Reihe ist, werden die Segmente mit den Segmentsteuerleitungen eingestellt. Gesamtbedarf: 4 + 7 = 11 Output-Pins&lt;br /&gt;
# Zweite Reduzierungsstufe: Die Segmentsteuerleitungen werden von einem &#039;&#039;&#039;BCD-nach-7Segment-Decoder&#039;&#039;&#039; angesprochen. Um eine BCD-Zahl an den Decoder zu geben werden am AVR 4 Steuerleitungen benötigt. Gesamtbedarf: 4 + 4 = 8 Output-Pins&lt;br /&gt;
# Dritte Reduzierungsstufe: Die Segmentsteuerleitungen werden von einem [[AVR-Tutorial:_Schieberegister|Schieberegister]] angesprochen. Um das Schieberegister zu füttern, werden am AVR 2-3 Steuerleitungen (je nach Art des Schieberegisters) benötigt. Gesamtbedarf: 4 + 2 bis 3 = 6 bis 7 Output-Pins&lt;br /&gt;
# Vierte Reduzierungsstufe: Die Segmentsteuerleitungen und die Versorgungssteuerleitungen werden von je einem Schieberegister bedient. Und es wird eine gemeinsame Clock-Steuerleitung verwendet. Gesamtbedarf: 2x 2 bis 3 minus 1 = 3 bis 5 Output-Pins&lt;br /&gt;
# Fünfte Reduzierungsstufe...: Nur 1 Pin zur Ansteuerung, geht das? Auch das geht, wenn z.&amp;amp;nbsp;B. die Ausgabe seriell zu einem weiteren &#039;&#039;&#039;Mikrocontroller&#039;&#039;&#039; geschickt wird, der dann eine eigene Displaysteuerung besitzt.&lt;br /&gt;
&lt;br /&gt;
Zufällig fand sich in meiner Bastelkiste ein BCD-nach-7Segment-Decoder nämlich der CA3161 von Intersil sowie vier grüne Siebensegmentanzeigen von einem früheren Drehzahlmesser-Projekt. Die sollen jetzt dem Recycling zugeführt werden. &lt;br /&gt;
&lt;br /&gt;
Die Verkabelung ist experimentell gestaltet. Als einfaches Steckbrett zum Anschluss an die 40-polige  Steckerleiste J4 wird ein 40-poliges IDE Kabel eines geplünderten Rechners benutzt. Von dem 40-poligen Kabel geht es dann über ein längeres 10-poliges Kabel (8 plus +5V und GND) zum Aufbaubrett mit der Zusatzschaltung. &lt;br /&gt;
&lt;br /&gt;
Achtung: Wenn ein solches IDE-Kabel in eigenen Schaltungen benutzt wird, vorher kontrollieren, ob die benötigten Pins 1:1 verbunden sind (Danke an Markus [http://www.mikrocontroller.net/topic/167829#1604209])&lt;br /&gt;
&lt;br /&gt;
Plan der Leitungszuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot; id=&amp;quot;timer0-12mhz-2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Funktion&lt;br /&gt;
| Bezeichnung am ATmega8&lt;br /&gt;
| Pin an ATmega8-16PI&lt;br /&gt;
| Pin an 40-pol. J4&lt;br /&gt;
| Leitung auf 10-pol. Verbindung&lt;br /&gt;
| Pin an CA3161&lt;br /&gt;
| Bezeichnung am CA3161&lt;br /&gt;
| Transistor-Nr.&lt;br /&gt;
|-&lt;br /&gt;
| Versorgung Minuten-Einer&lt;br /&gt;
| PB0&lt;br /&gt;
| 14&lt;br /&gt;
| 9&lt;br /&gt;
| 1&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| Q1&lt;br /&gt;
|-&lt;br /&gt;
| Versorgung Minuten-Zehner&lt;br /&gt;
| PD2&lt;br /&gt;
| 4&lt;br /&gt;
| 28&lt;br /&gt;
| 2&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| Q2&lt;br /&gt;
|-&lt;br /&gt;
| Versorgung Stunden-Einer&lt;br /&gt;
| PD3&lt;br /&gt;
| 5&lt;br /&gt;
| 29&lt;br /&gt;
| 3&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| Q3&lt;br /&gt;
|-&lt;br /&gt;
| Versorgung Stunden-Zehner&lt;br /&gt;
| PD4 (Anm. 1)&lt;br /&gt;
| 6&lt;br /&gt;
| 30&lt;br /&gt;
| 4&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| Q4&lt;br /&gt;
|-&lt;br /&gt;
| Segmente BCD 2&amp;lt;sup&amp;gt;0&amp;lt;/sup&amp;gt;&lt;br /&gt;
| PB2&lt;br /&gt;
| 16&lt;br /&gt;
| 13&lt;br /&gt;
| 5&lt;br /&gt;
| 7 &lt;br /&gt;
| 2&amp;lt;sup&amp;gt;0&amp;lt;/sup&amp;gt;&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Segmente BCD 2&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
| PB3&lt;br /&gt;
| 17&lt;br /&gt;
| 14&lt;br /&gt;
| 6&lt;br /&gt;
| 1&lt;br /&gt;
| 2&amp;lt;sup&amp;gt;1&amp;lt;/sup&amp;gt;&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Segmente BCD 2&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
| PB4&lt;br /&gt;
| 18&lt;br /&gt;
| 15&lt;br /&gt;
| 7&lt;br /&gt;
| 2&lt;br /&gt;
| 2&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Segmente BCD 2&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;&lt;br /&gt;
| PB5&lt;br /&gt;
| 19&lt;br /&gt;
| 16&lt;br /&gt;
| 8&lt;br /&gt;
| 6&lt;br /&gt;
| 2&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;&lt;br /&gt;
| - &lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA uhr 7segment SPlan.png|thumb|right|256px|Uhr mit 7-Segment-Anzeige (Pollin Funk-AVR-Evaluationsboard)]] &lt;br /&gt;
&lt;br /&gt;
Im nebenstehenden Bild ist der Versuch eines ersten Schaltplans zu sehen. Dabei wurde [[KiCAD]] verwendet. Bei der Erstellung des &amp;quot;Spezial-IC&amp;quot; CA3161 war der [http://kicad.rohrbacher.net/quicklib.php Quick KICAD Library Component Builder] sehr hilfreich. Achtung: Es ist nicht der komplette Aufbau von Spannungsversorgung und Beschaltung des ATmega8 zu sehen, lediglich die Bauteile und Verbindungen für dieses Beispiel sind eingezeichnet!&lt;br /&gt;
&lt;br /&gt;
Bei den 7-Segmentanzeigen handelt es sich um Anzeigen vom Typ Kingbright SA52-11GWA (&#039;&#039;common anode&#039;&#039; , 13 mm, grün). Die Versorgung jeweils einer Ziffer mit Strom erfolgt über einen geschalteten PNP-Transistor Typ BC558B mit 1,5 kOhm [[Basiswiderstand]]. Die Strombegrenzung für die Anzeige wird über je einen 220Ω [[LED#Vorwiderstand|Vorwiderstand]] pro Segmentleitung gemacht. Diese Dimensionierung ist noch nicht optimal, denn die Ziffern können ruhig noch etwas heller leuchten. Bei dieser Dimensionierung benötigt eine Ziffer im [[Multiplexen|Multiplexbetrieb]] ca. 3 mA Strom (Messung mit Multimeter zwischen Transistor und CA Anschluss). Im Dauerbetrieb ist das absolute Maximum Rating für diese Anzeige 25 mA. &lt;br /&gt;
&lt;br /&gt;
Anm. 1: Bei &#039;&#039;&#039;PD4&#039;&#039;&#039; heisst es aufpassen! Dieser Pin ist auch über einen 180Ω Vorwiderstand an den Optokoppler angeschlossen, um die Versorgungsspannung eines ggf, installierten RFM-Modus einzuschalten. Das Ganze wirkt dann als starker Pulldown-Widerstand, d.h. diese Leitung an diesem Pin wird im Reset-Zustand LOW gezogen und dadurch öffnet Q4 und lässt Ziffer 4 leuchten, wenn gleichzeitig mein ISP-Adapter (STK200-komp.) an J2 angeschlossen ist und dadurch PB3 (MOSI) und PB5 (SCK) auf LOW gezogen werden... Die anderen Versorgungspins sind im Reset im Tristate-Modus, d.h. die Ziffern 1 bis 3 sind aus, weil Q1 bis Q3 gesperrt sind. &lt;br /&gt;
&lt;br /&gt;
Das führt zu einer grundsätzlichen und wichtigen Überlegung in einem Projekt - Was macht die Periferie, wenn der Mikrocontroller bzw. das Programm darin keine Kontrolle hat? Beispielsweise beim Reset oder beim ISP-Programmieren oder beim Brownout... Im Hinblick auf diese Überlegungen ist die hier verwendete Kombination aus Schaltung und Programm unsicher!&lt;br /&gt;
&lt;br /&gt;
Der Programmcode ist wieder in mehrere Module gegliedert:&lt;br /&gt;
&lt;br /&gt;
* PFA_uhr_7segment.c - Das Hauptprogramm&lt;br /&gt;
* PFA_7segment.c - Die eigentliche Ansteuerung&lt;br /&gt;
* PFA_7segment.h - Die Prototypen für PFA_7segment.c&lt;br /&gt;
* sowie diese Unveränderten Dateien aus dem Uhr-Beispiel (s.o.): PFA_funkhw.h, PFA_rs232.c, PFA_rs232.h, PFA_uart.c, PFA_uart.h, lcd-routines.c, lcd-routines.h&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_uhr_7segment.c&#039;&#039;&#039; - Das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
Die Ansteuerung der neuen Anzeige (Bsp:led7segment_multiplex) ist im Timer0-Overflow-Interrupt eingeklinkt. Sie wird bei jedem (!) Timer-Overflow-Interrupt aufgerufen, d.h. sie darf nicht zulange arbeiten, damit keine weiteren Interrupts auflaufen. Der Timer wurde anderes als im Uhr-Beispiel eingerichtet, nämlich mit gleichlangen Overflow-Intervallen. Die Steuerdaten für die sieben Segmente pro Ziffer werden bei möglichen Änderungen, d.h. bei einem Sekundenwechsel, vorausberechnet und in einem Feld (Bsp: led7segment_ziffern) gespeichert. Die Initialisierung der neuen Anzeige erfolgt einmalig zu Beginn des Hauptprogramms (Bsp: led7segment_init).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_uhr_7segment.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
&lt;br /&gt;
    RS232-Einstellungen:&lt;br /&gt;
    9600 Baud, 8 Bits, No Parity, 1 Stopbit&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_rs232.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_7segment.h&amp;quot;&lt;br /&gt;
#include &amp;quot;lcd-routines.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* in Millisekunden */&lt;br /&gt;
#define ENTPRELLUNG        10&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t stunden;&lt;br /&gt;
volatile uint8_t minuten;&lt;br /&gt;
volatile uint8_t sekunden;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// 0: 183x256 + 1x27&lt;br /&gt;
// 1: 183x256 + 1x27 durchlaufendes TCNT0 berücksichtigt&lt;br /&gt;
// 2: 325x125&lt;br /&gt;
//&lt;br /&gt;
#define RELOAD 	125&lt;br /&gt;
#define COUNT 	375&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect)&lt;br /&gt;
{&lt;br /&gt;
    static uint16_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    TCNT0 = 256 - RELOAD + TCNT0;&lt;br /&gt;
&lt;br /&gt;
    led7segment_multiplex();&lt;br /&gt;
&lt;br /&gt;
    ticks += 1;&lt;br /&gt;
    if ( ticks == COUNT )&lt;br /&gt;
    {&lt;br /&gt;
        // 1 Sekunde ist um.&lt;br /&gt;
        sekunden += 1;&lt;br /&gt;
&lt;br /&gt;
        if ( sekunden == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( minuten == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( stunden == 24 )&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
&lt;br /&gt;
        // 1x möglichst viel vorberechnen, damit die Multiplex-Routine schnell ist&lt;br /&gt;
#if 1&lt;br /&gt;
        // Äktschn beim Entwickeln ;-)&lt;br /&gt;
        led7segment_ziffern[0] = (sekunden % 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[1] = (sekunden / 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[2] = (minuten % 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[3] = (minuten / 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
#else&lt;br /&gt;
        led7segment_ziffern[0] = (minuten % 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[1] = (minuten / 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[2] = (stunden % 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
        led7segment_ziffern[3] = (stunden / 10) &amp;lt;&amp;lt; 2;&lt;br /&gt;
#endif&lt;br /&gt;
        // nächste Runde&lt;br /&gt;
        ticks = 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void uhr_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Rrescaler 256&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;CS02) | (0&amp;lt;&amp;lt;CS01) | (0&amp;lt;&amp;lt;CS00);&lt;br /&gt;
&lt;br /&gt;
    // Zählregister&lt;br /&gt;
    TCNT0 = 0;&lt;br /&gt;
&lt;br /&gt;
    // Overflow-Flag löschen&lt;br /&gt;
    TIFR = (1&amp;lt;&amp;lt;TOV0);&lt;br /&gt;
&lt;br /&gt;
    // Timer0 Overflow enable&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global einschalten&lt;br /&gt;
    sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void uhrzeit_lcd(void)&lt;br /&gt;
{&lt;br /&gt;
    static int16_t alter_wert = -1;&lt;br /&gt;
    char puffer[4]; // max. 3 Ziffern (uint8_t) plus 1 Nullbyte&lt;br /&gt;
&lt;br /&gt;
    if ( sekunden != alter_wert )&lt;br /&gt;
    {&lt;br /&gt;
        // Ausgabe nur bei Änderungen&lt;br /&gt;
        alter_wert = sekunden;&lt;br /&gt;
&lt;br /&gt;
        lcd_clear();&lt;br /&gt;
&lt;br /&gt;
        itoa((int) stunden+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        lcd_string(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        itoa((int) minuten+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
&lt;br /&gt;
        lcd_string(&amp;quot; : &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        itoa((int) sekunden+100, puffer, 10);&lt;br /&gt;
        lcd_string(&amp;amp;puffer[1]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ausgabe_led(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
        LED_AN(LED1);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
        LED_AN(LED2);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
    uint8_t zaehler = 0;&lt;br /&gt;
&lt;br /&gt;
    DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);           // Port B: Eingang für Taster&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);  // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
    UART_init();&lt;br /&gt;
    UART_putchar(FORMFEED);&lt;br /&gt;
&lt;br /&gt;
    lcd_init();&lt;br /&gt;
&lt;br /&gt;
    led7segment_init();&lt;br /&gt;
&lt;br /&gt;
    uhr_init();&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        // Bekannte Uhrzeit auf LCD ausgeben&lt;br /&gt;
        uhrzeit_lcd();&lt;br /&gt;
&lt;br /&gt;
        // ggf. Kommando von TASTER1 einlesen&lt;br /&gt;
        if ( TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
            {&lt;br /&gt;
                // Wechsel von OFFEN nach GESCHLOSSEN?&lt;br /&gt;
                if ( alter_tastenzustand == TASTE_AUF )&lt;br /&gt;
                {&lt;br /&gt;
                    zaehler++;&lt;br /&gt;
                    alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
        {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
            _delay_ms(ENTPRELLUNG);&lt;br /&gt;
            if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
                alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Feedback TASTER1 auf LEDs&lt;br /&gt;
        ausgabe_led(zaehler); // LED Ausgabe&lt;br /&gt;
&lt;br /&gt;
        // Je nach TASTER1-Eingabe Uhrzeit per RS232 ändern oder anzeigen&lt;br /&gt;
        switch (zaehler % 4)&lt;br /&gt;
        {&lt;br /&gt;
        case 3:&lt;br /&gt;
            rs232_io(&amp;quot;Stunden = &amp;quot;, &amp;amp;stunden, 0, 23);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 2:&lt;br /&gt;
            rs232_io(&amp;quot;Minuten = &amp;quot;, &amp;amp;minuten, 0, 59);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 1:&lt;br /&gt;
            rs232_io(&amp;quot;Sekunden = &amp;quot;, &amp;amp;sekunden, 0, 59);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case 0:&lt;br /&gt;
        default:&lt;br /&gt;
            rs232_uhrzeit_zeigen(stunden, minuten, sekunden);&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_uhr_7segment.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_7segment.c&#039;&#039;&#039; - Die eigentliche Ansteuerung&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard&lt;br /&gt;
// PFA_7segment.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_7segment.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
uint8_t led7segment_ziffern[ANZAHL_ZIFFERN];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt;PB0) | (1&amp;lt;&amp;lt;PB2) | (1&amp;lt;&amp;lt;PB3) | (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3) | (1&amp;lt;&amp;lt;PD4);&lt;br /&gt;
&lt;br /&gt;
    // Alle Segmente aus&lt;br /&gt;
    PORTB |= ((1&amp;lt;&amp;lt;PB2) | (1&amp;lt;&amp;lt;PB3) | (1&amp;lt;&amp;lt;PB4) | (1&amp;lt;&amp;lt;PB5)); // BCD Code 0b1111 für &amp;quot;Blank&amp;quot; an Ausgabepins&lt;br /&gt;
&lt;br /&gt;
    // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTD |= ((1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3) | (1&amp;lt;&amp;lt;PD4));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void)&lt;br /&gt;
{&lt;br /&gt;
    static uint8_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTD |= ((1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3) | (1&amp;lt;&amp;lt;PD4));&lt;br /&gt;
&lt;br /&gt;
    // Segmente der neuen Ziffer berechnen&lt;br /&gt;
    PORTB = (PORTB &amp;amp; 0b11000011) | led7segment_ziffern[ticks];&lt;br /&gt;
    switch ( ticks )&lt;br /&gt;
    {&lt;br /&gt;
    case 0:&lt;br /&gt;
        PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB0);	// Spannung ein&lt;br /&gt;
        break;&lt;br /&gt;
    case 1:&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
        break;&lt;br /&gt;
    case 2:&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
        break;&lt;br /&gt;
    case 3:&lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD4);&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // auf nächste Ziffer weiterschalten&lt;br /&gt;
    ticks += 1;&lt;br /&gt;
    ticks %= ANZAHL_ZIFFERN;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_7segment.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_7segment.h&#039;&#039;&#039; - Die Prototypen für PFA_7segment.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_7segment.h&lt;br /&gt;
// v 1.0&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define ANZAHL_ZIFFERN	4&lt;br /&gt;
&lt;br /&gt;
extern uint8_t led7segment_ziffern[];&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void);&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void);&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_7segment.h&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem Logikanalysator kann das Multiplexen auf den einzelnen Leitungen gemessen werden (wie praktisch, dass am zweckentfremdeten IDE-Kabel eine zweite Buchsenleiste ist). Im folgenden Bild sind 5 Durchläufe der vierstelligen Anzeige dargestellt. &lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA uhr 7segment logik.png|Logikdiagramm der Uhr mit 7-Segment-Anzeige (Pollin Funk-AVR-Evaluationsboard)]] &lt;br /&gt;
&lt;br /&gt;
In den Spuren 01 bis 04 (blau) liegen die Versorgungssteuerleitungen, die den Multiplexbetrieb steuern, und in den Spuren 05 bis 08 (rot) die BCD-codierten Segmentsteuerleitungen. Ein Zyklus (gelber Bereich) dauert ca. 10,7 ms. Dabei wird für ca. 2,7 ms die Versorgungsspannung einer Ziffer eingeschaltet, während die anderen Ziffern ausgeschaltet sind. Die Logik auf den Versorgungsleitungen ist negiert, da die verwendeten PNP-Transistoren zum Durchschalten der Versorgungsspannung bei log. 0 öffnen und bei log. 1 schliessen (siehe auch [[Transistor#PNP.2FNPN_als_Schalter.2C_wohin_mit_der_Last.3F|Transistor als Schalter]]). Nach 2,7 ms  wird die Versorgungsspannung einer Ziffer gekappt, indem der zugehörige Transistor mit einem HIGH Pegel gesperrt wird und ein LOW Pegel am nächsten Transistor startet die Versorgung und damit die Sichtbarkeit der nächsten Ziffer...&lt;br /&gt;
&lt;br /&gt;
=== Suppentimer ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Suppe.jpg|thumb|right|175px|Motivation (schmackhaft)]] &lt;br /&gt;
&lt;br /&gt;
Kennt ihr das? Da ist man am bosseln und plötzlich meldet sich der Jieper auf Futter. Was geht schnell genug zwischen Compilerlauf und Flashen - ein leckeres Tütchen Suppe... Während das Reaktionsgemisch köchelt, flugs noch ein paar LEDs eingebaut und Stunden später riecht es aus der Küche komisch und die Nachbarin klopft. Wieder mal die Zeit vergessen!&lt;br /&gt;
&lt;br /&gt;
Aber es naht Abhilfe in Form des &#039;&#039;&#039;Suppentimers ST&#039;&#039;&#039;. In der ersten Ausbaustufe wird die 7-Segment-Uhr aus dem Beispiel vorher dafür genommen und es wird die Software umgeschrieben. UART und LCD werden nicht benötigt, deshalb können die Pin-Zuordnungen etwas bequemer gewählt werden. Z.B. bleiben jetzt die ISP-Leitungen an PORT B frei (Schaltplan siehe bei der Beschreibung von PFA_7segment.c).&lt;br /&gt;
&lt;br /&gt;
Die Bedienung und Anzeige bekommt eine raffinierte Benutzersteuerung:&lt;br /&gt;
&lt;br /&gt;
Der ST zählt nach dem Einschalten oder Reset Sekunden hoch und wenn bei der gewünschten Zahl der Taster 1 gedrückt wird, wird diese Zahl als Alarmzeit in Minuten genommen. Eine Marktrecherche hat nämlich ergeben, dass die typischen Tütchensuppen je nach Sorte im 5 bis 15 Minuten-Bereich köcheln sollen. 5-15 Sekunden zum Setzen des Alarms sind zumutbar, oder?&lt;br /&gt;
&lt;br /&gt;
Zur Korrektur des Alarms kann man entweder Reset drücken, das geht immer, oder man kann Taster 1 nochmal drücken, um die gesetzte Alarmzeit zu löschen.&lt;br /&gt;
&lt;br /&gt;
Bei gesetzter Alarmzeit wird links die Alarmzeit in Minuten angezeigt und rechts die aktuelle Zeit. Wenn die aktuelle Zeit gleich der Alarmzeit ist, schlägt der ST Alarm. Im Moment geschieht das durch Blinken der eingestellten Alarmzeit und Wechselblinken der LEDs (so wie in Blinky). Die aktuelle Zeit läuft weiter, so dass man abschätzen kann, wieviele Lötstellen &amp;quot;doch noch gehen&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Der letzte Bedienschritt ist, dass der Alarm mit einem Tastendruck an Taster 1 bestätigt werden muss. Quasi ein Nervfaktor, denn in einer späteren Hardware-Ausbaustufe könnte man drastischere Mittel einsetzen, z.&amp;amp;nbsp;B. einen 90 dB Piezosummer anwerfen oder so ähnlich.&lt;br /&gt;
&lt;br /&gt;
Bei der Umsetzung in die Steuersoftware (Firmware) hilft es, wenn man den geplanten Ablauf in einem Diagramm aufzeichnet. Eine Methode dafür ist es, ein Zustandsdiagramm für eine [[Statemachine|Zustandsmaschine]] zu zeichnen. Solche &#039;&#039;state charts&#039;&#039; erleichtern auch das spätere Debuggen und mehr ([http://www.embedded.com/design/opensource/219400531 State charts can provide you with software quality insurance] von Peter Mueller auf www.embedded.com).&lt;br /&gt;
&lt;br /&gt;
Die verschiedenen Zustände in obigem Usermanual sind u.a. &amp;quot;Nixmachen&amp;quot; (Sekunden hochzählen), Alarm setzen, Alarmzeit und aktuelle Zeit vergleichen, Alarmzeit löschen, Alarm geben, Alarm abschalten,... &lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA Suppentimer Zustand0.png|thumb|right|260px|Zustandsdiagramm]]&lt;br /&gt;
&lt;br /&gt;
In Anlehnung an die erste Testsuppe TS für den ST sind die Zustände in meinem Diagramm als kleine Klöße dargestellt. Die Übergänge oder Aktionen zum Zustandswechsel zwischen den Zuständen sind durch Pfeile dargestellt. Das sind einmal die Tastendrücke oder Ergebnisse der Vergleiche (beschriftete Pfeile) oder auch automatische Übergänge im Programm bzw. drei Warteschleifen (unbeschriftete Pfeile).&lt;br /&gt;
&lt;br /&gt;
Der Quellcode ist gegenüber dem Uhr Beispiel abgespeckt, da ja die UART und LCD Routinen beim ST nicht benötigt werden:&lt;br /&gt;
&lt;br /&gt;
* PFA_Suppentimer.c - Das Hauptprogramm&lt;br /&gt;
* PFA_7segment.c - Die Ansteuerung der Anzeige (geänderte Pinzuordnung!)&lt;br /&gt;
* PFA_7segment.h - dazugehörige Includedatei&lt;br /&gt;
* PFA_funkhw.h - Definitionen für das PFA ;-) Board (unverändert)&lt;br /&gt;
&lt;br /&gt;
Bevor es in den C-Code geht noch eine kleine Lesehilfe. Der Code enthält vier Hauptversionen mit steigendem Umfang bzw. einer Zusatzfunktion: Stromsparen! Welche Version übersetzt wird, wird durch das Makro SLEEP_CODE in PFA_Suppentimer.c gesteuert:&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA Suppentimer Zustand1-3.png|thumb|260px|Zustandsdiagramm mit Schlafmodus]]&lt;br /&gt;
&lt;br /&gt;
* 0 - keine Zusatzfunktion. Der ST arbeitet wie oben beschrieben.&lt;br /&gt;
* 1 bis 3 - ST fällt in einen Schlafmodus, wenn die Alarmzeit nicht innerhalb von SCHLAFENSZEIT Sekunden eingegeben wird. Im Schlafmodus wird die Anzeige abgeschaltet. Wie der Schlafmodus im Programm aussieht, steuert der Wert 1, 2 oder 3&lt;br /&gt;
** 1 - Zum Aufwecken aus einem simulierten Schlaf wartet der ST auf einen Tastendruck an Taster 1. Bis der Tastendruck kommt, dreht in einer aus Tasty bekannten Programmschleife Däumchen. &lt;br /&gt;
** 2 - Hier wird die Abfrage in der Programmschleife durch ein im Interrupt gesetztes Flag verlassen. Dazu wird eine kleine externe Hardware benötigt. An PD2 bzw. INT0 ist ein Taster 2 anzuschliessen, der per Interrupt INT0 den ST &amp;quot;aufweckt&amp;quot;. Die Art des Interrupts ist hier noch frei wählbar. Noch ist das Schlafen lediglich in einer kleinen Schleife im Hauptprogramm simuliert&lt;br /&gt;
** 3 - Jetzt geht es richtig zur Sache. Der ST fällt, wie im Artikel [[Sleep Mode]] beschrieben, in Tiefschlaf. Das Programm läuft nicht mehr. Der Timer pennt. Nur ein mit Taster2 ausgelöster &#039;&#039;&#039;LOW LEVEL (!)&#039;&#039;&#039; Interrupt oder ein Reset wecken den Schläfer auf.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_Suppentimer.c&#039;&#039;&#039; - Das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_Suppentimer.c&lt;br /&gt;
// v 1.0&lt;br /&gt;
// v 1.1 - Abfrage Taster in der Hauptschleife&lt;br /&gt;
//         Abfrage nur in den Zuständen, in denen Eingabe möglich ist&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
&lt;br /&gt;
    Externer Quarz-Oszillator: 12 MHz&lt;br /&gt;
    Optimierung: -Os&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_7segment.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#if 1&lt;br /&gt;
// Schneller Suppentimer zum Programmentwickeln&lt;br /&gt;
#define TRIGGER sekunden&lt;br /&gt;
#else&lt;br /&gt;
// Langsamer Suppentimer zum Suppenkochen&lt;br /&gt;
#define TRIGGER minuten&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
// Betriebszustände des Suppentimers&lt;br /&gt;
#define RUHEZUSTAND       0&lt;br /&gt;
#define ALARM_SETZEN      RUHEZUSTAND     + 1   // Nicht veränderbar&lt;br /&gt;
#define ALARM_PRUEFEN     ALARM_SETZEN    + 1&lt;br /&gt;
#define ALARM_LOESCHEN    ALARM_PRUEFEN   + 1&lt;br /&gt;
#define ALARM_GEBEN       ALARM_LOESCHEN  + 1&lt;br /&gt;
#define ALARM_QUITTIEREN  ALARM_GEBEN     + 1   // Nicht veränderbar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// SLEEP_CODE = Auswahl zur schrittweisen Programmentwicklung&lt;br /&gt;
//&lt;br /&gt;
// 0: Kein SLEEP&lt;br /&gt;
//&lt;br /&gt;
// 1: SIMULATION Stufe 1 SLEEP und INT0 werden NICHT benutzt.&lt;br /&gt;
//    Aufwachen: TASTER1 auf dem Board&lt;br /&gt;
//&lt;br /&gt;
// 2: SIMULATION Stufe 2 SLEEP wird NICHT benutzt, aber INT0 wird benutzt.&lt;br /&gt;
//    Aufwachen: Externe TASTER2 Hardware an PD2&lt;br /&gt;
//&lt;br /&gt;
// 3: Echtes SLEEP: SLEEP und INT0 werden benutzt.&lt;br /&gt;
//    Aufwachen: Externe TASTER2 Hardware an PD2&lt;br /&gt;
//&lt;br /&gt;
#define SLEEP_CODE        0&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE &amp;gt; 0 )&lt;br /&gt;
// Weitere Zustände&lt;br /&gt;
#define SCHLAFEN_GEHEN    ALARM_QUITTIEREN  + 1&lt;br /&gt;
#define SCHLAFEN          SCHLAFEN_GEHEN    + 1&lt;br /&gt;
#define AUFWACHEN         SCHLAFEN          + 1&lt;br /&gt;
// Sekunden im RUHEZUSTAND bis Schlafen beginnt&lt;br /&gt;
#define SCHLAFENSZEIT     23&lt;br /&gt;
#endif /* ( SLEEP_CODE &amp;gt; 0 ) */&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 2 )&lt;br /&gt;
// Hilfsvariable fürs Aufwachen&lt;br /&gt;
volatile uint8_t int0_zustand;&lt;br /&gt;
#endif /* ( SLEEP_CODE == 2 ) */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* in Millisekunden */&lt;br /&gt;
#define ENTPRELLUNG        10&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define RELOAD 	125&lt;br /&gt;
#define COUNT 	375&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
#define RELOAD 	75&lt;br /&gt;
#define COUNT 	625&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
#define RELOAD 	25&lt;br /&gt;
#define COUNT 	1875&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t stunden;&lt;br /&gt;
volatile uint8_t minuten;&lt;br /&gt;
volatile uint8_t sekunden;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE &amp;gt; 1 )&lt;br /&gt;
ISR(INT0_vect)&lt;br /&gt;
{&lt;br /&gt;
#if ( SLEEP_CODE == 2 )&lt;br /&gt;
    int0_zustand = AUFWACHEN;&lt;br /&gt;
#endif /* ( SLEEP_CODE == 2 ) */&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 3 )&lt;br /&gt;
    // set_sleep_mode(SLEEP_MODE_IDLE);&lt;br /&gt;
#endif /* ( SLEEP_CODE == 3 ) */&lt;br /&gt;
}&lt;br /&gt;
#endif /* ( SLEEP_CODE &amp;gt; 1 ) */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER0_OVF_vect)&lt;br /&gt;
{&lt;br /&gt;
    static uint16_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    TCNT0 = 256 - RELOAD + TCNT0;&lt;br /&gt;
&lt;br /&gt;
    led7segment_multiplex();&lt;br /&gt;
&lt;br /&gt;
    ticks += 1;&lt;br /&gt;
    if ( ticks == COUNT )&lt;br /&gt;
    {&lt;br /&gt;
        // 1 Sekunde ist um.&lt;br /&gt;
        sekunden += 1;&lt;br /&gt;
&lt;br /&gt;
        if ( sekunden == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( minuten == 60 )&lt;br /&gt;
        {&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden += 1;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( stunden == 24 )&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
&lt;br /&gt;
        // nächste Runde&lt;br /&gt;
        ticks = 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void uhr_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Prescaler 256 einstellen&lt;br /&gt;
    TCCR0 = (1&amp;lt;&amp;lt;CS02) | (0&amp;lt;&amp;lt;CS01) | (0&amp;lt;&amp;lt;CS00);&lt;br /&gt;
&lt;br /&gt;
    // Zählregister initialisieren&lt;br /&gt;
    TCNT0 = 256 - RELOAD;&lt;br /&gt;
&lt;br /&gt;
    // Overflow-Flag löschen&lt;br /&gt;
    TIFR = (1&amp;lt;&amp;lt;TOV0);&lt;br /&gt;
&lt;br /&gt;
    // Timer0 Overflow erlauben&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE0);&lt;br /&gt;
&lt;br /&gt;
    // Interrupts global einschalten&lt;br /&gt;
    sei();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void ausgabe_led(uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;0)) // Bit 0&lt;br /&gt;
        LED_AN(LED1);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED1);&lt;br /&gt;
&lt;br /&gt;
    if (wert &amp;amp; (1&amp;lt;&amp;lt;1)) // Bit 1&lt;br /&gt;
        LED_AN(LED2);&lt;br /&gt;
    else&lt;br /&gt;
        LED_AUS(LED2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#if 0&lt;br /&gt;
// Beim Makro darf die Argumentübergabe keine Seiteneffekte haben!&lt;br /&gt;
// Die Codegrösse ist bei -Os in beiden Fällen gleich&lt;br /&gt;
#define setze_ziffer(pziffer, wert)  *(pziffer) = (wert)&lt;br /&gt;
#else&lt;br /&gt;
static void setze_ziffer(uint8_t * pziffer, uint8_t wert)&lt;br /&gt;
{&lt;br /&gt;
    *pziffer = wert;&lt;br /&gt;
}&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint8_t taster1(uint8_t jetzt_zustand, uint8_t naechster_zustand)&lt;br /&gt;
{&lt;br /&gt;
    static uint8_t alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
&lt;br /&gt;
    if ( TASTER_GEDRUECKT() )&lt;br /&gt;
    {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
        _delay_ms(ENTPRELLUNG);&lt;br /&gt;
        if ( TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
        {&lt;br /&gt;
            // Wechsel von OFFEN nach GESCHLOSSEN?&lt;br /&gt;
            if ( alter_tastenzustand == TASTE_AUF )&lt;br /&gt;
            {&lt;br /&gt;
                jetzt_zustand = naechster_zustand;&lt;br /&gt;
                alter_tastenzustand = TASTE_ZU;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
    {&lt;br /&gt;
#if ENTPRELLUNG&lt;br /&gt;
        _delay_ms(ENTPRELLUNG);&lt;br /&gt;
        if ( !TASTER_GEDRUECKT() )&lt;br /&gt;
#endif /* ENTPRELLUNG */&lt;br /&gt;
            alter_tastenzustand = TASTE_AUF;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return jetzt_zustand;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t zustand = RUHEZUSTAND;&lt;br /&gt;
    uint8_t alarmzeit;&lt;br /&gt;
&lt;br /&gt;
    DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;TASTER);           // Port B: Eingang für Taster&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;LED1) | (1&amp;lt;&amp;lt;LED2);  // Port D: Ausgang für LED1 und LED2&lt;br /&gt;
&lt;br /&gt;
    led7segment_init();&lt;br /&gt;
&lt;br /&gt;
    uhr_init();&lt;br /&gt;
&lt;br /&gt;
    while (1)&lt;br /&gt;
    {&lt;br /&gt;
        switch (zustand)&lt;br /&gt;
        {&lt;br /&gt;
        case RUHEZUSTAND:&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[0], (sekunden % 10));&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[1], (sekunden / 10));&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[2], (0b00001111));     // BCD &amp;quot;blank&amp;quot;&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[3], (0b00001111));&lt;br /&gt;
&lt;br /&gt;
#if (SLEEP_CODE &amp;gt; 0)&lt;br /&gt;
            // SLEEP?&lt;br /&gt;
            if ( sekunden &amp;gt;= SCHLAFENSZEIT )&lt;br /&gt;
                zustand = SCHLAFEN_GEHEN;&lt;br /&gt;
#endif /* (SLEEP_CODE &amp;gt; 0) */&lt;br /&gt;
&lt;br /&gt;
            // Aus diesem Zustand geht es&lt;br /&gt;
            // durch Usereingabe (Tastendruck)&lt;br /&gt;
            // nächster Zustand dann RUHEZUSTAND+1 = ALARM_SETZEN&lt;br /&gt;
            if ( zustand == RUHEZUSTAND )&lt;br /&gt;
                zustand = taster1(RUHEZUSTAND, ALARM_SETZEN);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case ALARM_SETZEN:&lt;br /&gt;
            alarmzeit = sekunden;&lt;br /&gt;
            TRIGGER = 0;&lt;br /&gt;
            zustand = ALARM_PRUEFEN;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case ALARM_PRUEFEN:&lt;br /&gt;
            if ( TRIGGER == alarmzeit )&lt;br /&gt;
            {&lt;br /&gt;
                zustand = ALARM_GEBEN;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[0], (TRIGGER % 10));&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[1], (TRIGGER / 10));&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[2], (alarmzeit % 10));&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[3], (alarmzeit / 10));&lt;br /&gt;
                zustand = taster1(ALARM_PRUEFEN, ALARM_LOESCHEN);&lt;br /&gt;
            }&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case ALARM_LOESCHEN:&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
            zustand = RUHEZUSTAND;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case ALARM_GEBEN:&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[0], (TRIGGER % 10));&lt;br /&gt;
            setze_ziffer(&amp;amp;led7segment_ziffern[1], (TRIGGER / 10));&lt;br /&gt;
&lt;br /&gt;
            // Ziffer 3+4: Alarmzeit und LEDs blinken im Sekundentakt&lt;br /&gt;
            if ( (sekunden &amp;amp; 1) )&lt;br /&gt;
            {&lt;br /&gt;
                ausgabe_led(2);&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[2], (0b00001111));     // BCD &amp;quot;blank&amp;quot;&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[3], (0b00001111));&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                ausgabe_led(1);&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[2], (alarmzeit % 10));&lt;br /&gt;
                setze_ziffer(&amp;amp;led7segment_ziffern[3], (alarmzeit / 10));&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Aus diesem Zustand geht es nur durch&lt;br /&gt;
            // Usereingabe (Tastendruck) raus!&lt;br /&gt;
            // nächster Zustand dann ALARM_GEBEN+1 = ALARM_QUITTIEREN&lt;br /&gt;
            zustand = taster1(ALARM_GEBEN, ALARM_QUITTIEREN);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case ALARM_QUITTIEREN:&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
            zustand = RUHEZUSTAND;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE &amp;gt; 0 )&lt;br /&gt;
        case SCHLAFEN_GEHEN:&lt;br /&gt;
            led7segment_exit();&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 2 )&lt;br /&gt;
            int0_zustand = SCHLAFEN;&lt;br /&gt;
            // PD2 ist Input, internen Pullup ein&lt;br /&gt;
            PORTD |= (1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
            // INT0 z.&amp;amp;nbsp;B. bei fallender Flanke&lt;br /&gt;
            MCUCR |= (1&amp;lt;&amp;lt;ISC01) | (0&amp;lt;&amp;lt;ISC00);&lt;br /&gt;
            // Anstehende INT0 löschen (durch 1 schreiben)&lt;br /&gt;
            GIFR |= (1&amp;lt;&amp;lt;INTF0);&lt;br /&gt;
            // Wecker stellen: INT0 enable, sei läuft bereits (uhr_init)&lt;br /&gt;
            GICR |= (1&amp;lt;&amp;lt;INT0);&lt;br /&gt;
#endif /* ( SLEEP_CODE == 2 ) */&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 3 )&lt;br /&gt;
            // Tiefschlaf!&lt;br /&gt;
            set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
            // PD2 ist Input, internen Pullup ein&lt;br /&gt;
            PORTD |= (1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
            // ##############################################&lt;br /&gt;
            // # ACHTUNG: nur LOW LEVEL Interrupt möglich ! #&lt;br /&gt;
            // ##############################################&lt;br /&gt;
            MCUCR |= (0&amp;lt;&amp;lt;ISC01) | (0&amp;lt;&amp;lt;ISC00);&lt;br /&gt;
            // Anstehende INT0 löschen (durch 1 schreiben)&lt;br /&gt;
            GIFR |= (1&amp;lt;&amp;lt;INTF0);&lt;br /&gt;
            // Wecker stellen: INT0 enable, sei läuft bereits (uhr_init)&lt;br /&gt;
            GICR |= (1&amp;lt;&amp;lt;INT0);&lt;br /&gt;
#endif /* ( SLEEP_CODE == 3 ) */&lt;br /&gt;
&lt;br /&gt;
            zustand = SCHLAFEN;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case SCHLAFEN:&lt;br /&gt;
            ausgabe_led(0);&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 1 )&lt;br /&gt;
            // Tastendrücken abwarten = Aufwachen&lt;br /&gt;
            while ( taster1(1, 0) )&lt;br /&gt;
                ;&lt;br /&gt;
#endif /* SLEEP_CODE == 1 */&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 2 )&lt;br /&gt;
            // Schlafen&lt;br /&gt;
            while ( int0_zustand == SCHLAFEN )&lt;br /&gt;
                ;&lt;br /&gt;
#endif /* ( SLEEP_CODE == 2 ) */&lt;br /&gt;
&lt;br /&gt;
#if ( SLEEP_CODE == 3 )&lt;br /&gt;
            // Und weg...&lt;br /&gt;
            sleep_mode();&lt;br /&gt;
            // Hier geht es bei SLEEP_MODE_PWR_DOWN !!!&lt;br /&gt;
            // nur nach einem INT0-Interrupt weiter&lt;br /&gt;
#endif /* ( SLEEP_CODE == 3 ) */&lt;br /&gt;
&lt;br /&gt;
            zustand = AUFWACHEN;&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case AUFWACHEN:&lt;br /&gt;
#if (( SLEEP_CODE == 2 ) || ( SLEEP_CODE == 3 ))&lt;br /&gt;
            // INT0 disable, sei läuft weiter (siehe uhr_init)&lt;br /&gt;
            GICR &amp;amp;= ~(1&amp;lt;&amp;lt;INT0);&lt;br /&gt;
&lt;br /&gt;
            // PD2 bleibt Input, internen Pullup aus&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
#endif /* ( SLEEP_CODE == 2 ) || ( SLEEP_CODE == 3 ) */&lt;br /&gt;
&lt;br /&gt;
            sekunden = 0;&lt;br /&gt;
            minuten = 0;&lt;br /&gt;
            stunden = 0;&lt;br /&gt;
            led7segment_init();&lt;br /&gt;
            zustand = RUHEZUSTAND;&lt;br /&gt;
            break;&lt;br /&gt;
#endif /* ( SLEEP_CODE &amp;gt; 0 ) */&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ( zustand != ALARM_GEBEN ) // Blinken nicht stören!&lt;br /&gt;
            ausgabe_led(zustand);       // Feedback auf LEDs&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_Suppentimer.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA Suppentimer SPlan.png|thumb|256px|Teil-Schaltplan Suppentimer ST. Details zur 7-Segmentanzeige siehe Beispiel Uhr mit 7-Segmentanzeige]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_7segment.c&#039;&#039;&#039; - Die Ansteuerung der Anzeige (geänderte Pinzuordnung!)&lt;br /&gt;
&lt;br /&gt;
Dieser Quellcode enthält die Grundansteuerung der 7-Segment-Anzeige mit den neu zugeordneten Pins. Die Hardware wurde ausserdem um einen Taster 2 (Aufwecktaster) und eine Anschaltung der Versorgungsspannung des Anzeigeteils ergänzt, um tatsächlich einen energiesparenden und erholsamen Schlaf zu haben. OK es geht mehr ums Prinzip, denn der nicht abschaltbare Rest des Evaluationsboards (Netz-LED, Spannungswandler, MAX232) saugen schon noch im zig Milliampere-Bereich.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard&lt;br /&gt;
// PFA_7segment.c&lt;br /&gt;
//&lt;br /&gt;
// v 1.0&lt;br /&gt;
// v 1.1  - Pinbelegung geändert&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_7segment.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint8_t led7segment_ziffern[ANZAHL_ZIFFERN];&lt;br /&gt;
static uint8_t led7segment_enabled = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void led7segment_power_off(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void led7segment_power_on(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; ANZAHL_ZIFFERN; i++)&lt;br /&gt;
        led7segment_ziffern[i] = 0b00001111; // BCD Blank&lt;br /&gt;
&lt;br /&gt;
    // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
    DDRD |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung an&lt;br /&gt;
    led7segment_power_on();&lt;br /&gt;
&lt;br /&gt;
    // Alle Segmente der Ziffern aus&lt;br /&gt;
    DDRC |= (1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3);&lt;br /&gt;
    PORTC |= ((1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3)); // BCD Code 0b1111 für &amp;quot;Blank&amp;quot; an Ausgabepins&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; ANZAHL_ZIFFERN; i++)&lt;br /&gt;
        led7segment_ziffern[i] = 0b00001111; // BCD Blank&lt;br /&gt;
&lt;br /&gt;
    // RESET-Zustand: Pins Input und Tristate&lt;br /&gt;
    DDRD  &amp;amp;= ~((1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3));&lt;br /&gt;
    PORTD &amp;amp;= ~((1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3));&lt;br /&gt;
&lt;br /&gt;
    // RESET-Zustand: Pins Input und Tristate&lt;br /&gt;
    DDRC  &amp;amp;= ~((1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3));&lt;br /&gt;
    PORTC &amp;amp;= ~((1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3));&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung aus&lt;br /&gt;
    led7segment_power_off();&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void)&lt;br /&gt;
{&lt;br /&gt;
    static uint8_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    if ( led7segment_enabled )&lt;br /&gt;
    {&lt;br /&gt;
        // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
&lt;br /&gt;
        // Segmente der neuen Ziffer berechnen&lt;br /&gt;
        PORTC = (PORTC &amp;amp; 0b11110000) | led7segment_ziffern[ticks];&lt;br /&gt;
        switch ( ticks )&lt;br /&gt;
        {&lt;br /&gt;
        case 0:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD0);	// Spannung ein&lt;br /&gt;
            break;&lt;br /&gt;
        case 1:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD1);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // auf nächste Ziffer weiterschalten&lt;br /&gt;
        ticks += 1;&lt;br /&gt;
        ticks %= ANZAHL_ZIFFERN;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_7segment.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PFA_7segment.h&#039;&#039;&#039; - dazugehörige Includedatei&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
// PFA_7segment.h&lt;br /&gt;
// v 1.1&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define ANZAHL_ZIFFERN	4&lt;br /&gt;
&lt;br /&gt;
extern uint8_t led7segment_ziffern[];&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void);&lt;br /&gt;
&lt;br /&gt;
void led7segment_exit(void);&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void);&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_7segment.h&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Redesign der Hardware&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA Suppentimer SPlan SR.png|thumb|256px|Redesign-Schaltplan Suppentimer ST mit Schieberegister &#039;&#039;&#039;74HC164&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
Das IC CA3161 ist nicht mehr gut erhältlich. Deshalb wurde die Schaltung mit einem Schieberegister (&#039;&#039;8 Bit Serial Shift Register&#039;&#039;) aus der [[74xx]] Familie umgebaut. Statt dem 74xx595 wie im [[AVR-Tutorial: Schieberegister]] beschrieben, wurde das einfachere &#039;&#039;&#039;74HC164&#039;&#039;&#039; verwendet. Das 74xx595 hätte ich bestellen müssen und das 74HC164 konnte ich kurzfristig organisieren ;-) Die Routinen zur Ansteuerung befinden sich in der geänderten Datei PFA_7segment.c:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//&lt;br /&gt;
// Pollin Funk-AVR-Evaluationsboard&lt;br /&gt;
// PFA_7segment.c&lt;br /&gt;
//&lt;br /&gt;
// v 1.0&lt;br /&gt;
// v 1.1  - Pinbelegung geändert (s. Code)&lt;br /&gt;
// v 1.2  - Trennung in BCD-Version und Schieberegister-Version&lt;br /&gt;
//          Pinbelegung s. Code&lt;br /&gt;
//&lt;br /&gt;
// PB0 =&amp;gt; Spannungsversorgung Anzeige (über NPN Transistor)&lt;br /&gt;
//          C: GND von der Anzeige&lt;br /&gt;
//          B: PB0 =&amp;gt; 2,2 K =&amp;gt; B&lt;br /&gt;
//          E: GND am PFA&lt;br /&gt;
// PD2 =&amp;gt; Externer Taster2&lt;br /&gt;
//          PD2 =&amp;gt; Taster (offen) =&amp;gt; 330Ω =&amp;gt; GND&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_7segment.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;lt;avr/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define BCD               0&lt;br /&gt;
#define SCHIEBEREGISTER   !(BCD)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
uint8_t led7segment_ziffern[ANZAHL_ZIFFERN];&lt;br /&gt;
static uint8_t led7segment_enabled = 0;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void led7segment_power_off(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB  |=  (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void led7segment_power_on(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB  |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#if SCHIEBEREGISTER&lt;br /&gt;
/*&lt;br /&gt;
    ###################################&lt;br /&gt;
          SCHIEBEREGISTER 74HC164&lt;br /&gt;
    ###################################&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define SR_DDR        DDRB&lt;br /&gt;
#define SR_PORT       PORTB&lt;br /&gt;
#define SR_CLEAR      PB2&lt;br /&gt;
#define SR_CLOCK      PB5&lt;br /&gt;
#define SR_INPUT      PB3&lt;br /&gt;
&lt;br /&gt;
#define ZIFFERN_DDR   DDRC&lt;br /&gt;
#define ZIFFERN_PORT  PORTC&lt;br /&gt;
#define ZIFFER1       PC0&lt;br /&gt;
#define ZIFFER2       PC1&lt;br /&gt;
#define ZIFFER3       PC2&lt;br /&gt;
#define ZIFFER4       PC3&lt;br /&gt;
&lt;br /&gt;
static uint8_t segment_tab[] =&lt;br /&gt;
{&lt;br /&gt;
    // Quelle: AVR-Tutorial auf www.mikrocontroller.net&lt;br /&gt;
    /* .db */ 0b11000000 , //    ; 0: a, b, c, d, e, f&lt;br /&gt;
    /* .db */ 0b11111001 , //    ; 1: b, c&lt;br /&gt;
    /* .db */ 0b10100100 , //    ; 2: a, b, d, e, g&lt;br /&gt;
    /* .db */ 0b10110000 , //    ; 3: a, b, c, d, g&lt;br /&gt;
    /* .db */ 0b10011001 , //    ; 4: b, c, f, g&lt;br /&gt;
    /* .db */ 0b10010010 , //    ; 5: a, c, d, f, g&lt;br /&gt;
    /* .db */ 0b10000010 , //    ; 6: a, c, d, e, f, g&lt;br /&gt;
    /* .db */ 0b11111000 , //    ; 7: a, b, c&lt;br /&gt;
    /* .db */ 0b10000000 , //    ; 8: a, b, c, d, e, f, g&lt;br /&gt;
    /* .db */ 0b10010000 , //    ; 9: a, b, c, d, f, g&lt;br /&gt;
    0xFF,&lt;br /&gt;
    0xFF,&lt;br /&gt;
    0xFF,&lt;br /&gt;
    0xFF,&lt;br /&gt;
    0xFF,&lt;br /&gt;
    0xFF&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; ANZAHL_ZIFFERN; i++)&lt;br /&gt;
        led7segment_ziffern[i] = 0b00001111; // BCD Blank&lt;br /&gt;
&lt;br /&gt;
    // Alle Ziffern aus&lt;br /&gt;
    ZIFFERN_DDR  |= ((1&amp;lt;&amp;lt;ZIFFER1) | (1&amp;lt;&amp;lt;ZIFFER2) | (1&amp;lt;&amp;lt;ZIFFER3) | (1&amp;lt;&amp;lt;ZIFFER4));&lt;br /&gt;
    ZIFFERN_PORT |= ((1&amp;lt;&amp;lt;ZIFFER1) | (1&amp;lt;&amp;lt;ZIFFER2) | (1&amp;lt;&amp;lt;ZIFFER3) | (1&amp;lt;&amp;lt;ZIFFER4));&lt;br /&gt;
&lt;br /&gt;
    SR_DDR  |=  ((1&amp;lt;&amp;lt;SR_INPUT) | (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR)); // Output&lt;br /&gt;
    SR_PORT &amp;amp;= ~((1&amp;lt;&amp;lt;SR_INPUT) | (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR)); // LOW&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung an&lt;br /&gt;
    led7segment_power_on();&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    SR_DDR  &amp;amp;= ~((1&amp;lt;&amp;lt;SR_INPUT) | (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR)); // Input&lt;br /&gt;
    SR_PORT &amp;amp;= ~((1&amp;lt;&amp;lt;SR_INPUT) | (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR)); // Pullups aus, Tristate&lt;br /&gt;
&lt;br /&gt;
    // RESET-Zustand: Pins Input und Tristate&lt;br /&gt;
    ZIFFERN_DDR  &amp;amp;= ~((1&amp;lt;&amp;lt;ZIFFER1) | (1&amp;lt;&amp;lt;ZIFFER2) | (1&amp;lt;&amp;lt;ZIFFER3) | (1&amp;lt;&amp;lt;ZIFFER4));&lt;br /&gt;
    ZIFFERN_PORT &amp;amp;= ~((1&amp;lt;&amp;lt;ZIFFER1) | (1&amp;lt;&amp;lt;ZIFFER2) | (1&amp;lt;&amp;lt;ZIFFER3) | (1&amp;lt;&amp;lt;ZIFFER4));&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung aus&lt;br /&gt;
    led7segment_power_off();&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void)&lt;br /&gt;
{&lt;br /&gt;
    static uint8_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    if ( led7segment_enabled )&lt;br /&gt;
    {&lt;br /&gt;
        uint8_t tmp_segmente;&lt;br /&gt;
        uint8_t i;&lt;br /&gt;
&lt;br /&gt;
        // Aktuelle Ziffer aus&lt;br /&gt;
        switch ( ticks )&lt;br /&gt;
        {&lt;br /&gt;
        case 0:&lt;br /&gt;
            ZIFFERN_PORT |= (1&amp;lt;&amp;lt;ZIFFER1);&lt;br /&gt;
            break;&lt;br /&gt;
        case 1:&lt;br /&gt;
            ZIFFERN_PORT |= (1&amp;lt;&amp;lt;ZIFFER2);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            ZIFFERN_PORT |= (1&amp;lt;&amp;lt;ZIFFER3);&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            ZIFFERN_PORT |= (1&amp;lt;&amp;lt;ZIFFER4);&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Nächste Ziffer&lt;br /&gt;
        ticks += 1;&lt;br /&gt;
        ticks %= ANZAHL_ZIFFERN;&lt;br /&gt;
        tmp_segmente = segment_tab[led7segment_ziffern[ticks] &amp;amp; 0x0F];&lt;br /&gt;
&lt;br /&gt;
        // Register leeren&lt;br /&gt;
        SR_PORT = (1&amp;lt;&amp;lt;SR_CLOCK) | (0&amp;lt;&amp;lt;SR_CLEAR);&lt;br /&gt;
        SR_PORT = (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR);&lt;br /&gt;
&lt;br /&gt;
        i = 8;&lt;br /&gt;
        do&lt;br /&gt;
        {&lt;br /&gt;
            i -= 1;&lt;br /&gt;
            SR_PORT |= (1&amp;lt;&amp;lt;SR_INPUT);&lt;br /&gt;
            SR_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;SR_CLOCK);&lt;br /&gt;
            if ( !(tmp_segmente &amp;amp; (1&amp;lt;&amp;lt;i)) )   // MSB zuerst&lt;br /&gt;
                SR_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;SR_INPUT);&lt;br /&gt;
            SR_PORT |= (1&amp;lt;&amp;lt;SR_CLOCK);&lt;br /&gt;
        }&lt;br /&gt;
        while (i);&lt;br /&gt;
&lt;br /&gt;
        SR_PORT = (1&amp;lt;&amp;lt;SR_CLOCK) | (1&amp;lt;&amp;lt;SR_CLEAR) | (0&amp;lt;&amp;lt;SR_INPUT);&lt;br /&gt;
&lt;br /&gt;
        // Aktuelle Ziffer ein&lt;br /&gt;
        switch ( ticks )&lt;br /&gt;
        {&lt;br /&gt;
        case 0:&lt;br /&gt;
            ZIFFERN_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;ZIFFER1);&lt;br /&gt;
            break;&lt;br /&gt;
        case 1:&lt;br /&gt;
            ZIFFERN_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;ZIFFER2);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            ZIFFERN_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;ZIFFER3);&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            ZIFFERN_PORT &amp;amp;= ~(1&amp;lt;&amp;lt;ZIFFER4);&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
#endif /* SCHIEBEREGISTER */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#if BCD&lt;br /&gt;
/*&lt;br /&gt;
    ####################################&lt;br /&gt;
        BCD-7Segment-Decoder CA3161&lt;br /&gt;
    ####################################&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_init(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; ANZAHL_ZIFFERN; i++)&lt;br /&gt;
        led7segment_ziffern[i] = 0b00001111; // BCD Blank&lt;br /&gt;
&lt;br /&gt;
    // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
    DDRD  |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
    PORTD |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung an&lt;br /&gt;
    led7segment_power_on();&lt;br /&gt;
&lt;br /&gt;
    // Alle Segmente der Ziffern aus&lt;br /&gt;
    DDRC  |= (1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3);&lt;br /&gt;
    PORTC |= (1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3); // BCD Code 0b1111 für &amp;quot;Blank&amp;quot; an Ausgabepins&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_exit(void)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    for (i = 0; i &amp;lt; ANZAHL_ZIFFERN; i++)&lt;br /&gt;
        led7segment_ziffern[i] = 0b00001111; // BCD Blank&lt;br /&gt;
&lt;br /&gt;
    // RESET-Zustand: Pins Input und Tristate&lt;br /&gt;
    DDRD  &amp;amp;= ~((1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3));&lt;br /&gt;
    PORTD &amp;amp;= ~((1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3));&lt;br /&gt;
&lt;br /&gt;
    // RESET-Zustand: Pins Input und Tristate&lt;br /&gt;
    DDRC  &amp;amp;= ~((1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3));&lt;br /&gt;
    PORTC &amp;amp;= ~((1&amp;lt;&amp;lt;PC0) | (1&amp;lt;&amp;lt;PC1) | (1&amp;lt;&amp;lt;PC2) | (1&amp;lt;&amp;lt;PC3));&lt;br /&gt;
&lt;br /&gt;
    // Versorgungsspannung aus&lt;br /&gt;
    led7segment_power_off();&lt;br /&gt;
&lt;br /&gt;
    led7segment_enabled = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void led7segment_multiplex(void)&lt;br /&gt;
{&lt;br /&gt;
    static uint8_t ticks = 0;&lt;br /&gt;
&lt;br /&gt;
    if ( led7segment_enabled )&lt;br /&gt;
    {&lt;br /&gt;
        // Spannung aller Ziffern aus (neg. Logik)&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD0) | (1&amp;lt;&amp;lt;PD1) | (1&amp;lt;&amp;lt;PD2) | (1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
&lt;br /&gt;
        // Segmente der neuen Ziffer berechnen&lt;br /&gt;
        PORTC = (PORTC &amp;amp; 0b11110000) | led7segment_ziffern[ticks];&lt;br /&gt;
        switch ( ticks )&lt;br /&gt;
        {&lt;br /&gt;
        case 0:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD0);	// Spannung ein&lt;br /&gt;
            break;&lt;br /&gt;
        case 1:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD1);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD2);&lt;br /&gt;
            break;&lt;br /&gt;
        case 3:&lt;br /&gt;
            PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD3);&lt;br /&gt;
            break;&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // auf nächste Ziffer weiterschalten&lt;br /&gt;
        ticks += 1;&lt;br /&gt;
        ticks %= ANZAHL_ZIFFERN;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
#endif /* BCD */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_7segment.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Timer1 Interrupt ===&lt;br /&gt;
&lt;br /&gt;
F: Wie kann man den Timer 1 Compare Match Interrupt auslösen ([http://www.mikrocontroller.net/topic/124400])?&lt;br /&gt;
&lt;br /&gt;
Hier ist ein Beispiel mit dem Timer1 im Modus &#039;&#039;PWM phase and frequency correct&#039;&#039;. LED1 blinkt mit 1 Hz per Timer 1 Interrupt und LED2 mit 1 Hz per Software (_delay_ms()):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Pollin Funk AVR Evaluationsboard&lt;br /&gt;
    F_CPU             12000000&lt;br /&gt;
    MCU               ATMega8&lt;br /&gt;
    Compileoptionen   -Os&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 12000000L&lt;br /&gt;
#endif&lt;br /&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;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR (TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
  PORTD ^= (1&amp;lt;&amp;lt;PD6);  // toggle LED1&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Timer_io_init(void)&lt;br /&gt;
{&lt;br /&gt;
  // Table 39 im Atmega8 Datenblatt&lt;br /&gt;
  // Waveform generation mode: &lt;br /&gt;
  // Mode 9: PWM Phase and frequency correct&lt;br /&gt;
  TCCR1A |= (0 &amp;lt;&amp;lt; WGM11) | (1 &amp;lt;&amp;lt; WGM10);&lt;br /&gt;
  TCCR1B |= (1 &amp;lt;&amp;lt; WGM13) | (0 &amp;lt;&amp;lt; WGM12);&lt;br /&gt;
&lt;br /&gt;
  // Table 40 im Atmega8 Datenblatt&lt;br /&gt;
  // PRESCALER: clk/1024&lt;br /&gt;
  TCCR1B |= (1 &amp;lt;&amp;lt; CS12)  | (0 &amp;lt;&amp;lt; CS11)  | (1 &amp;lt;&amp;lt; CS10);&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
    Wunsch: ca. 1 Hz Blinken&lt;br /&gt;
    1 Hz genau - siehe: http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC&lt;br /&gt;
&lt;br /&gt;
    Setzen von TOP (OCR1A) = Anzahl der Timer1-Takte von IRQ zu IRQ&lt;br /&gt;
    Figure 40 im Atmega8 Datenblatt&lt;br /&gt;
    1. /2 wegen 2 Halbperioden pro Sekunde bei 1 Hz&lt;br /&gt;
    2. /2 wegen Runterzählen von TOP (=OCR1A) nach BOTTOM (=0) &lt;br /&gt;
       und Hochzählen von BOTTOM (=0) bis TOP (=OCR1A) zwischen &lt;br /&gt;
       den Interrupts&lt;br /&gt;
  */&lt;br /&gt;
&lt;br /&gt;
#define DELAY (F_CPU / 1024 / 2 / 2) &lt;br /&gt;
  OCR1A = DELAY; &lt;br /&gt;
  TCNT1 = - DELAY; // Anlauf für 1. Interrupt verlängern&lt;br /&gt;
&lt;br /&gt;
  TIMSK = (1 &amp;lt;&amp;lt; OCIE1A);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRD = (1&amp;lt;&amp;lt;PD6) | (1&amp;lt;&amp;lt;PD5); &lt;br /&gt;
  Timer_io_init();&lt;br /&gt;
  sei();&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    PORTD ^= (1&amp;lt;&amp;lt;PD5);  // toggle LED2&lt;br /&gt;
    _delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== RC5 Empfänger ===&lt;br /&gt;
&lt;br /&gt;
Im Verlauf der Diskussion [http://www.mikrocontroller.net/topic/124288] stellte sich die Frage, wie ein Empfänger für den RC5 Code von Infrarot (IR) Fernbedienungen aufgebaut werden kann.&lt;br /&gt;
&lt;br /&gt;
Vorab eine Anmerkung, die man auch regelmäßig im Forum liest: Nur relativ wenige IR-Fernbedienungen benutzen auch den von Philips erfundenen RC5 Code. Ich habe mir eine preiswerte Universalfernbedienung beschafft, die man auf Philips Geräte einstellen kann. &lt;br /&gt;
&lt;br /&gt;
Der Aufbau des RC5-Codes und vieler anderer IR-Codes ist übrigens sehr gut auf der Seite von Sam Bergmans beschrieben ([http://www.sbprojects.com/knowledge/ir/ir.htm www.sbprojects.com]). Und der Eigenbau eines passenden Senders war ja die Ausgangslage in obiger Diskussion...&lt;br /&gt;
&lt;br /&gt;
[[Bild:TSOP1736.png|thumb|right|240px|&#039;&#039;&#039;TSOP1736&#039;&#039;&#039; aus: Photo Modules for PCM Remote Control Systems von Vishay]]&lt;br /&gt;
&lt;br /&gt;
Zum Empfang von RC5-kodierten IR-Signale ist der IR-Empfänger &#039;&#039;&#039;TSOP1736&#039;&#039;&#039; ([http://www.datasheetcatalog.org/datasheets/208/301092_DS.pdf PDF]) von Vishay geeignet. &lt;br /&gt;
&lt;br /&gt;
Leider ist der TSOP1736 inzwischen abgekündigt. Es gibt aber funktionskompatiblen Ersatz [http://www.mikrocontroller.net/topic/198470#1946910] und [http://www.mikrocontroller.net/topic/200202#1966074]. Bei den Ersatztypen ist unbedingt die Pinbelegung anhand des Datenblattes zu kontrollieren und ggf. anzupassen.&lt;br /&gt;
&lt;br /&gt;
In der Codesammlung hat &#039;&#039;&#039;Peter Dannegger&#039;&#039;&#039; C-Quelltexte veröffentlicht, die eine Dekodierung von RC5 Signalen auf AVR µCs ermöglichen ([http://www.mikrocontroller.net/topic/12216]). Bei einigen Compilerversionen muss die Rückgabevariable rc5_data &#039;&#039;&#039;volatile&#039;&#039;&#039; gekennzeichnet sein, wenn man mit Optimierung übersetzt ([http://www.mikrocontroller.net/topic/137340]). &lt;br /&gt;
&lt;br /&gt;
Die Quelltexte werden wie üblich in ein AVR-Studio C Projekt aufgenommen. Unter den Projektoptionen wird die 8000000 bei der Taktrate und die Optimierungstufe -Os eingestellt. 8 MHz ist ja der an der kleinsten µC-Fassung zu verbauende Quarz.&lt;br /&gt;
&lt;br /&gt;
Folgende Änderungen sind zur Anpassung an einen &#039;&#039;&#039;Attiny2313&#039;&#039;&#039; auf dem Pollin Funk AVR Board nötig bzw. hilfreich.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Änderungen in main.h&#039;&#039;&#039;:&lt;br /&gt;
* Anpassen der Includes. Hier ist der Pfad um avr/ zu ergänzen und die veraltete signal.h wird auskommentiert.&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;signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Anpassen des Eingangspins für das Signal vom TSOP1736. &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define xRC5 PD2 // IR input low active (statt PD7)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Anpassen der Taktrate. Der Attiny2313 wird auf dem werksmäßigen Pollin Funk AVR Board mit 8 MHz betrieben. Die Einstellung der externen Taktquelle mit den [[AVR Fuses]] ist ebenfalls nötig, wenn noch nicht geschehen! Tipps zu Anpassungen für noch geringere Taktraten gibt es in der Diskussion ab Beitrag [http://www.mikrocontroller.net/topic/164465#1569609] im Forum.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
//#define XTAL 11.0592e6&lt;br /&gt;
#define XTAL 8e6 // Attiny2313 auf Pollin Funk AVR Board&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Sicherstellen, dass die Variable für den Datenaustausch zwischen Hauptprogramm und Timerroutine &#039;&#039;&#039;volatile&#039;&#039;&#039; deklariert ist.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
extern volatile uint  rc5_data;        // store result&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Änderungen in main.c&#039;&#039;&#039;:&lt;br /&gt;
* Anpassen der Timer-Register. Die Benennung beim Attiny2313 ist anders als im Original&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // TCCR0 = 1&amp;lt;&amp;lt;CS02; //divide by 256 Original&lt;br /&gt;
  TCCR0B = 1&amp;lt;&amp;lt;CS02;   //divide by 256 Attiny2313&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Anpassung der UART-Initialisierung an die Initialisierung des Attiny2313 (siehe andere Beispiele in diesem Artikel)&lt;br /&gt;
* Änderung der DEBUG-LED. Im Original wird PORTB zum Debuggen benutzt. Das wird auf die LED1 auf dem Board geändert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Am Programmanfang einfügen&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;PD6); // PD6 Ausgang&lt;br /&gt;
  PORTD |= (1&amp;lt;&amp;lt;PD6); // DEBUG-LED LED1 an&lt;br /&gt;
...&lt;br /&gt;
  // Auskommentiert&lt;br /&gt;
  // DDRB = i; // LED output&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Änderungen in rc5.c&#039;&#039;&#039;:&lt;br /&gt;
* Sicherstellen, dass die Variable für den Datenaustausch zwischen Hauptprogramm und Timerroutine &#039;&#039;&#039;volatile&#039;&#039;&#039; definiert ist.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
volatile uint  rc5_data;        // store result&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Einbau der DEBUG-LED. Dazu wird folgendes Codestück zwischen #if 1 und #endif ergänzt. &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    if( !tmp || rc5_time &amp;gt; PULSE_1_2 )&lt;br /&gt;
    { // start or long pulse time&lt;br /&gt;
#if 1&lt;br /&gt;
      /*&lt;br /&gt;
        Anschluss PD6----###---&amp;gt;|---- GND&lt;br /&gt;
      */&lt;br /&gt;
      DDRD |= (1&amp;lt;&amp;lt;PD6);   // PD6 auf Ausgang&lt;br /&gt;
      PORTD ^= (1&amp;lt;&amp;lt;PD6);  // LED1 an PD6 togglen&lt;br /&gt;
#endif&lt;br /&gt;
      if( !(tmp &amp;amp; 0x4000) ) // not to many bits&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der TSOP1736 OUT wird - wie in main.h definiert - an PD2 des Attiny2313 angeschlossen. PD2, Vcc und GND können von der 40-poligen Erweiterungsbuchse abgegriffen werden. In meinem Steckbrettaufbau hatte ich den 10 kOhm Pullup-Widerstand **) eingebaut aber die Entstörung der Betriebsspannung *) nicht. Bei einem &amp;quot;echten&amp;quot; Gerät würde ich beide Schaltungsoptionen einbauen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Schema IR-RX.png]]&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe des dekodierten Signals (Togglebit Anm.[http://www.mikrocontroller.net/topic/168346#1613187], Adresse, Kommando) kann mit einem [[RS232]] Terminalprogramm betrachtet werden, Anschluss des Pollin Boards an die serielle Schnittstelle vorausgesetzt. Die RS232-Einstellungen sind 19200 Baud, 8 Batenbits, No Parity und 1 Stopbit.&lt;br /&gt;
&lt;br /&gt;
Im Forumsbeitrag [http://www.mikrocontroller.net/topic/168346#1613115] stellt Alex &lt;br /&gt;
seine Erweiterung dieser RC5-Routine vor, mit der eine Fallunterscheidung kurzer/langer Tastendruck möglich ist. Damit realisiert Alex auf einer Taste eine EIN/AUS-Funktion (kurzer Tastendruck) und eine DIMMEN-Funktion (langer Tastendruck).&lt;br /&gt;
&lt;br /&gt;
=== Klickidiklacki ===&lt;br /&gt;
&lt;br /&gt;
In der Diskussion [http://www.mikrocontroller.net/topic/160416#1526039] ist die Frage aufgetaucht, wie man die &#039;&#039;&#039;Compare Output Unit&#039;&#039;&#039; verwenden kann. Das dort angegebene Beispiel, die seltsame Simulation im AVR Studio und die Diskussion haben mich dazu geführt, folgendes Beispiel mit dem Attiny2313 zu erstellen. &lt;br /&gt;
&lt;br /&gt;
Das Programm ist komplett abgespeckt: Der Taster1 wirkt über eine Brücke Pin 10 nach Pin 30 an J4 als externer Taktgeber an Pin T0. Wenn die Anzahl der Tastendrücke gleich dem vorgegehenen Wert 5 in Register OCR0A ist, wird der Ausgang OC0A automatisch umgeschaltet. Da der Ausgang OC0A über eine Brücke an J4 mit der LED1 verbunden ist, wird die LED umgeschaltet.&lt;br /&gt;
&lt;br /&gt;
Die Besonderheit: Alles funktioniert automatisch per Hardware. Zu Programmbeginn wird einmal die Hardware eingestellt, und sie läuft dann autonom parallel zur Software. Die Software ist in diesem Fall eine leere Endlosschleife.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Beispiel: Compare Output Unit&lt;br /&gt;
&lt;br /&gt;
    Pollin Funk AVR Board&lt;br /&gt;
    Attiny2313 @ 8 MHz&lt;br /&gt;
&lt;br /&gt;
                         Attiny2313   J4 &lt;br /&gt;
    Taster1 active-high     PB1       Pin 10     --+&lt;br /&gt;
    LED1    active-high     PD5       Pin 31 --+   |&lt;br /&gt;
                                               |   | Brücke&lt;br /&gt;
    T0                      PD4       Pin 30   | --+&lt;br /&gt;
    OC0B                    PD5       Pin 31   |   &lt;br /&gt;
    OC0A                    PB2       Pin 11 --+  &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB2); // PB2 Ausgang OC0A&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // Compare Output Unit einstellen. S 74&lt;br /&gt;
  //&lt;br /&gt;
  // Vergleichswert&lt;br /&gt;
  OCR0A = 5 - 1; // 5x Taster drücken&lt;br /&gt;
  // Toggle OC0A bei TCNT0 == OCR0A&lt;br /&gt;
  TCCR0A |= (0 &amp;lt;&amp;lt; COM0A1) | (1 &amp;lt;&amp;lt; COM0A0);&lt;br /&gt;
  // CTC Mode&lt;br /&gt;
  TCCR0A |= (1 &amp;lt;&amp;lt; WGM01) | (0 &amp;lt;&amp;lt; WGM00);&lt;br /&gt;
  // Externe Clock an T0 bei steigender Flanke (Taster schliesst)&lt;br /&gt;
  TCCR0B |= (1 &amp;lt;&amp;lt; CS00) | (1 &amp;lt;&amp;lt; CS01) | (1 &amp;lt;&amp;lt; CS02);   &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;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;CTC-Modus&#039;&#039;&#039; bewirkt, dass beim zutreffenden Vergleich (TCNT0 == OCR0A) TCNT0 automatisch wieder auf 0 gesetzt wird. Leider kann dieser Modus mit dem anderen Ausgang OC0B nicht realisiert werden; es hätte sich in diesem Fall gerade angeboten, weil PD5 und OC0B bereits auf dem gleichen Pin liegen (Pin 31 an J4). Ohne den CTC-Modus für OC0B müsste man selbst beim Vergleich TCNT0 per Software zurücksetzen.&lt;br /&gt;
&lt;br /&gt;
Und bitte noch beachten: Es ist nur die seltsame Hardwareentprellung des Pollin-Boards vorhanden. Also nicht wundern, wenn die Zählerei mit dem Taster unzuverlässig ist. Wenn man jedoch eine nichtprellende Taktquelle an T0 benutzt, könnte man das Programm benutzen, um eine ankommende Frequenz z.&amp;amp;nbsp;B. hier durch 5 zu teilen (Bsp. 10 Hz auf 2 Hz runterteilen).&lt;br /&gt;
&lt;br /&gt;
=== AVR-GCC-Tutorial/Die Timer und Zähler des AVR ===&lt;br /&gt;
&lt;br /&gt;
Im folgenden ist das &#039;&#039;&#039;Beispiel für Compare Match Mode&#039;&#039;&#039; aus dem [[AVR-GCC-Tutorial/Die Timer und Zähler des AVR]] vom Atmega8 auf den Attiny2313 umgeschrieben. Die wesentlichen Änderungen sind mit ### markiert. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Anpassung des Timer-Beispiels aus dem AVR-GCC-Tutorial&lt;br /&gt;
   an den Attiny2313 @ 8MHz auf dem Pollin Funk AVR Board&lt;br /&gt;
&lt;br /&gt;
   Erweiterung: (Lauf-)Zeitausgabe über UART 9600/8N1&lt;br /&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;stdlib.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
//Variablen für die Zeit&lt;br /&gt;
volatile unsigned int  millisekunden=0;&lt;br /&gt;
volatile unsigned int  sekunde=0;&lt;br /&gt;
volatile unsigned int  minute=0;&lt;br /&gt;
volatile unsigned int  stunde=0;&lt;br /&gt;
&lt;br /&gt;
void uart_init(void)&lt;br /&gt;
{&lt;br /&gt;
  // 9600 Baud bei F_CPU 8 MHz: (Baudratenfehler = +0,2%)&lt;br /&gt;
  UBRRH = (uint8_t) ((F_CPU / (16 * 9600L) - 1) &amp;gt;&amp;gt; 8);&lt;br /&gt;
  UBRRL = (uint8_t)  (F_CPU / (16 * 9600L) - 1);&lt;br /&gt;
&lt;br /&gt;
  //      Empfangen,   Senden erlauben&lt;br /&gt;
  UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
  // Frame Format:              8 Bits,                  No Parity,          1 Stopbit&lt;br /&gt;
  UCSRC = ((1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0)) | ((0&amp;lt;&amp;lt;UPM1) | (0&amp;lt;&amp;lt;UPM0)) | (0&amp;lt;&amp;lt;USBS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int uart_putchar( char c )&lt;br /&gt;
{&lt;br /&gt;
  if( c == &#039;\n&#039; )&lt;br /&gt;
    uart_putchar( &#039;\r&#039; );&lt;br /&gt;
 &lt;br /&gt;
  loop_until_bit_is_set( UCSRA, UDRE );&lt;br /&gt;
  UDR = c;&lt;br /&gt;
  return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void uart_puts(char *s)&lt;br /&gt;
{&lt;br /&gt;
  while(*s)&lt;br /&gt;
    uart_putchar(*s++);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void zeitausgabe(void)&lt;br /&gt;
{&lt;br /&gt;
  static char last_sekunde = 255;&lt;br /&gt;
  char buffer[4];&lt;br /&gt;
&lt;br /&gt;
  if ((last_sekunde != sekunde)) &lt;br /&gt;
  {&lt;br /&gt;
    last_sekunde = sekunde;&lt;br /&gt;
    itoa(stunde+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;:&amp;quot;);&lt;br /&gt;
    itoa(minute+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;:&amp;quot;);&lt;br /&gt;
    itoa(sekunde+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
   uart_init();&lt;br /&gt;
&lt;br /&gt;
   //Timer 0 konfigurieren auf CTC-Modus Attiny2313&lt;br /&gt;
   TCCR0A = (1&amp;lt;&amp;lt;WGM01);            // ###&lt;br /&gt;
   TCCR0B = (1&amp;lt;&amp;lt;CS01) | (1&amp;lt;&amp;lt;CS00); // ### Prescaler 8*8 = 64 bei 8 MHz&lt;br /&gt;
   OCR0A=125-1;                    // ### Bugfix 20091221&lt;br /&gt;
 &lt;br /&gt;
   //Compare Interrupt aktivieren&lt;br /&gt;
   TIMSK|=(1&amp;lt;&amp;lt;OCIE0A);             // ###&lt;br /&gt;
&lt;br /&gt;
   //Globale Interrupts aktivieren&lt;br /&gt;
   sei();&lt;br /&gt;
&lt;br /&gt;
   while(1)&lt;br /&gt;
   {&lt;br /&gt;
     /*Hier kann die aktuelle Zeit ausgeben werden*/&lt;br /&gt;
     zeitausgabe();&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   return 0;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Der Compare Interrupt Handler&lt;br /&gt;
// Wird aufgerufen wenn TCNT0 = 125&lt;br /&gt;
ISR (TIMER0_COMPA_vect)            // ###&lt;br /&gt;
{&lt;br /&gt;
   millisekunden++;&lt;br /&gt;
   if(millisekunden==1000)&lt;br /&gt;
   {&lt;br /&gt;
      sekunde++;&lt;br /&gt;
      millisekunden=0;&lt;br /&gt;
      if(sekunde==60)&lt;br /&gt;
      {&lt;br /&gt;
         minute++;&lt;br /&gt;
         sekunde=0;&lt;br /&gt;
      }&lt;br /&gt;
      if(minute ==60)&lt;br /&gt;
      {&lt;br /&gt;
        stunde++;&lt;br /&gt;
        minute=0;&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;
Der Bequemlichkeit halber ist eine (Lauf-)Zeitanzeige per UART (9600 Baud  8 Datenbits, 1 Stoppbit, keine Parität) hinzugefügt: So lässt sich das Programm als sekunden&amp;lt;strike&amp;gt;genaue&amp;lt;/strike&amp;gt;aufgelöste Stoppuhr benutzen (RESET-Taster = Neustart).&lt;br /&gt;
&lt;br /&gt;
Noch ein Nachtrag zu diesem Beispiel: &lt;br /&gt;
&lt;br /&gt;
Die ISR und das Anwendungsprogramm (genauer die Routine &#039;&#039;zeitausgabe()&#039;&#039;) benutzen gemeinsame Variablen, die auch korrekt als &#039;&#039;&#039;volatile&#039;&#039;&#039; gekennzeichnet sind. &lt;br /&gt;
&lt;br /&gt;
Dennoch versteckt in diesem Programm ein potenzieller Bug: Der Datenzugriff ist nicht &#039;&#039;&#039;atomar&#039;&#039;&#039;! Der Bug macht sich bloß nicht bemerkbar, weil die &#039;&#039;zeitausgabe&#039;&#039; weit weniger als eine Sekunde braucht, um die Daten auszugeben. &lt;br /&gt;
&lt;br /&gt;
Angenommen &#039;&#039;zeitausgabe()&#039;&#039; braucht länger, was könnte passieren? Die gemeinsamen Variablen vom Typ unsigned int sind größer 1 Byte bzw. der kompletten Zeitstempel &#039;&#039;stunde:minute:sekunde&#039;&#039; ist wesentlich größer als 1 Byte. Eine Leseoperation bzw. die Ausgabe benötigt also &#039;&#039;&#039;mehrere&#039;&#039;&#039; Maschinenanweisungen bzw. sogar C-Anweisungen. Ein Interrupt könnte diese Anweisugen z.&amp;amp;nbsp;B. der beiden Bytes von unsigned int &#039;&#039;sekunde&#039;&#039; unterbrechen und ggf. das noch nicht gelesene Byte ändern. Oder &#039;&#039;minute&#039;&#039; sei bereits über UART ausgegeben, dann vor der Ausgabe von &#039;&#039;sekunde&#039;&#039; unterbricht der Timer und erhöht &#039;&#039;sekunde&#039;&#039;. Wenn &#039;&#039;sekunde&#039;&#039; bereits 59 war, wird &#039;&#039;minute&#039;&#039; erhöht und sekunde auf 0 gesetzt. &#039;&#039;minute&#039;&#039; war aber bereits ausgegeben... die Anzeige ist dann falsch! &lt;br /&gt;
&lt;br /&gt;
Als Ausweg bietet die avr-libc Hilfsmittel zur Sicherstellung des atomaren Datenzugriffes. Mit Hilfe des Makros ATOMIC_BLOCK können  Interrupts gesperrt werden, wenn die Anweisungen im {}-Block ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;util/atomic.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void zeitausgabe(void)&lt;br /&gt;
{&lt;br /&gt;
  static char last_sekunde = 255;&lt;br /&gt;
  char buffer[4];&lt;br /&gt;
  unsigned int tmp_stunde;&lt;br /&gt;
  unsigned int tmp_minute;&lt;br /&gt;
  unsigned int tmp_sekunde;&lt;br /&gt;
&lt;br /&gt;
  // Übersetzung mit der Compileroption &lt;br /&gt;
  // -std=c99 oder -std=gnu99 erforderlich&lt;br /&gt;
  ATOMIC_BLOCK(ATOMIC_FORCEON)&lt;br /&gt;
  {&lt;br /&gt;
    // Schnappschuss anfertigen&lt;br /&gt;
    // Timerinterrupt ist gesperrt!&lt;br /&gt;
    // Diese Anweisungen knapp halten!&lt;br /&gt;
    tmp_stunde = stunde;&lt;br /&gt;
    tmp_minute = minute;&lt;br /&gt;
    tmp_sekunde = sekunde;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Schnappschuss aufarbeiten&lt;br /&gt;
  if ((last_sekunde != tmp_sekunde)) &lt;br /&gt;
  {&lt;br /&gt;
    last_sekunde = tmp_sekunde;&lt;br /&gt;
    itoa(tmp_stunde+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;:&amp;quot;);&lt;br /&gt;
    itoa(tmp_minute+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;:&amp;quot;);&lt;br /&gt;
    itoa(tmp_sekunde+100, buffer,10);&lt;br /&gt;
    uart_puts(buffer+1);&lt;br /&gt;
    uart_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiteres kann man dazu im Artikel [[Interrupt]] und [[AVR-GCC-Tutorial#Variablen_gr.C3.B6.C3.9Fer_1_Byte| AVR-GCC-Tutorial - Variablen größer 1 Byte]] und in der Dokumentation der avr-libc nachlesen.&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Scotty, Handbremse!&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Die Frage in [http://www.mikrocontroller.net/topic/164079#1565907] hat mich neugierig gemacht, wie das &#039;&#039;&#039;Clock Prescale Register CLKPR&#039;&#039;&#039; arbeitet. Achtung: Bei großen Clock Prescale Werten vorher unbedingt [http://www.mikrocontroller.net/articles/AVR_Fuses#Reaktivieren_beim_CLKPR-Problem_.28Attiny13.29 dies] lesen.&lt;br /&gt;
&lt;br /&gt;
Im AVR Studio (4.12 SP2) konnte im Originalprogramm beim ATtiny13 als auch später beim ATtiny2313 in der Simulation (Debugger) kein Unterschied festgestellt werden egal ob mit oder ohne &#039;&#039;&#039;Clock Division Factor&#039;&#039;&#039; simuliert wurde.&lt;br /&gt;
&lt;br /&gt;
Daraufhin wurde das Originalprogramm auf das Pollin Funk AVR Board &amp;quot;angepasst&amp;quot;, d.h. LED1 und LED2 blinken wechselweise und der Taster dient als Eingabe: &lt;br /&gt;
* Taster loslassen und RESET ausführen, und das Programm arbeitet ohne Clock Division Factor&lt;br /&gt;
* Taster gedrückt halten und RESET ausführen, und das Programm arbeitet mit Clock Division Factor. Sobald die LEDs blinken, kann der Taster losgelassen werden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
; http://www.mikrocontroller.net/topic/164079#1565907&lt;br /&gt;
; Blink2.asm Blinker mit Unterprogramm (aus Franzis Lernpaket)&lt;br /&gt;
; modifiziert 20100122 für Attiny2313 @ 8 MHz auf Pollin Funk AVR Hardware&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;tn2313def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
      rjmp Anfang&lt;br /&gt;
&lt;br /&gt;
Anfang:&lt;br /&gt;
; Stack einrichten weil rcall/ret verwendet wird&lt;br /&gt;
; Nicht nötig bei ATtiny13 (Originalcode)&lt;br /&gt;
; Aber nötig bei ATtiny2313&lt;br /&gt;
      ldi r16,LOW(RAMEND)&lt;br /&gt;
      out SPL,r16&lt;br /&gt;
&lt;br /&gt;
; Speed Divider Set&lt;br /&gt;
      IN r16,PINB&lt;br /&gt;
      SBRC r16,PINB1     ; Taster losgelassen =&amp;gt; kein Nurhalbekraft&lt;br /&gt;
      rcall Nurhalbekraft&lt;br /&gt;
&lt;br /&gt;
; Ports initialisieren&lt;br /&gt;
      ldi r16,((1&amp;lt;&amp;lt;PORTD6)|(1&amp;lt;&amp;lt;PORTD5))&lt;br /&gt;
      out DDRD,r16       ; als Ausgang einrichten, Rest Eingang&lt;br /&gt;
&lt;br /&gt;
; Endlose Arbeitsschleife: Wechselbinkler mit LED1 und LED2&lt;br /&gt;
L_Schleife:&lt;br /&gt;
      ldi r16,(1&amp;lt;&amp;lt;PORTD5)&lt;br /&gt;
      out PORTD,r16&lt;br /&gt;
      rcall Warten&lt;br /&gt;
      ldi r16,(1&amp;lt;&amp;lt;PORTD6)&lt;br /&gt;
      out PORTD,r16&lt;br /&gt;
      rcall Warten&lt;br /&gt;
      rjmp L_Schleife&lt;br /&gt;
&lt;br /&gt;
; Hilfsroutinen&lt;br /&gt;
&lt;br /&gt;
; Speed Divider Set&lt;br /&gt;
Nurhalbekraft:&lt;br /&gt;
      in r17, SREG        ; Statusregister in Hilfsregister retten&lt;br /&gt;
      cli                 ; Global Interrupts disablen&lt;br /&gt;
      ldi r16,(1&amp;lt;&amp;lt;CLKPCE) ; 1, Clock Prescaler Change Enable&lt;br /&gt;
      out CLKPR,r16       ; 1, Clock Prescale Register &amp;quot;öffnen&amp;quot;&lt;br /&gt;
      ldi r16,(1&amp;lt;&amp;lt;CLKPS0) ; 1, Sollwert Clock Division Factor 2&lt;br /&gt;
      out CLKPR,r16       ; 1, Clock Prescale Register &amp;quot;beschreiben&amp;quot;&lt;br /&gt;
      ; max. 4 Takte Regel ist eingehalten.&lt;br /&gt;
      out SREG, r17       ; Statusregister aus Hilfsregister restaurieren&lt;br /&gt;
      ret                 ; zurück zum Aufrufer (rcall)&lt;br /&gt;
&lt;br /&gt;
; Warten&lt;br /&gt;
; ca. 150ms bei 8 MHz&lt;br /&gt;
Warten:&lt;br /&gt;
      Ldi   r18,8*10&lt;br /&gt;
L0_Warten:&lt;br /&gt;
      Ldi   r16,20&lt;br /&gt;
L1_Warten:                ;äußere Schleife&lt;br /&gt;
      Ldi   r17,250&lt;br /&gt;
L2_Warten:                ;innere Schleife&lt;br /&gt;
      dec   r17&lt;br /&gt;
      brne  L2_Warten&lt;br /&gt;
      dec   r16&lt;br /&gt;
      brne  L1_Warten&lt;br /&gt;
      dec   r18&lt;br /&gt;
      brne  L0_Warten&lt;br /&gt;
      ret&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ergebnis: Der Clock Division Factor ist auf der Hardware aktiv bzw. der Simulator (bei meinem AVR Studio) ist fehlerhaft. Probieren geht über Studieren (Simulieren) :)&lt;br /&gt;
&lt;br /&gt;
=== Tasty Reloaded ===&lt;br /&gt;
&lt;br /&gt;
Peter Dannegger hat in [http://www.mikrocontroller.net/topic/164194#1566921 &amp;quot;Entprellen für Anfänger&amp;quot;] eine neue vereinfachte Entprellroutine vorgestellt. Hier ein Minimalbeispiel in dem dieses Verfahren verwendet wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Target: Attiny2313 @ 8 MHz Pollin Funk AVR Board&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU 8e6   // ###&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/****************************************************/&lt;br /&gt;
/*                                                  */&lt;br /&gt;
/*  Not so powerful Debouncing Macro                */&lt;br /&gt;
/*  No Interrupt needed                             */&lt;br /&gt;
/*                                                  */&lt;br /&gt;
/*  Author: Peter Dannegger                         */&lt;br /&gt;
/*                                                  */&lt;br /&gt;
/****************************************************/&lt;br /&gt;
&lt;br /&gt;
// Modification for active high push button&lt;br /&gt;
&lt;br /&gt;
#define debounce( port, pin )                                        \&lt;br /&gt;
({                                                                   \&lt;br /&gt;
  static uint8_t flag = 0; /* new variable on every macro usage */   \&lt;br /&gt;
  uint8_t i = 0;                                                     \&lt;br /&gt;
                                                                     \&lt;br /&gt;
  if( flag ){                  /* check for key release: */          \&lt;br /&gt;
    for(;;){                   /* loop... */                         \&lt;br /&gt;
      if( (port &amp;amp; 1&amp;lt;&amp;lt;pin) ){   /* ... until key pressed or ... */    \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                      \&lt;br /&gt;
        break;                                                       \&lt;br /&gt;
      }                                                              \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                    \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms released */    \&lt;br /&gt;
        flag = 0;              /* clear press flag */                \&lt;br /&gt;
        i = 0;                 /* 0 = key release debounced */       \&lt;br /&gt;
        break;                                                       \&lt;br /&gt;
      }                                                              \&lt;br /&gt;
    }                                                                \&lt;br /&gt;
  }else{                       /* else check for key press: */       \&lt;br /&gt;
    for(;;){                   /* loop ... */                        \&lt;br /&gt;
      if( !(port &amp;amp; 1&amp;lt;&amp;lt;pin) ){  /* ... until key released or ... */   \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                      \&lt;br /&gt;
        break;                                                       \&lt;br /&gt;
      }                                                              \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                    \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms pressed */     \&lt;br /&gt;
        flag = 1;              /* set press flag */                  \&lt;br /&gt;
        i = 1;                 /* 1 = key press debounced */         \&lt;br /&gt;
        break;                                                       \&lt;br /&gt;
      }                                                              \ &lt;br /&gt;
    }                                                                \&lt;br /&gt;
  }                                                                  \&lt;br /&gt;
  i;                           /* return value of Macro */           \&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
  Debouncing Example&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  // push button @ PB1 in active high configuration&lt;br /&gt;
  DDRB &amp;amp;= ~(1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
  // PORTB |= 1&amp;lt;&amp;lt;PB1; // not needed because active high&lt;br /&gt;
&lt;br /&gt;
  // LED @ PD5 in active high configuration&lt;br /&gt;
  DDRD |=   1&amp;lt;&amp;lt;PD5;&lt;br /&gt;
&lt;br /&gt;
  for(;;){&lt;br /&gt;
    if( debounce( PINB, PB1 ) )&lt;br /&gt;
      PORTD ^= 1&amp;lt;&amp;lt;PD5; // Toggle LED&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Codebedarf für die debounce()-Teilfunktion liegt bei meinem System bei nur 54 Bytes und der SRAM Bedarf bei 1 Byte (WinAVR-20071221, -Os).&lt;br /&gt;
&lt;br /&gt;
Peter schreibt: &#039;&#039;&amp;quot;Man kann sogar das Programm so ändern, daß beide Flanken zurückgegeben werden (als 1 bzw. 2), dazu muß nur eine Zuweisung auf &amp;quot;i = 2;&amp;quot; geändert werden.&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Änderung betrifft diesen Teil, bei dem statt&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt; &lt;br /&gt;
        i = 0;                 /* 0 = key release debounced */       \&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zu schreiben wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
        i = 2;                 /* 2 = key release debounced */       \&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann schaltet im Beispiel die LED beim Drücken und Loslassen um!&lt;br /&gt;
&lt;br /&gt;
=== Blinky Reloaded ===&lt;br /&gt;
&lt;br /&gt;
Die Frage von Markus und die Grundroutine in [http://www.mikrocontroller.net/topic/165543#1581129] zum Tutorial [[LED-Fading]] von Falk bringt mich dazu, das mit dem Attiny2313 auch mal zu probieren. &lt;br /&gt;
&lt;br /&gt;
Im folgenden Quelltext werden die LED1 und LED2 des Pollinboards langsam und wechselseitig auf- und abgeblendet. Damit die 8-Bit PWM Signale zu den LED gelangen können, wird eine Drahtbrücke zwischen Pin 12 und Pin 32 an der 40-poligen Jumperleiste J4 angebracht. &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;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    F_CPU = 8 MHz&lt;br /&gt;
    Compileroption -Os&lt;br /&gt;
&lt;br /&gt;
    Attiny2313     POLLIN FUNK AVR   BRÜCKE&lt;br /&gt;
    =======================================&lt;br /&gt;
    PB2   OC0A                11 an J4&lt;br /&gt;
    PD5   OC0B    PD5/LED2    31 an J4&lt;br /&gt;
    PB3   OC1A                12 an J4 --+&lt;br /&gt;
    PB4   OC1B                13 an J4   |&lt;br /&gt;
                  PD6/LED1    32 an J4 --+&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#if 0&lt;br /&gt;
// Beispiel mit linearem Verlauf der PWM Werte&lt;br /&gt;
unsigned char pwmtable_8D[32] = {&lt;br /&gt;
   0*8,  1*8,  2*8,  3*8,  4*8,  5*8,  6*8,  7*8,&lt;br /&gt;
   8*8,  9*8, 10*8, 11*8, 12*8, 13*8, 14*8, 15*8,&lt;br /&gt;
  16*8, 17*8, 18*8, 19*8, 20*8, 21*8, 22*8, 23*8,&lt;br /&gt;
  24*8, 25*8, 26*8, 27*8, 28*8, 29*8, 30*8, 31*8 &lt;br /&gt;
};&lt;br /&gt;
#endif &lt;br /&gt;
&lt;br /&gt;
// Beispiel mit nichtlinearem Verlauf der PWM Werte&lt;br /&gt;
unsigned char pwmtable_8D[32] = {&lt;br /&gt;
0, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 10, 11, &lt;br /&gt;
13, 16, 19, 23, 27, 32, 38, 45, 54, 64, 76,&lt;br /&gt;
91, 108, 128, 152, 181, 215, 255&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
void init_fade(void) &lt;br /&gt;
{&lt;br /&gt;
  DDRB = (1&amp;lt;&amp;lt;PB2) | (1&amp;lt;&amp;lt;PB3) | (1&amp;lt;&amp;lt;PB4);&lt;br /&gt;
  DDRD = (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
&lt;br /&gt;
  // 8-Bit Timer0 &lt;br /&gt;
  // Clear OC0A/OC0B on compare-match when up counting&lt;br /&gt;
  // Set OC0A/OC0B on compare-match on TOP&lt;br /&gt;
  TCCR0A = (1&amp;lt;&amp;lt;COM0A1) | (1&amp;lt;&amp;lt;COM0B1) | (1&amp;lt;&amp;lt;WGM01) | (1&amp;lt;&amp;lt;WGM00); // 0xA3 Mode 3 Fast-PWM TOP=0xFF&lt;br /&gt;
  TCCR0B = (1&amp;lt;&amp;lt;CS02);  // precaler 256 -&amp;gt; ~122 Hz PWM frequency &lt;br /&gt;
&lt;br /&gt;
  // 16-Bit Timer1&lt;br /&gt;
  // Clear OC0A/OC0B on compare-match&lt;br /&gt;
  // Set OC0A/OC0B on compare-match on TOP (=0xFF)&lt;br /&gt;
  TCCR1A = (1&amp;lt;&amp;lt;COM1A1) | (1&amp;lt;&amp;lt;COM1B1) | (1&amp;lt;&amp;lt;WGM10); // // 0xA1&lt;br /&gt;
  TCCR1B = (1&amp;lt;&amp;lt;WGM12);  // 0x08 Mode 5 non-inverted PWM on OC1A, 8 Bit Fast PWM&lt;br /&gt;
  TCCR1B |= (1&amp;lt;&amp;lt;CS02);  // precaler 256 -&amp;gt; ~122 Hz PWM frequency &lt;br /&gt;
} &lt;br /&gt;
&lt;br /&gt;
void fade(void) &lt;br /&gt;
{&lt;br /&gt;
  unsigned char tmp;&lt;br /&gt;
&lt;br /&gt;
  for(tmp=0; tmp&amp;lt;=31; tmp++){&lt;br /&gt;
    OCR0B = *(pwmtable_8D+31-tmp); // LED2 ausfaden&lt;br /&gt;
    OCR1A = *(pwmtable_8D+tmp);    // gleichzeitig LED1 einfaden&lt;br /&gt;
    _delay_ms(50);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  _delay_ms(1000);&lt;br /&gt;
    &lt;br /&gt;
  for(tmp=0; tmp&amp;lt;=31; tmp++){&lt;br /&gt;
    OCR0B = *(pwmtable_8D+tmp);    //LED2 wieder einfaden&lt;br /&gt;
    OCR1A = *(pwmtable_8D+31-tmp); //LED1 ausfaden&lt;br /&gt;
    _delay_ms(50);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  _delay_ms(1000);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  init_fade();&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    fade();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aufgepasst? Im Quelltext sind pro Timer &#039;&#039;&#039;zwei&#039;&#039;&#039; PWMs aktiviert, aber nur je eine wird für LED1 und LED2 benutzt. Die doppelte Aktivierung ist natürlich nicht notwendig, sondern das ist eine Vorschau auf später, weil Markus sein Programm auf vier LEDs erweitern will.&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Dreh&#039; am Rad&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
Seltsamerweise boomen derzeit (2/2010) die [[ADC]]-Themen im Forum. Da konnte ich mich nicht zurückhalten. Das muss ich auch probieren und bin wieder zurück auf den &#039;&#039;&#039;Atmega8&#039;&#039;&#039; gewechselt, weil der Attiny2313 keinen ADC besitzt.&lt;br /&gt;
&lt;br /&gt;
Für dieses ADC Beispiel braucht man einen verstellbaren Widerstand ([http://de.wikipedia.org/wiki/Potentiometer Potentiometer (Wikipedia)]). Dessen Schleiferbahn wird zwischen der Versorgungsspannung Vcc und GND angeschlossen. Der Schleifkontakt wird mit ADC2 (PC2) am Atmega8 verbunden. Das Potentiometer wirkt damit als [[Spannungsteiler]], wobei R1+R2 aus dem Artikel der Gesamtwiderstand des Potentiometers sind und die Stellung des Schleifkontakts die Widerstände R1 und R2 verändert. Ich habe ein 10K lineares Potentiometer aus der Bastelkiste benutzt.&lt;br /&gt;
&lt;br /&gt;
Im Programm wird die Ausgangsspannung des Spannungsteilers gegen die Referenzspannung AVcc gemessen und als 10-Bit ADC-Wert digitalisiert (angepasste Funktion ReadChannel() aus dem [[AVR-GCC-Tutorial]]). &lt;br /&gt;
&lt;br /&gt;
Als Schmankerl wird der ADC-Wert auch benutzt, um einen Stufenschalter nachzubilden. Diese Anregung habe ich beim Stöbern im Zeitschriftenladen bekommen. In der Zeitschrift &#039;&#039;Funkamateur&#039;&#039; 2/2010 beschreibt &#039;&#039;DH1LD&#039;&#039; sein Projekt &amp;quot;Virtueller Stufenschalter für den Mikrocontroller&amp;quot;, geschrieben in BASCOM-AVR.&lt;br /&gt;
&lt;br /&gt;
Als Anzeige gibt es eine einfache Hardwareanzeige: Die LED2 blinkt umgekehrt proportional zum ADC-Wert (kleiner Wert = schnellstes Blinken).  Und es gibt zwei Optionen für die UART-Anzeige an einem Terminal (9600/8N1 Einstellung): Eine Ziffernanzeige und eine &amp;quot;billige&amp;quot; Balkenanzeige.&lt;br /&gt;
&lt;br /&gt;
Das Projekt besteht aus den folgenden drei Quelltexten. Die UART-Routinen wurden bereits beim Zähler mit RS232 benutzt. &lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;PFA_ADC_10K.c&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    ADC-Beispiel&lt;br /&gt;
&lt;br /&gt;
    Pollin Funk AVR Board&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Boardeigene Hardware&lt;br /&gt;
    ====================&lt;br /&gt;
    LED2&lt;br /&gt;
    UART 9600/8N1&lt;br /&gt;
&lt;br /&gt;
    Zusätzliche Hardware:&lt;br /&gt;
    ====================&lt;br /&gt;
    ca. 10K lineares Potentiometer &lt;br /&gt;
    Schleiferbahn zwischen Vcc und GND &lt;br /&gt;
    Schleiferkontakt zu ADC2 (= PC2)&lt;br /&gt;
&lt;br /&gt;
    Bugfix 20100212&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;       // utoa()&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;       // strlen()&lt;br /&gt;
#include &amp;quot;PFA_uart_m8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   ADC &lt;br /&gt;
   Single Conversion Modus&lt;br /&gt;
   Vref == AVcc&lt;br /&gt;
   für F_CPU 12 MHz angepasst&lt;br /&gt;
   Arithm. Mittelwert aus 4 Einzelmessung&lt;br /&gt;
 */&lt;br /&gt;
uint16_t ReadChannel(uint8_t mux)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t result;&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
  mux |= (1&amp;lt;&amp;lt;REFS0);   // AVCC als Referenz         ###&lt;br /&gt;
  ADMUX = mux;         // Referenz und Kanal setzen ###&lt;br /&gt;
&lt;br /&gt;
  /* &lt;br /&gt;
     Frequenzvorteiler setzen und ADC enablen&lt;br /&gt;
&lt;br /&gt;
     ADC soll optimal mit 50 kHz bis 200 kHz laufen&lt;br /&gt;
     12000000/200000 = 60&lt;br /&gt;
     12000000/50000  = 240&lt;br /&gt;
     =&amp;gt; Vorteiler &lt;br /&gt;
     64   (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1)&lt;br /&gt;
     128  (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
   */&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1);  // ###&lt;br /&gt;
 &lt;br /&gt;
  /* &lt;br /&gt;
     Nach dem Wechsel der ADC Referenz (oder dem Wechsel eines &lt;br /&gt;
     differentiellen Kanals) wird ein &amp;quot;Dummy-Readout&amp;quot; empfohlen. &lt;br /&gt;
     Man ermittelt also einen ungenauen ADC Wert und verwirft diesen. &lt;br /&gt;
&lt;br /&gt;
     Auch wenn es sich hier um eine Dummy_ADC_Wandlung handelt, muss&lt;br /&gt;
     das ADCW Register anschliessend gelesen werden, sonst wird das&lt;br /&gt;
     Ergebnis der eigentlichen Messung (s.u.) nicht übernommen!&lt;br /&gt;
   */&lt;br /&gt;
  ADCSRA |= (1&amp;lt;&amp;lt;ADSC); // eine Wandlung &amp;quot;single conversion&amp;quot; auslösen&lt;br /&gt;
  while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) &lt;br /&gt;
  {&lt;br /&gt;
    ; // auf den Abschluss der Konvertierung warten &lt;br /&gt;
  }&lt;br /&gt;
  result = ADCW;  &lt;br /&gt;
 &lt;br /&gt;
  /* &lt;br /&gt;
     Eigentliche Messung &lt;br /&gt;
     Mittelwert aus 4 aufeinanderfolgenden Wandlungen &lt;br /&gt;
   */&lt;br /&gt;
  result = 0; &lt;br /&gt;
  for( i=0; i&amp;lt;4; i++ )&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);  // eine Wandlung &amp;quot;single conversion&amp;quot; auslösen&lt;br /&gt;
    while ( ADCSRA &amp;amp; (1&amp;lt;&amp;lt;ADSC) ) &lt;br /&gt;
    {&lt;br /&gt;
      ; // auf den Abschluss der Konvertierung warten &lt;br /&gt;
    }&lt;br /&gt;
    result += ADCW;		    // Wandlungsergebnisse aufaddieren&lt;br /&gt;
  }&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN);   // ADC disablen (spart Strom)&lt;br /&gt;
  result /= 4;            // arithm. Mittelwert bilden&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Stufenschalter für ADC Werte&lt;br /&gt;
   &lt;br /&gt;
   Inspiriert durch:&lt;br /&gt;
   DH1LD&lt;br /&gt;
   &amp;quot;Virtueller Stufenschalter für den Mikrocontroller&amp;quot;&lt;br /&gt;
   BASCOM-AVR Routine&lt;br /&gt;
   Funkamateur 2/2009&lt;br /&gt;
 */&lt;br /&gt;
#define MESSBEREICH    1024  /* 0..1023 */&lt;br /&gt;
#define STUFENZAHL     11    /* 1..STUFENZAHL :) */&lt;br /&gt;
#define HYSTERESE      (5*(MESSBEREICH/STUFENZAHL)/100) /* ca. 5% */&lt;br /&gt;
#define STUFE(a)       ((STUFENZAHL*(a)+MESSBEREICH)/MESSBEREICH)&lt;br /&gt;
&lt;br /&gt;
uint8_t stufenschalter(uint16_t adcw)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_s = 0;&lt;br /&gt;
  uint8_t tmp_s = 0;&lt;br /&gt;
&lt;br /&gt;
  // Vorläufige Stufe berechnen&lt;br /&gt;
  tmp_s = STUFE(adcw);&lt;br /&gt;
&lt;br /&gt;
  // Korrektur für Hysterese&lt;br /&gt;
  if ((tmp_s-last_s == 1) &amp;amp;&amp;amp; (adcw &amp;gt; HYSTERESE))&lt;br /&gt;
  {&lt;br /&gt;
    tmp_s = STUFE(adcw - HYSTERESE);&lt;br /&gt;
  }&lt;br /&gt;
  else if ((tmp_s-last_s == -1) &amp;amp;&amp;amp; (adcw &amp;lt; MESSBEREICH-HYSTERESE))&lt;br /&gt;
  {&lt;br /&gt;
    tmp_s = STUFE(adcw + HYSTERESE);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  last_s = tmp_s;&lt;br /&gt;
  return last_s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Zahlenwerte über UART ausgeben&lt;br /&gt;
   Leichte Pufferung um die Ausgabemenge rel. klein zu halten&lt;br /&gt;
 */&lt;br /&gt;
void werte_anzeigen(uint16_t adcval, uint8_t schalterpos)&lt;br /&gt;
{&lt;br /&gt;
  static uint16_t last_adcval = 0xFFFF;&lt;br /&gt;
  static uint8_t last_schalterpos = 0xFF;&lt;br /&gt;
  uint8_t crlf = 0;&lt;br /&gt;
  char buffer[5];&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  if (last_adcval != adcval)&lt;br /&gt;
  {&lt;br /&gt;
    // Neuen ADC-Wert mit vier Stellen ausgeben&lt;br /&gt;
    last_adcval = adcval;&lt;br /&gt;
    UART_puts(&amp;quot;ADC=[&amp;quot;);&lt;br /&gt;
    utoa(adcval, buffer, 10);&lt;br /&gt;
    for (i=4-strlen(buffer); i; i--)&lt;br /&gt;
      UART_putchar(&#039; &#039;);&lt;br /&gt;
    UART_puts(buffer);&lt;br /&gt;
    UART_putchar(&#039;]&#039;);&lt;br /&gt;
    crlf = 1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (last_schalterpos != schalterpos)&lt;br /&gt;
  {&lt;br /&gt;
    // Neue Schalterposition mit zwei Stellen ausgeben&lt;br /&gt;
    last_schalterpos = schalterpos;&lt;br /&gt;
    UART_puts(&amp;quot;  Schalter=[&amp;quot;);&lt;br /&gt;
    utoa(schalterpos, buffer, 10);&lt;br /&gt;
    for (i=2-strlen(buffer); i; i--)&lt;br /&gt;
      UART_putchar(&#039; &#039;);&lt;br /&gt;
    UART_puts(buffer);&lt;br /&gt;
    UART_putchar(&#039;]&#039;);&lt;br /&gt;
    crlf = 1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (crlf)&lt;br /&gt;
    UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Schalterposition als horiz. Balken &lt;br /&gt;
   über UART ausgeben&lt;br /&gt;
 */&lt;br /&gt;
void balkenanzeige(uint8_t schalterpos)&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t last_schalterpos = 0xFF;&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  if ( last_schalterpos != schalterpos )&lt;br /&gt;
  {&lt;br /&gt;
    UART_puts(&amp;quot;\r|&amp;quot;);&lt;br /&gt;
    for(i=0; i&amp;lt;schalterpos; i++)&lt;br /&gt;
      UART_puts(&amp;quot;###|&amp;quot;);&lt;br /&gt;
    for(i=schalterpos; i&amp;lt;STUFENZAHL; i++)&lt;br /&gt;
      UART_puts(&amp;quot;---|&amp;quot;);&lt;br /&gt;
    last_schalterpos = schalterpos;&lt;br /&gt;
    UART_putchar(&#039;\r&#039;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
  uint8_t schalterpos;&lt;br /&gt;
  uint16_t i;&lt;br /&gt;
&lt;br /&gt;
  UART_init();&lt;br /&gt;
  UART_putchar(&#039;\014&#039;); // FORMFEED&lt;br /&gt;
  UART_puts(&amp;quot;PFA ADC Beispiel\r\n\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  DDRD = (1&amp;lt;&amp;lt;PD5); // PD5 Ausgang für LED2&lt;br /&gt;
&lt;br /&gt;
  for(i=0;;i++)&lt;br /&gt;
  {&lt;br /&gt;
    adcval = ReadChannel(2); &lt;br /&gt;
    schalterpos = stufenschalter(adcval);&lt;br /&gt;
&lt;br /&gt;
#if 0&lt;br /&gt;
    /* UART-Anzeige Option 1 */&lt;br /&gt;
    werte_anzeigen(adcval, schalterpos); &lt;br /&gt;
#else&lt;br /&gt;
    /* UART-Anzeige Option 2 */&lt;br /&gt;
    balkenanzeige(schalterpos);          &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
    /*&lt;br /&gt;
       LED2 blinken lassen&lt;br /&gt;
       Blinkfrequenz umgekehrt proportional zum ADC-Wert &lt;br /&gt;
     */&lt;br /&gt;
    if (i &amp;gt;= adcval)&lt;br /&gt;
    {&lt;br /&gt;
      i = 0;&lt;br /&gt;
      PORTD ^= (1&amp;lt;&amp;lt;PD5); // Toggle LED2 mit XOR&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei &#039;&#039;&#039;PFA_uart_m8.c&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    UART-Routinen für Pollin Funk AVR Board&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    9600/8N1&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;PFA_uart_m8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void UART_init(void)&lt;br /&gt;
{&lt;br /&gt;
    // Bausrate setzen&lt;br /&gt;
    UBRRH = (uint8_t) ((F_CPU / (16 * BAUD) - 1) &amp;gt;&amp;gt; 8);&lt;br /&gt;
    UBRRL = (uint8_t)  (F_CPU / (16 * BAUD) - 1);&lt;br /&gt;
&lt;br /&gt;
    //      Empfangen,   Senden erlauben&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&lt;br /&gt;
    // Frame Format:              8 Bits,                  No Parity,          1 Stopbit&lt;br /&gt;
    UCSRC = (1&amp;lt;&amp;lt;URSEL) | ((1&amp;lt;&amp;lt;UCSZ1) | (1&amp;lt;&amp;lt;UCSZ0)) | ((0&amp;lt;&amp;lt;UPM1) | (0&amp;lt;&amp;lt;UPM0)) | (0&amp;lt;&amp;lt;USBS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // sofort zurückkehren und Zustand melden&lt;br /&gt;
    return UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Eingang voll&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;RXC)) )&lt;br /&gt;
        ;&lt;br /&gt;
    return UDR;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z)&lt;br /&gt;
{&lt;br /&gt;
    // Warte bis UART Ausgang frei&lt;br /&gt;
    while ( !(UCSRA &amp;amp; (1&amp;lt;&amp;lt;UDRE)) )&lt;br /&gt;
        ;&lt;br /&gt;
    UDR = z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s)&lt;br /&gt;
{&lt;br /&gt;
    while ( *s )&lt;br /&gt;
    {&lt;br /&gt;
        UART_putchar(*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;
Datei &#039;&#039;&#039;PFA_uart_m8.h&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    UART-Routinen für Pollin Funk AVR Board&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    9600/8N1&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#ifndef PFA_UART_M8_H&lt;br /&gt;
#define PFA_UART_M8_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// 9600 Baud bei F_CPU 12 MHz =&amp;gt; Baudratenfehler = +0,2%&lt;br /&gt;
#define BAUD 9600L&lt;br /&gt;
&lt;br /&gt;
void UART_init(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang prüfen&lt;br /&gt;
uint8_t UART_peekchar(void);&lt;br /&gt;
&lt;br /&gt;
// Auf Zeichen im UART Eingang warten&lt;br /&gt;
uint8_t UART_getchar(void);&lt;br /&gt;
&lt;br /&gt;
// Zeichen in UART Ausgang geben&lt;br /&gt;
void UART_putchar(char z);&lt;br /&gt;
&lt;br /&gt;
// Zeichenkette (String) in UART Ausgang geben&lt;br /&gt;
void UART_puts(char *s);&lt;br /&gt;
&lt;br /&gt;
#endif /* PFA_UART_M8_H */&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Die Versorgung muss stimmen ===&lt;br /&gt;
&lt;br /&gt;
Mit dem ADC kann man auch ohne zusätzliche Schaltung eine unbekannte bzw. im einfachen Batteriebetrieb fallende Versorgungsspannung Vcc (AVcc) messen, Dazu wird die zumessende Versorgungsspannung Vcc als Referenz Aref des ADC eingestellt und damit wird die bekannte Spannung der internen Bandgapreferenz Vbg gemessen. Dann wird gerechnet...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    ADC-Beispiel #2: &lt;br /&gt;
    Messung der Vcc mit Hilfe der internen Bandgap-Refernez&lt;br /&gt;
&lt;br /&gt;
    Pollin Fun AVR Board&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Boardeigene Hardware&lt;br /&gt;
    ====================&lt;br /&gt;
    UART 9600/8N1&lt;br /&gt;
&lt;br /&gt;
    Zusätzliche Hardware:&lt;br /&gt;
    ====================&lt;br /&gt;
    -&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;       // utoa()&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;       // strlen()&lt;br /&gt;
#include &amp;quot;PFA_uart_m8.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define NUM_DUMMY_ADC 8&lt;br /&gt;
#define NUM_MESS_ADC  256    // max. 64 wenn uint16_t adcval&lt;br /&gt;
#define V_BANDGAP     1300UL // Atmega8&lt;br /&gt;
&lt;br /&gt;
uint16_t vcc_messung(void) &lt;br /&gt;
{&lt;br /&gt;
  uint32_t adcval;&lt;br /&gt;
  uint16_t i;&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
    Aref : AVcc&lt;br /&gt;
    Input: Interne 1.3V Bandgap-Referenz&lt;br /&gt;
   */&lt;br /&gt;
  ADMUX  = (1&amp;lt;&amp;lt;REFS0)|(1&amp;lt;&amp;lt;MUX3)|(1&amp;lt;&amp;lt;MUX2)|(1&amp;lt;&amp;lt;MUX1);&lt;br /&gt;
&lt;br /&gt;
  /* &lt;br /&gt;
     Frequenzvorteiler setzen und ADC enablen&lt;br /&gt;
&lt;br /&gt;
     ADC soll optimal mit 50 kHz bis 200 kHz laufen&lt;br /&gt;
     12000000/200000 = 60&lt;br /&gt;
     12000000/50000  = 240&lt;br /&gt;
     =&amp;gt; Vorteiler &lt;br /&gt;
     64   (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1)&lt;br /&gt;
     128  (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
   */&lt;br /&gt;
  ADCSRA = (1&amp;lt;&amp;lt;ADEN)|(1&amp;lt;&amp;lt;ADPS2)|(1&amp;lt;&amp;lt;ADPS1);&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
     MEHRERE Dummymessungen?&lt;br /&gt;
     http://www.mikrocontroller.net/topic/98469#853068&lt;br /&gt;
&lt;br /&gt;
     Experimentieren!&lt;br /&gt;
   */&lt;br /&gt;
  for(i=0; i&amp;lt;NUM_DUMMY_ADC; i++)&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);&lt;br /&gt;
    while (ADCSRA&amp;amp;(1&amp;lt;&amp;lt;ADSC)) &lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    adcval = ADCW;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
     Tatsächliche Messung: &lt;br /&gt;
     Mittelwert aus NUM_MESS_ADC Messungen&lt;br /&gt;
   */&lt;br /&gt;
  adcval = 0;&lt;br /&gt;
  for(i=0; i&amp;lt;NUM_MESS_ADC; i++)&lt;br /&gt;
  {&lt;br /&gt;
    ADCSRA |= (1&amp;lt;&amp;lt;ADSC);&lt;br /&gt;
    while (ADCSRA&amp;amp;(1&amp;lt;&amp;lt;ADSC)) &lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
    adcval += ADCW;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ADCSRA &amp;amp;= ~(1&amp;lt;&amp;lt;ADEN); // ADC disable&lt;br /&gt;
  return (adcval + NUM_MESS_ADC/2)/NUM_MESS_ADC;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Zahlenwerte über UART ausgeben&lt;br /&gt;
   Leichte Pufferung um die Ausgabemenge rel. klein zu halten&lt;br /&gt;
 */&lt;br /&gt;
void vcc_werte_anzeigen(uint16_t adcval, uint16_t vcc)&lt;br /&gt;
{&lt;br /&gt;
  static uint16_t last_adcval = 0xFFFF;&lt;br /&gt;
  static uint16_t last_vcc = 0xFF;&lt;br /&gt;
  uint8_t crlf = 0;&lt;br /&gt;
  char buffer[6];&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  if (last_adcval != adcval)&lt;br /&gt;
  {&lt;br /&gt;
    last_adcval = adcval;&lt;br /&gt;
    UART_puts(&amp;quot;ADC=[&amp;quot;);&lt;br /&gt;
    utoa(adcval, buffer, 10);&lt;br /&gt;
    for (i=5-strlen(buffer); i; i--)&lt;br /&gt;
      UART_putchar(&#039; &#039;);&lt;br /&gt;
    UART_puts(buffer);&lt;br /&gt;
    UART_putchar(&#039;]&#039;);&lt;br /&gt;
    crlf = 1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (last_vcc != vcc)&lt;br /&gt;
  {&lt;br /&gt;
    last_vcc = vcc;&lt;br /&gt;
    UART_puts(&amp;quot;  Vcc=[&amp;quot;);&lt;br /&gt;
    utoa(vcc, buffer, 10);&lt;br /&gt;
    for (i=5-strlen(buffer); i; i--)&lt;br /&gt;
      UART_putchar(&#039; &#039;);&lt;br /&gt;
    UART_puts(buffer);&lt;br /&gt;
    UART_puts(&amp;quot;] mV&amp;quot;);&lt;br /&gt;
    crlf = 1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (crlf)&lt;br /&gt;
    UART_puts(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t adcval;&lt;br /&gt;
  uint16_t vcc;&lt;br /&gt;
&lt;br /&gt;
  UART_init();&lt;br /&gt;
  UART_putchar(&#039;\014&#039;); // FORMFEED&lt;br /&gt;
  UART_puts(&amp;quot;PFA ADC Vcc Messung\r\n\r\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
 while(1)&lt;br /&gt;
  {&lt;br /&gt;
    adcval = vcc_messung();&lt;br /&gt;
    /*&lt;br /&gt;
        Allgemeine ADC Gleichung (10-Bit):&lt;br /&gt;
        V_mess = (V_ref * ADC_mess) / 1024&lt;br /&gt;
&lt;br /&gt;
        Einstellungen:&lt;br /&gt;
        V_mess = V_Bandgap (bekannt!)&lt;br /&gt;
        V_ref  = Vcc (unbekannt!)&lt;br /&gt;
        =&amp;gt; &lt;br /&gt;
        Vcc = V_ref = (V_Bandgap * 1024) / ADC_mess&lt;br /&gt;
     */&lt;br /&gt;
    if (adcval)&lt;br /&gt;
    {&lt;br /&gt;
      vcc = (V_BANDGAP * 1024) / adcval; // in mV&lt;br /&gt;
      vcc_werte_anzeigen(adcval, vcc); &lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      UART_puts(&amp;quot;ADC Einstellungen kontrollieren (adcval=0)\r\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Temperaturmessung mit DS1621 ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:PFA_TWI_DS1621.jpg|thumb|right|256px|Und es war Sommer... &amp;lt;br&amp;gt;Nein, die riesige 7-Segmentanzeige ist nicht Teil dieses Beispiels :)]] &lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel soll die Zimmertemperatur regelmäßig gemessen werden und von einem Atmega8 über RS232 an einen PC mit Terminalprogramm übertragen werden. &lt;br /&gt;
&lt;br /&gt;
Der [[Temperatursensor]] &#039;&#039;&#039;DS1621&#039;&#039;&#039; von Maxim kann über den I2C bzw. [[TWI]] Bus an AVR angeschlossen werden. Herz der Kommunikation mit dem Sensor ist die Bibliothek TWIMASTER von Peter Fleury (http://jump.to/fleury). Darin wird eine Hardware-TWI-Kommunikation abgewickelt.&lt;br /&gt;
&lt;br /&gt;
Ich hoffe der &amp;quot;Schaltplan&amp;quot; mit den vier Strippen, den beiden Pull-Up-Widerständen und der Adresseinstellung ist verständlich. Melden, wenn das nicht klar ist. Als Pull-Up-Widerstände habe ich 2,2 KOhm genommen, weil diese gerade vorhanden waren und im empfohlenen Bereich (1-10 KOhm) lagen. Details zum I2C-Bus und dessen Hardware finden sich übrigens auf http://www.i2c-bus.org/i2c-primer/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
   (Hardware)-TWI Kommunikation mit DS1621 &lt;br /&gt;
&lt;br /&gt;
   HARDWARE:&lt;br /&gt;
   Pollin Funk-AVR-Evaluationsboard v1.1&lt;br /&gt;
   Atmega8 @ 12 MHz (-Os)&lt;br /&gt;
   Maxim DS1621 Temperaursensor mit I2C Interface&lt;br /&gt;
&lt;br /&gt;
   LIBRARY:&lt;br /&gt;
   I2C master library using hardware TWI interface&lt;br /&gt;
   Peter Fleury &amp;lt;pfleury@gmx.ch&amp;gt;  http://jump.to/fleury&lt;br /&gt;
   v 1.3 2005/07/02 11:14:21&lt;br /&gt;
&lt;br /&gt;
   AUSGABE:&lt;br /&gt;
   UART 9600 Baud 8-N-1&lt;br /&gt;
 &lt;br /&gt;
   SCHALTUNG: &lt;br /&gt;
   Pin     Pin            Pin&lt;br /&gt;
   Atmega8 J4   Funktion  DS1621&lt;br /&gt;
   =========================================&lt;br /&gt;
   27 PC4  5 --- SDA ---- 1 -------------+&lt;br /&gt;
                                         |&lt;br /&gt;
   28 PC5  6 --- SCL ---- 2 ------+      |&lt;br /&gt;
                                  |      |&lt;br /&gt;
                Tout      3 NC    |      |&lt;br /&gt;
                                  |      |&lt;br /&gt;
   GND    35 --- GND ---- 4 ---+  |      |&lt;br /&gt;
                               |  #      #&lt;br /&gt;
                DS1621         |  # Rpu1 # Rpu2&lt;br /&gt;
                A2        5 ---+  # 2.2K # 2.2K&lt;br /&gt;
                Geräteadresse  |  #      #&lt;br /&gt;
                A1        6 ---+  |      |&lt;br /&gt;
                Device = 0     |  |      |&lt;br /&gt;
                A0        7 ---+  |      |&lt;br /&gt;
                                  |      |&lt;br /&gt;
   Vcc    36 --- Vcc ---- 8 ------+------+&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
   http://www.i2c-bus.org/i2c-primer/&lt;br /&gt;
   Rpu Pull-up resistance (a.k.a. I2C termination)&lt;br /&gt;
   Rpu commonly ranges from 1 kOhm to 10 kOhm&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
//#include &amp;quot;PFA_funkhw.h&amp;quot;&lt;br /&gt;
#include &amp;quot;PFA_uart.h&amp;quot;   // hier Umdefinition von DEBUG_PRINT mögl.&lt;br /&gt;
#include &amp;quot;i2cmaster.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define DS1621_A0       0&lt;br /&gt;
#define DS1621_A1       1&lt;br /&gt;
#define DS1621_A2       2&lt;br /&gt;
#define DS1621_DEVICE   ((0&amp;lt;&amp;lt;DS1621_A2)|(0&amp;lt;&amp;lt;DS1621_A1)|(0&amp;lt;&amp;lt;DS1621_A0))&lt;br /&gt;
#define DS1621_ADDRESS  (0x90 | DS1621_DEVICE)&lt;br /&gt;
#define MY_SDA          PC4&lt;br /&gt;
#define MY_SCL          PC5&lt;br /&gt;
&lt;br /&gt;
void DS1621_init(void)&lt;br /&gt;
{&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;DS1621_init:\r\n&amp;quot;);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;vor i2c_init\r\n&amp;quot;);&lt;br /&gt;
  i2c_init();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_init\r\n&amp;quot;);&lt;br /&gt;
  i2c_start(DS1621_ADDRESS + I2C_WRITE);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_start\r\n&amp;quot;);&lt;br /&gt;
  i2c_write(0xEE);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_write\r\n&amp;quot;);&lt;br /&gt;
	i2c_stop();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_stop\r\n&amp;quot;);&lt;br /&gt;
  i2c_start(DS1621_ADDRESS + I2C_WRITE);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_start\r\n&amp;quot;);&lt;br /&gt;
  i2c_write(0xAA);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_write\r\n&amp;quot;);&lt;br /&gt;
  i2c_stop();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_stop\r\n\r\n&amp;quot;);&lt;br /&gt;
  _delay_ms(200);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int16_t DS1621_temperatur(void)&lt;br /&gt;
{&lt;br /&gt;
  int16_t temperatur = 0;&lt;br /&gt;
&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;DS1621_temperatur:\r\n&amp;quot;);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;vor i2c_start\r\n&amp;quot;);&lt;br /&gt;
 	i2c_start(DS1621_ADDRESS + I2C_READ);&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_start\r\n&amp;quot;);&lt;br /&gt;
  temperatur = i2c_readAck();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_readAck\r\n&amp;quot;);&lt;br /&gt;
  i2c_readNak();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_readNAK\r\n&amp;quot;);&lt;br /&gt;
  i2c_stop();&lt;br /&gt;
  DEBUG_PRINT(&amp;quot;nach i2c_stop\r\n\r\n&amp;quot;);&lt;br /&gt;
  return temperatur;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
  UART_init();&lt;br /&gt;
&lt;br /&gt;
  // FORMFEED + Intro&lt;br /&gt;
  UART_puts(&amp;quot;\014(Hardware)-TWI Atmega8&amp;lt;-&amp;gt;DS1621\r\n\r\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
  DS1621_init();&lt;br /&gt;
&lt;br /&gt;
  while (1)&lt;br /&gt;
  {&lt;br /&gt;
    char s[5];&lt;br /&gt;
    int16_t temperatur;&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
&lt;br /&gt;
    // DS1621 auslesen&lt;br /&gt;
    temperatur = DS1621_temperatur();&lt;br /&gt;
&lt;br /&gt;
    // Für UART aufbereiten    &lt;br /&gt;
    if (temperatur &amp;lt; 0)&lt;br /&gt;
    {&lt;br /&gt;
      temperatur *= -1; &lt;br /&gt;
      s[0] = &#039;-&#039;;&lt;br /&gt;
      // todo&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
      s[0] = &#039;+&#039;;&lt;br /&gt;
    &lt;br /&gt;
    temperatur %= 1000;&lt;br /&gt;
    s[1] = temperatur/100 + &#039;0&#039;;&lt;br /&gt;
    temperatur %= 100;&lt;br /&gt;
    s[2] = temperatur/10  + &#039;0&#039;;&lt;br /&gt;
    temperatur %= 10;&lt;br /&gt;
    s[3] = temperatur + &#039;0&#039;;&lt;br /&gt;
    s[4] = 0;&lt;br /&gt;
&lt;br /&gt;
    UART_puts(s);&lt;br /&gt;
    UART_puts(&amp;quot; \370C\r\n&amp;quot;); // Gradzeichen C&lt;br /&gt;
&lt;br /&gt;
    // Nächste Messung in 10s&lt;br /&gt;
    for (i=0; i&amp;lt;10; i++)&lt;br /&gt;
      _delay_ms(1000);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// EOF PFA_TWI_DS1621.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die C-Quelltexte, das DS1621 Datenblatt und eine fertige Intel-HEX-Datei befinden sich im Archiv [[Media:PFA_TWI_DS1621.zip|PFA_TWI_DS1621.zip]].&lt;br /&gt;
&lt;br /&gt;
Zu Beginn von main() wird die UART mit 9600-8N1 initialisiert. Anschliessend wird der Sensor initialisiert. Als Geräteadresse (0x90) ist dabei im Datenblatt fest vorgegeben und die Adressbits A0, A1, A2 für einen von 8 möglichen DS1621 an einem I2C Bus sind auf LOW eingestellt bzw. mit GND verbunden.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Messung ist ein einfacher Aufruf der Funktion DS1621_temperatur(). Die Aufbereitung des Ergebnisses ist primitiv gemacht und die Temperatur wird als Text im 10s Abstand an den PC gesendet. &lt;br /&gt;
&lt;br /&gt;
Temperaturen unter 0°C sind nicht getestet. Man sieht auch an DEBUG_PRINT(), dass ich selbst am experimentieren bin. Wer die Debugtexte  lesen möchte, kann in PFA_uart.h DEBUG_PRINT() entsprechend definieren.&lt;br /&gt;
&lt;br /&gt;
=== Analog Comparator ===&lt;br /&gt;
&lt;br /&gt;
Die inzwischen gelösten Probleme von &amp;quot;guest&amp;quot; ([http://www.mikrocontroller.net/topic/169773#1623018]) mit dem &#039;&#039;&#039;Analog Comparator&#039;&#039;&#039; haben mich zum Experimentieren mit dieser Funktion angeregt. Hier im Wiki findet man auch Infos zum Analog Comparator im [[AVR-GCC-Tutorial#AC_.28Analog_Comparator.29|AVR-GCC-Tutorial: AC (Analog_Comparator)]].&lt;br /&gt;
&lt;br /&gt;
Man braucht für die folgenden drei Beispiele z.&amp;amp;nbsp;B. den &#039;&#039;&#039;Atmega8&#039;&#039;&#039; und eine Drahtbrücke z.&amp;amp;nbsp;B. an dem 40-poligen Stecker J4. Die &amp;quot;Schaltung&amp;quot; arbeitet dann mit der LED2 auf dem Board und mit dem TASTER1 auf dem Board. Die Drahtbrücke verbindet den Taster (Pin PB1 Pinnummer 10 an J4 mit AIN1(PD7) Pinnummer 33 an J4). Die Vergleichsspannung für AIN0 wird auf die interne Bandgap-Referenz gesetzt, so dass kein externer Spannungsanschluss an ANI0(PD6) benötigt wird. Die LED1 an diesem Anschluss bleibt unbenutzt und stört hier nicht.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;erste Beispiel&#039;&#039;&#039; zeigt eine grundsätzliche Funktion des Analog Comparators. Die beiden Eingänge AIN0 (int. Bandgap) und AIN1 (0V oder 5V über TASTER1) werden miteinander verglichen und &#039;&#039;&#039;wenn sich der Vergleich ändert&#039;&#039;&#039;, wird der Interrupt ausgelöst. &lt;br /&gt;
&lt;br /&gt;
Hierbei wird der Interrupt nicht abhängig vom IST-Zustand des Vergleichs ausgelöst, sondern abhängig von der Änderung zu einem vorhergehenden Zustand:&lt;br /&gt;
* wenn sich der Vergleich ändert (ACIS1 0 und ACIS0 0, &#039;&#039;toggle&#039;&#039; Wechsel): Beispiel 1&lt;br /&gt;
* wenn ein Wechsel von AIN1 &amp;gt; AIN0 nach AIN1 &amp;lt; AIN0 stattfindet (ACIS1 1 und ACIS0 0, &#039;&#039;falling&#039;&#039; fallende Flanke)&lt;br /&gt;
* wenn ein Wechsel von AIN1 &amp;lt; AIN0 nach AIN1 &amp;gt; AIN0 stattfindet (ACIS1 1 und ACIS0 0, &#039;&#039;rising&#039;&#039; steigende Flanke): Beispiele 2 und 3&lt;br /&gt;
&lt;br /&gt;
Es besteht auch die Möglichkeit das Vergleichssignal OCR vor der Hardware, die die Änderung oben bewertet, abzugreifen und auszugeben. Damit kann man direkt, d.h. statisch im IST-Zustand ohne Infos über den vorhergehenden Zustand AIN0 mit AIN1 vergleichen. Das ist in den folgenden Beispielen aber nicht eingebaut.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Analog Comparator Beispiel #1&lt;br /&gt;
&lt;br /&gt;
    Funktion&lt;br /&gt;
    ========&lt;br /&gt;
    Taster1 triggert den Interrupt ANA_COMP_vect &lt;br /&gt;
    und in der ISR wird die LED getoggelt&lt;br /&gt;
&lt;br /&gt;
    Hardware (Pollin Funk AVR Board):&lt;br /&gt;
    ================================&lt;br /&gt;
    Interne Bandgap als AIN0 (s.u.)&lt;br /&gt;
    &lt;br /&gt;
    Taster1 über Brücke an AIN1 (PD7) legen&lt;br /&gt;
    =&amp;gt; Brücke PB1 (#10 an J4) &amp;lt;----&amp;gt; PD7 (#33 an J4)&lt;br /&gt;
&lt;br /&gt;
    Active high LED an PD5&lt;br /&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;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ISR(ANA_COMP_vect)&lt;br /&gt;
{  &lt;br /&gt;
  PORTD ^= (1&amp;lt;&amp;lt;PD5);  // LED toogle&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() &lt;br /&gt;
{  &lt;br /&gt;
  DDRD = 1&amp;lt;&amp;lt;PD5; // LED Ausgang&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
    Atmega8 Datenblatt S. 193&lt;br /&gt;
    PD7 = AIN1 = Analog comperator negative input&lt;br /&gt;
    PD6 = AIN0 = Analog comperator positive input&lt;br /&gt;
&lt;br /&gt;
    Register ACSR&lt;br /&gt;
    =============&lt;br /&gt;
    ACD:  Analog comparator disable&lt;br /&gt;
    ACBG: Analog Bandgap an AIN0&lt;br /&gt;
    ACIE: Analog comparator interrupt enable&lt;br /&gt;
    ACIC: Analog comparator input capture enable&lt;br /&gt;
    ACIS1/ACIS0: Analog comparator interrupt mode select&lt;br /&gt;
      00 toggle&lt;br /&gt;
      01 reserved&lt;br /&gt;
      10 falling&lt;br /&gt;
      11 rising&lt;br /&gt;
   */&lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACBG);    &lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACIE);    &lt;br /&gt;
  sei();&lt;br /&gt;
      &lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;zweiten Beispiel&#039;&#039;&#039; löst ein Drücken des Tasters einen Interrupt aus (Trigger). Dadurch wird die LED2 in der ISR eingeschaltet. Nach einer Wartezeit von 1s wird die LED2 dann im Hauptprogramm ausgeschaltet. Ein weiterer Tastendruck innerhalb der Wartezeit verlängert die Wartezeit nicht, d.h. es wird nicht nachgetriggert. Weil _delay_ms() benutzt wird, muss der Quelltext mit Optimierung übersetzt werden (z.&amp;amp;nbsp;B. -Os Option).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Analog Comparator Beispiel #2&lt;br /&gt;
&lt;br /&gt;
    Funktion&lt;br /&gt;
    ========&lt;br /&gt;
    Taster1 triggert den Interrupt ANA_COMP_vect &lt;br /&gt;
    und in der ISR wird die LED angeschaltet.&lt;br /&gt;
    In main() wird die LED 1s nach dem auslösenden &lt;br /&gt;
    Tastendruck ausgeschaltet.Ein neuer Interrupt &lt;br /&gt;
    innerhalb des 1s Wartens kann das Ausschlaten &lt;br /&gt;
    nicht verzögern (Nicht nachtriggerbar)&lt;br /&gt;
&lt;br /&gt;
    Hardware (Pollin Funk AVR Board):&lt;br /&gt;
    ================================&lt;br /&gt;
    Interne Bandgap als AIN0 (s.u.)&lt;br /&gt;
    &lt;br /&gt;
    Taster1 über Brücke an AIN1 (PD7) legen&lt;br /&gt;
    =&amp;gt; Brücke PB1 (#10 an J4) &amp;lt;----&amp;gt; PD7 (#33 an J4)&lt;br /&gt;
&lt;br /&gt;
    Active high LED an PD5&lt;br /&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;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t led_ist_an;&lt;br /&gt;
&lt;br /&gt;
ISR(ANA_COMP_vect)&lt;br /&gt;
{  &lt;br /&gt;
  PORTD |= (1&amp;lt;&amp;lt;PD5);  // LED an&lt;br /&gt;
  led_ist_an = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() &lt;br /&gt;
{  &lt;br /&gt;
  DDRD = 1&amp;lt;&amp;lt;PD5; // LED Ausgang&lt;br /&gt;
&lt;br /&gt;
  /*&lt;br /&gt;
    Atmega8 Datenblatt S. 193&lt;br /&gt;
    PD7 = AIN1 = Analog comperator negative input&lt;br /&gt;
    PD6 = AIN0 = Analog comperator positive input&lt;br /&gt;
&lt;br /&gt;
    Register ACSR&lt;br /&gt;
    =============&lt;br /&gt;
    ACD:  Analog comparator disable&lt;br /&gt;
    ACBG: Analog Bandgap an AIN0&lt;br /&gt;
    ACIE: Analog comparator interrupt enable&lt;br /&gt;
    ACIC: Analog comparator input capture enable&lt;br /&gt;
    ACIS1/ACIS0: Analog comparator interrupt mode select&lt;br /&gt;
      00 toggle&lt;br /&gt;
      01 reserved&lt;br /&gt;
      10 falling&lt;br /&gt;
      11 rising&lt;br /&gt;
   */&lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACBG);    &lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACIS1) | (1&amp;lt;&amp;lt;ACIS0);&lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACIE);    &lt;br /&gt;
  sei();&lt;br /&gt;
      &lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    if ( led_ist_an )&lt;br /&gt;
    {&lt;br /&gt;
      _delay_ms(1000);&lt;br /&gt;
      PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5); // LED aus&lt;br /&gt;
      led_ist_an = 0;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;dritten Beispiel&#039;&#039;&#039; löst ein Drücken des Tasters einen Interrupt aus (Trigger). Dadurch wird die LED2 in der ISR eingeschaltet. Nach einer Wartezeit von 1s wird die LED2 dann im Hauptprogramm ausgeschaltet. Im Gegensatz zu Beispiel Zwei verlängert weiterer Tastendruck innerhalb der Wartezeit Wartezeit nicht, d.h. es wird nachgetriggert. Weil _delay_ms() benutzt wird, muss der Quelltext mit Optimierung übersetzt werden (z.&amp;amp;nbsp;B. -Os Option).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8 @ 12 MHz&lt;br /&gt;
&lt;br /&gt;
    Analog Comparator Beispiel #3&lt;br /&gt;
&lt;br /&gt;
    Funktion&lt;br /&gt;
    ========&lt;br /&gt;
    Taster1 triggert den Interrupt ANA_COMP_vect &lt;br /&gt;
    und in der ISR wird die LED angeschaltet.&lt;br /&gt;
    In main() wird die LED 1s nach dem LETZTEN&lt;br /&gt;
    Tastendruck ausgeschaltet. Eine neu auftretender &lt;br /&gt;
    Interrupt kann also das Ausschalten verzögern &lt;br /&gt;
    (Nachtriggern).&lt;br /&gt;
&lt;br /&gt;
    Hardware (Pollin Funk AVR Board):&lt;br /&gt;
    ================================&lt;br /&gt;
    Interne Bandgap als AIN0 (s.u.)&lt;br /&gt;
    &lt;br /&gt;
    Taster1 über Brücke an AIN1 (PD7) legen&lt;br /&gt;
    =&amp;gt; Brücke PB1 (#10 an J4) &amp;lt;----&amp;gt; PD7 (#33 an J4)&lt;br /&gt;
&lt;br /&gt;
    Active high LED an PD5&lt;br /&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;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t trigger;&lt;br /&gt;
&lt;br /&gt;
ISR(ANA_COMP_vect)&lt;br /&gt;
{  &lt;br /&gt;
  PORTD |= (1&amp;lt;&amp;lt;PD5);  // LED an&lt;br /&gt;
  trigger = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() &lt;br /&gt;
{  &lt;br /&gt;
  DDRD = 1&amp;lt;&amp;lt;PD5; // LED Ausgang&lt;br /&gt;
  &lt;br /&gt;
  /*&lt;br /&gt;
    Atmega8 Datenblatt S. 193&lt;br /&gt;
    PD7 = AIN1 = Analog comperator negative input&lt;br /&gt;
    PD6 = AIN0 = Analog comperator positive input&lt;br /&gt;
&lt;br /&gt;
    Register ACSR&lt;br /&gt;
    =============&lt;br /&gt;
    ACD:  Analog comparator disable&lt;br /&gt;
    ACBG: Analog Bandgap an AIN0&lt;br /&gt;
    ACIE: Analog comparator interrupt enable&lt;br /&gt;
    ACIC: Analog comparator input capture enable&lt;br /&gt;
    ACIS1/ACIS0: Analog comparator interrupt mode select&lt;br /&gt;
      00 toggle&lt;br /&gt;
      01 reserved&lt;br /&gt;
      10 falling&lt;br /&gt;
      11 rising&lt;br /&gt;
   */&lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACBG);    &lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACIS1) | (1&amp;lt;&amp;lt;ACIS0);&lt;br /&gt;
  ACSR |= (1&amp;lt;&amp;lt;ACIE);    &lt;br /&gt;
  sei();&lt;br /&gt;
      &lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    if ( trigger )&lt;br /&gt;
    {&lt;br /&gt;
      do&lt;br /&gt;
      {&lt;br /&gt;
        trigger = 0;&lt;br /&gt;
        _delay_ms(1000);&lt;br /&gt;
      } while ( trigger );&lt;br /&gt;
      PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5); // LED aus&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Warten mit _delay_ms() in den Beispielen würde man in einer &amp;quot;echten&amp;quot; Anwendung anders lösen. Statt in einer Warteschleife Däumchen zu drehen, muss der Atmega8 ja normalerweise weitere Funktionen abarbeiten. Man könnte z.&amp;amp;nbsp;B. einen Timer benutzen und regelmäßig eine Abfrage mithilfe einer selbstgeschriebenen Timeoutfunktion machen, ob die Wartezeit bereits verstrichen ist.&lt;br /&gt;
&lt;br /&gt;
=== Pennen bis der Hund bellt ===&lt;br /&gt;
&lt;br /&gt;
In [http://www.mikrocontroller.net/topic/199684#1959921] fragt Alex, wie man mit dem &#039;&#039;&#039;Watchdog&#039;&#039;&#039; einen AVR aus dem &#039;&#039;&#039;Sleep Mode&#039;&#039;&#039; wecken kann. Diese nützliche Anwendung des Watchdogs ist zwar im Artikel [[AVR-Tutorial:_Watchdog#Aufwecken_aus_einem_Sleep_Mode|AVR-Tutorial: Watchdog]] genannt, war aber bisher noch ohne Beispielprogramm. Das soll hier ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
    Atmega8 @ 12 MHz --- ODER --- Attiny2313 @ 8 MHz&lt;br /&gt;
    Pollin-Funk-AVR-Board&lt;br /&gt;
*/&lt;br /&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/wdt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MS_AKTIV 100&lt;br /&gt;
&lt;br /&gt;
// Aktivierten Watchdog nach Reset erstmal schnell abschalten&lt;br /&gt;
// http://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html&lt;br /&gt;
void get_mcusr(void) \&lt;br /&gt;
__attribute__((naked)) \&lt;br /&gt;
__attribute__((section(&amp;quot;.init3&amp;quot;)));&lt;br /&gt;
&lt;br /&gt;
#if defined(__AVR_ATmega8__)&lt;br /&gt;
#define MCUSR MCUCSR&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
uint8_t mcusr_mirror __attribute__ ((section (&amp;quot;.noinit&amp;quot;)));&lt;br /&gt;
void get_mcusr(void)&lt;br /&gt;
{&lt;br /&gt;
  mcusr_mirror = MCUSR;&lt;br /&gt;
  MCUSR = 0;&lt;br /&gt;
  wdt_disable();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LED5 auf Pollin-Funk-AVR-Board als Arbeitsanzeige&lt;br /&gt;
inline void LED5_an(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;PD5);  // Ausgang&lt;br /&gt;
  PORTD |= (1&amp;lt;&amp;lt;PD5); // active high LED an&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline void LED5_aus(void)&lt;br /&gt;
{&lt;br /&gt;
  PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5); // active high LED aus&lt;br /&gt;
  DDRD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);  // Eingang&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// LED6 auf Pollin-Funk-AVR-Board als Sleepanzeige&lt;br /&gt;
inline void LED6_an(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRD |= (1&amp;lt;&amp;lt;PD6);  // Ausgang&lt;br /&gt;
  PORTD |= (1&amp;lt;&amp;lt;PD6); // active high LED an&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
inline void LED6_aus(void)&lt;br /&gt;
{&lt;br /&gt;
  PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD6); // active high LED aus&lt;br /&gt;
  DDRD &amp;amp;= ~(1&amp;lt;&amp;lt;PD6);  // Eingang&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#if defined(__AVR_ATtiny2313__)&lt;br /&gt;
ISR(WDT_OVERFLOW_vect)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t ms = 0;&lt;br /&gt;
&lt;br /&gt;
  LED6_aus();&lt;br /&gt;
  wdt_enable(WDTO_2S);&lt;br /&gt;
&lt;br /&gt;
  while(1)&lt;br /&gt;
  {&lt;br /&gt;
    // Beispielhafte Arbeitsroutine&lt;br /&gt;
    // Mit Watchdogüberwachung gegen schlechtes Benehmen&lt;br /&gt;
    wdt_reset();&lt;br /&gt;
    LED5_an();&lt;br /&gt;
    _delay_ms(1);&lt;br /&gt;
    ms++;&lt;br /&gt;
&lt;br /&gt;
    // Prüfen, ob die Arbeitsroutine MS_AKTIV ms gelaufen ist&lt;br /&gt;
    // wenn ja, dann SLEEP bis Watchdog nach der Zeit WDTO_2S weckt...&lt;br /&gt;
    if (ms == MS_AKTIV)&lt;br /&gt;
    {&lt;br /&gt;
      LED5_aus();&lt;br /&gt;
      LED6_an();&lt;br /&gt;
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
      wdt_reset();&lt;br /&gt;
#if defined(__AVR_ATtiny2313__)&lt;br /&gt;
      cli();&lt;br /&gt;
      WDTCSR |= (1&amp;lt;&amp;lt;WDIE); // WDT Interrupt enable&lt;br /&gt;
#endif&lt;br /&gt;
      sleep_enable();&lt;br /&gt;
#if defined(__AVR_ATtiny2313__)&lt;br /&gt;
      sei();&lt;br /&gt;
#endif&lt;br /&gt;
      sleep_cpu();&lt;br /&gt;
      sleep_disable();&lt;br /&gt;
      ms = 0;&lt;br /&gt;
    }&lt;br /&gt;
  }  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Arbeitsweise beim &#039;&#039;&#039;Atmega8&#039;&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
Die LED5 blitzt durch LED5_an() und die Schleife über den Zähler &#039;&#039;ms&#039;&#039; für &#039;&#039;MS_AKTIV&#039;&#039; Millisekunden auf. Ist in der if-Abfrage &#039;&#039;ms&#039;&#039; gleich &#039;&#039;MS_AKTIV&#039;&#039;, geht der Atmega8 geht nach dem Abschalten der LED5 und Anschalten der LED6 an der Stelle sleep_cpu() in den Schlafmodus. Nach der Zeit WDTO_2S wird der zu Programmstart mit WDTO_2S aufgesetzte Watchdog aktiv und weckt den AVR aus dem Schlafmodus. Bei dem Atmega8 wird durch den Watchdog immer ein &#039;&#039;&#039;System Reset&#039;&#039;&#039; ausgelöst, Dies erkennt man daran, dass LED6 zu Beginn von main ausgeschaltet wird, d.h. flackert. Ohne Reset würde LED6 dauernd angeschaltet bleiben.&lt;br /&gt;
&lt;br /&gt;
Die Arbeitsweise beim &#039;&#039;&#039;Attiny2313&#039;&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
Der Watchdog des Attiny2313 kann nicht nur einen System Reset durchführen, so wie beim Atmega8 oben, sondern es ist auch möglich einen Interrupt mit entsprechender ISR auszuführen. Dazu muss man im WDTCSR Register einstellen, ob nur der Interrupt ausgeführt wird oder ob zuerst der Interrupt ausgeführt wird (beispielsweise zur Datenrettung ins EEPROM oder zur Anzeige einer Statusmeldung) und dann beim nächsten auftretenden Watchdog der System Reset. &lt;br /&gt;
&lt;br /&gt;
Im Beispielprogramm wurde unmittelbar vor dem Sleep der Interrupt Modus plus System Reset Modus eingestellt. Die ISR kann dabei leer sein - es ist nichts zu tun - aber die ISR muss natürlich vorhanden sein. Beim Aufwachen aus dem Sleep löscht der AVR automatisch das WDIE Bit und der nächste Watchdog wird zu einem System Reset führen, d.h. man hat die normale Watchdogfunktion zur Überwachung seines Programms.&lt;br /&gt;
&lt;br /&gt;
Den Unterschied zwischen Atmega8 und Attiny2313 Programm sieht man an dem Verhalten der LED6. Beim Attiny2313 bleibt die LED6 dauerhaft an, d.h.der Programmteil zwischen main und while wird nur einmal beim Power-Up bzw. sonstigen Resets außer den Watchdog-Resets durchlaufen.&lt;br /&gt;
&lt;br /&gt;
== Forenbeiträge ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/82156 Optokoppler auf Pollin Funk Evaluations-Board]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/102101 Pollin evaluation nur Fuses geht nicht aber programmieren!]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/119579 Pollin Mainboard/Funkboard und RFM12 Funkmodul]&lt;br /&gt;
* Kritik an den &amp;quot;Entprellkondensatoren&amp;quot; der Taster: [http://www.mikrocontroller.net/topic/122450#1113811], [http://www.mikrocontroller.net/topic/122551#1118124], [http://www.mikrocontroller.net/topic/152361#1431824] und [http://www.mikrocontroller.net/topic/159743#1518616]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/147072#1369628 Pollin Funkboard mit Mega8, [[RFM12]], [[LCD]], [[DCF77]] und [[DS18S20]]]&lt;br /&gt;
&lt;br /&gt;
== Scratchpad ==&lt;br /&gt;
(Vorgemerktes für neue Abschnitte, Ergänzungen und Korrekturen)&lt;br /&gt;
&lt;br /&gt;
* Differentielle ADC-Messung [http://www.mikrocontroller.net/topic/165949]&lt;br /&gt;
&lt;br /&gt;
* Mittelwertbildung und gleitender Durchschnitt [http://www.mikrocontroller.net/topic/166587], [http://www.mikrocontroller.net/topic/170608#1631873]&lt;br /&gt;
&lt;br /&gt;
* LED Dimmen über ADC [http://www.mikrocontroller.net/topic/169364#1619346]&lt;br /&gt;
&lt;br /&gt;
* RC5 Erweiterung [http://www.mikrocontroller.net/topic/168346#1613115]&lt;br /&gt;
&lt;br /&gt;
* DS18S20, DS18B20 [http://www.mikrocontroller.net/topic/14792#1631385], [http://www.mikrocontroller.net/topic/170112#1626088], [http://www.mikrocontroller.net/topic/171691]&lt;br /&gt;
&lt;br /&gt;
* DS1621 [http://www.mikrocontroller.net/topic/174906#1681232], [http://www.mikrocontroller.net/topic/192823#1886493]&lt;br /&gt;
&lt;br /&gt;
* Piezosummer ohne Elektronik [http://www.mikrocontroller.net/topic/118816?page=2#1106912]&lt;br /&gt;
&lt;br /&gt;
* Input Capture [http://www.mikrocontroller.net/topic/204072#2011304]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.pollin.de/shop/downloads/D810046B.PDF Datenblatt Funk-AVR-Evaluationsboard (PDF)] Best.nr. 810046 von www.pollin.de &amp;lt;!-- (derzeit ist das Board nicht im Angebot Stand 3/2008) --&amp;gt; &lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf Datenblatt Atmega8 (PDF)]&lt;br /&gt;
* [http://www.controller-designs.de/index.php?lang=de&amp;amp;cat=internal&amp;amp;cont=info_pollinfunkboard&amp;amp;sub= Erfahrungsbericht] von Marco Schmoll (www.controller-designs.de). Bezieht sich auf die ältere v1.0 bzw. v1.1 Hardware.&lt;br /&gt;
* [http://www.dh2faa.de/mctrl2.html Ansteuerung mit BASCOM] von  Heiko Bujak. Siehe auch Beitrag im Forum [http://www.mikrocontroller.net/topic/119579]&lt;br /&gt;
* [[Pollin ATMEL Evaluations-Board]]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Kategorie:RFM12]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Entprellung&amp;diff=81633</id>
		<title>Entprellung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Entprellung&amp;diff=81633"/>
		<updated>2014-02-17T12:32:06Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Flankenerkennung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Problembeschreibung ==&lt;br /&gt;
Mechanische [[Schalter]] wie [[Drehgeber]] neigen beim Ein- und Ausschalten zum sogenannten &#039;&#039;&#039;Prellen&#039;&#039;&#039;, d.h sie schalten schnell mehrfach aus und ein, verursacht durch mechanische Vibrationen des Schaltkontaktes, sofern sie nicht dagegen geschützt sind. Vereinfacht dargestellt, sieht eine von einem Schalter oder Taster geschaltete Spannung beim Schalten wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Entprellen.png]]&lt;br /&gt;
&lt;br /&gt;
Für die Vermeidung bzw. Auswertung dieses unsauberen Signals gibt es verschiedene Ansätze:&lt;br /&gt;
&lt;br /&gt;
== Hardwareentprellung ==&lt;br /&gt;
&lt;br /&gt;
===Prellfreie Schalter===&lt;br /&gt;
&lt;br /&gt;
Für Spezialanwendungen hält die elektromechanische Industrie verschiedene Sonderkonstruktionen bereit, die saubere Schaltzustände nach Aussen generieren, indem sie entweder eine mechanische Dämpfung in Form eines selbsthemmenden Federmechanismus oder eine integrierte elektronische Signalverzögerung benutzen.&lt;br /&gt;
&lt;br /&gt;
Solche Systeme sind jedoch teuer und werden meist nur im Leistungsbereich eingesetzt.&lt;br /&gt;
&lt;br /&gt;
===Wechselschalter===&lt;br /&gt;
&lt;br /&gt;
Für die Entprellung von Wechselschaltern (engl. Double Throw Switch) kann ein klassisches RS-[[Flipflop]] genutzt werden. Bei dieser Variante werden neben zwei NAND-Gattern nur noch zwei Pull-Up Widerstände benötigt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:NAND_debouncer.png|thumb|left|350px|&#039;&#039;&#039;Taster entprellen mit NAND-RS-Flipflop&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der gezeigten Schalterstellung liegt an der Position /S der Pegel 0 an. Damit ist das Flipflop gesetzt und der Ausgang auf dem Pegel 1. Schließt der Schalter zwischen den Kontakten 2 und 3, liegt an der Postion /R der Pegel 0 an. Dies bedeutet, dass der Ausgang des Flipflops auf den Pegel 0 geht. Sobald der Schalter von einem zum anderen Kontakt wechselt, beginnt er in der Regel zu prellen. Während des Prellens wechselt der Schalter zwischen den beiden Zuständen &amp;quot;Schalter berührt Kontakt&amp;quot; und &amp;quot;Schalter ist frei in der Luft&amp;quot;. Der Ausgang des Flipflops bleibt in dieser Prellzeit aber stabil, da der Schalter während des Prellens nie den gegenüberliegenden Kontakt berührt und das RS-Flipflop seinen Zustand allein halten kann. Die Prellzeit ist stark vom Schaltertyp abhängig und liegt zwischen 0,1 und 10ms. Die Dimensionierung der Widerstände ist relativ unkritisch. Als Richtwert können hier 100kOhm verwendet werden.&lt;br /&gt;
&lt;br /&gt;
====Wechselschalter ohne Flip-Flop====&lt;br /&gt;
&lt;br /&gt;
Wenn man mal gerade kein Flip-Flop zur Hand hat, kann man sich auch mit dieser Schaltung behelfen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:WechselEntprellC.PNG|thumb|left|350px|&#039;&#039;&#039;Wechsler entprellen mit Kondensator&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Funktionsweise:&lt;br /&gt;
Beim Umschalten wird der Kondensator immer sofort umgeladen.&lt;br /&gt;
Während der Kontakt prellt, befindet er sich in der Luft und hat keinerlei Verbindung. Während dieser Zeit übernimmt der Kondensator das halten des Pegels.&lt;br /&gt;
&lt;br /&gt;
Dimensionierung:&lt;br /&gt;
Ist der entprellte Taster an ein IC Angeschlossen, ist der &#039;&#039;Input Leakage Current&#039;&#039; der ausschlaggebende Strom. Falls weitere Ströme fließen sind diese mit zu berücksichtigen. Bei einem Mikrocontroller von Atmel sind 1µA typisch.&lt;br /&gt;
Es gilt:&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{dU}{dt} = \frac{I}{C}&amp;lt;/math&amp;gt;&lt;br /&gt;
Da eine Prellung ca. 10ms dauert und die Spannung in dieser Zeit beispielsweise um maximal 0,5V fallen soll kommt man auf folgende Kapazität:&lt;br /&gt;
:&amp;lt;math&amp;gt; C = \frac{I \cdot dt}{dU} = \frac{1\mu A \cdot 10ms}{0,5V} = 20nF &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um Stromspitzen zu verringern kann ein Widerstand mit eingefügt werden. Eine Zeitkonstante von 1µs bis 1ms scheint sinnvoll. Also 500 Ohm bis 500kOhm sind nutzbar, wobei bei niedrigem Widerstand die Stromspitzen höher sind, und bei 500kOhm der Pinstrom störend wird.&lt;br /&gt;
&lt;br /&gt;
===Einfacher Taster===&lt;br /&gt;
&lt;br /&gt;
Auch wenn das RS-Flipflop sehr effektiv ist, wird diese Variante der Entprellung nur selten angewendet. Grund dafür ist, dass in Schaltungen häufiger einfache Taster eingesetzt werden. Diese sind oft kleiner und preisgünstiger. Um einfache Taster (engl. Single Throw Switch) zu entprellen, kann ein einfacher RC-Tiefpass eingesetzt werden. Hierbei wird ein Kondensator über einen Widerstand je nach Schalterstellung auf- oder entladen. Das RC-Glied bildet einen Tiefpass, sodass die Spannung über den Kondensator nicht von einen Pegel auf den anderen springen kann.&lt;br /&gt;
&lt;br /&gt;
[[Bild:RC_debouncer.png|thumb|left|300px|Taster entprellen mit RC-Entpreller]]&lt;br /&gt;
&lt;br /&gt;
[[Datei:Entprellen1a.png|thumb|350px| Entstehender Spannungsverlauf]]&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
Wenn der Schalter geöffnet ist, lädt sich der Kondensator langsam über die beiden Widerstände R&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; und R&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; auf V&amp;lt;sub&amp;gt;cc&amp;lt;/sub&amp;gt; auf. Beim Erreichen der Umschaltschwelle springt der Ausgang auf den Pegel 0. Wird der Schalter geschlossen, entlädt sich der Kondensator langsam über den Widerstand R&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;. Demnach ändert sich der Ausgang des Inverters auf den Pegel 1. Während der Taster prellt, kann sich die Spannung über dem Kondensator nicht sprunghaft ändern, da das Auf- und Entladen eher langsam über die Widerstände erfolgt. Außerdem sind die Schaltschwellen für den Übergang LOW-&amp;gt;HIGH und HIGH-&amp;gt;LOW stark verschieden (Hysterese, siehe Artikel [[Schmitt-Trigger]]). Bei richtiger Dimensionierung der Bauelemente wird somit der Ausgang des Inverters prellfrei.&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass der Inverter &#039;&#039;&#039;unbedingt&#039;&#039;&#039; einer mit [[Schmitt-Trigger]] Eingängen sein muss, weil bei Standard-Logikeingängen im Bereich von üblicherweise 0,8V - 2,0V der Ausgang nicht definiert ist. Als Inverter kann zum Beispiel der 74HC14 oder der CD40106 (pinkompatibel) eingesetzt werden. Alternativ kann auch ein CD4093 eingesetzt werden. Bei dem CD4093 handelt es sich um NAND-Gatter mit Schmitt-Trigger-Eingängen. Um aus einem NAND-Gatter einen Inverter zu machen, müssen einfach nur die beiden Eingänge verbunden werden oder ein Eingang fest auf HIGH gelegt werden.&lt;br /&gt;
&lt;br /&gt;
Für eine geeignete Dimensionierung muss man etwas mit den Standardformeln für einen Kondensator jonglieren. Die Spannung über den Kondensator beim Entladen berechnet sich nach:&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t) = U_0 \cdot e^{\frac{-t}{R_2 C_1}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit der Ausgang des Inverters stabil ist, muss die Spannung über den Kondensator und damit die Spannung am Eingang des Inverters über der Spannung bleiben, bei welcher der Inverter umschaltet. Diese Schwellwertspannung ist genau die zeitabhängige Spannung über den Kondensator.&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t)\!\ = U_{th}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Umstellen der Formel ergibt sich nun:&lt;br /&gt;
:&amp;lt;math&amp;gt;R_2=\frac{-t}{C_1 \cdot ln\left(\frac{U_{th}}{U_0} \right)}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Taster prellt üblicherweise etwa 10ms. Zur Sicherheit kann bei der Berechnung des Widerstandes eine Prellzeit von 20ms angenommen werden. U_0 ist die Betriebsspannung also Vcc. Die Schwellwertspannung muss aus dem Datenblatt des eingesetzten Schmitt-Triggers abgelesen werden. Beim 74HC14 beträgt der gesuchte Wert 2,0V. Nimmt man für den Kondensator 1µF und beträgt die  Betriebsspannung 5V, ergibt sich für den Widerstand ein Wert von etwa 22kOhm.&lt;br /&gt;
&lt;br /&gt;
Wird der Schalter geöffnet, lädt sich der Kondensator nach folgender Formel auf:&lt;br /&gt;
:&amp;lt;math&amp;gt;U_C(t) = U_0 \cdot \left( 1-e^{\frac{-t}{(R_1+R_2)\cdot C_1}} \right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit U_th=U_C ergibt das Umstellen nach (R_1+R_2):&lt;br /&gt;
:&amp;lt;math&amp;gt;R_1+R_2 = \frac{-t}{C_1 \cdot ln\left(1-\frac{U_{th}}{U_0} \right)} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Schwellspannung lässt sich aus dem Datenblatt ein Wert von 2,3V ablesen. Mit diesem Wert und den Annahmen von oben ergibt sich für R_1+R_2 ein Wert von 32kOhm. Somit ergibt sich für R_1 ein Wert von etwa 10kOhm.&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Beim 74LS14 von Hitachi z.&amp;amp;nbsp;B. sind die oberen und unteren Schaltschwellwerte unterschiedlich. Es muss darauf geachtet werden, dass U_{th} beim Entladen die untere Schwelle und U_{th} beim Laden die obere Schwelle einnimmt.&lt;br /&gt;
&lt;br /&gt;
== Softwareentprellung ==&lt;br /&gt;
&lt;br /&gt;
In den Zeiten der elektronischen Auswertung von Tastern und Schaltern ist das softwaretechnische Entprellen oft billiger, als die Benutzung eines teuren Schalters. Daher werden heute z.B. auch Computertastaturen nicht mehr mit prellarmen Tasten oder Entprellkondensatoren ausgestattet.&lt;br /&gt;
&lt;br /&gt;
Bei Verwendung des in den meisten Geräten ohnehin vorhandenen Mikrocontrollers z.B., kann man sich die zusätzliche Hardware sparen, da die Entprellung in Software praktisch genauso gut funktioniert. Dabei ist nur zu beachten, dass zusätzliche Rechenleistung und je nach Umsetzung auch einige Hardwareressourcen (z.B. Timer) benötigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Flankenerkennung ===&lt;br /&gt;
Bei einem Taster gibt es insgesamt 4 theoretische Zustände:&lt;br /&gt;
&lt;br /&gt;
* 1. war nicht gedrückt und ist nicht gedrückt&lt;br /&gt;
* 2. war nicht gedrückt und ist gedrückt (steigende Flanke)&lt;br /&gt;
* 3. war gedrückt und ist immer noch gedrückt&lt;br /&gt;
* 4. war gedrückt und ist nicht mehr gedrückt (fallende Flanke)&lt;br /&gt;
&lt;br /&gt;
Diese einzelnen Zustände lassen sich jetzt bequem abfragen/durchlaufen. Die Entprellung geschieht dabei durch die ganze Laufzeit des Programms. Die Taster werden hierbei als Active-Low angeschlossen, um die internen Pull-Ups zu nutzen.&lt;br /&gt;
&lt;br /&gt;
Diese Routine gibt für den Zustand &amp;quot;steigende Flanke&amp;quot; den Wert &amp;quot;1&amp;quot; zurück, sonst &amp;quot;0&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define TASTERPORT PINC&lt;br /&gt;
#define TASTERBIT PINC1&lt;br /&gt;
&lt;br /&gt;
char taster(void)&lt;br /&gt;
{&lt;br /&gt;
    static unsigned char zustand;&lt;br /&gt;
    char rw = 0;&lt;br /&gt;
&lt;br /&gt;
    if(zustand == 0 &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gedrueckt (steigende Flanke)&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 1;&lt;br /&gt;
        rw = 1;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 1 &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gehalten&lt;br /&gt;
    {&lt;br /&gt;
         zustand = 2;&lt;br /&gt;
         rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 2 &amp;amp;&amp;amp; (TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird losgelassen (fallende Flanke)&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 3;&lt;br /&gt;
        rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else if (zustand == 3 &amp;amp;&amp;amp; (TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster losgelassen&lt;br /&gt;
    {&lt;br /&gt;
        zustand = 0;&lt;br /&gt;
        rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return rw;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Erweiterung, damit beliebig lange das Halten einer Taste erkannt wird kann man ganz einfach so implementieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // zustand kann entweder zum ersten mal als gehalten detektiert werden oder aber jedes weitere mal&lt;br /&gt;
    else if (((zustand == 1) || (zustand == 2)) &amp;amp;&amp;amp; !(TASTERPORT &amp;amp; (1&amp;lt;&amp;lt;TASTERBIT)))   //Taster wird gehalten&lt;br /&gt;
    {&lt;br /&gt;
         zustand = 2;&lt;br /&gt;
         rw = 0;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Warteschleifen-Verfahren ===&lt;br /&gt;
&lt;br /&gt;
Soll nun mit einem Mikrocontroller gezählt werden, wie oft ein Kontakt oder ein Relais geschaltet wird, muss das Prellen des Kontaktes exakt berücksichtigt - und von einem gewollten Mehrfachschalten abgegrenzt werden, da sonst  möglicherweise Fehlimpulse gezählt- oder andererseits echte Schaltvorgänge übersprungen werden. Dies muss beim Schreiben des Programms hinsichtlich des Abtastens des Kontaktes unbedingt Rechnung getragen werden.&lt;br /&gt;
&lt;br /&gt;
Beim folgenden einfachen Beispiel für eine Entprellung ist zu beachten, dass der AVR im Falle eines Tastendrucks 200ms wartet, also brach liegt. Bei zeitkritischen Anwendungen sollte man ein anderes Verfahren nutzen (z.&amp;amp;nbsp;B. Abfrage der Tastenzustände in einer Timer-Interrupt-Service-Routine).&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;inttypes.h&amp;gt;&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#warning &amp;quot;F_CPU war noch nicht definiert, wird nun mit 3686400 definiert&amp;quot;&lt;br /&gt;
#define F_CPU 3686400UL     /* Quarz mit 3.6864 Mhz  */&lt;br /&gt;
#endif&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;     /* bei alter avr-libc: #include &amp;lt;avr/delay.h&amp;gt; */      &lt;br /&gt;
&lt;br /&gt;
/* Einfache Funktion zum Entprellen eines Tasters */&lt;br /&gt;
inline uint8_t debounce(volatile uint8_t *port, uint8_t pin)&lt;br /&gt;
{&lt;br /&gt;
    if ( !(*port &amp;amp; (1 &amp;lt;&amp;lt; pin)) )&lt;br /&gt;
    {&lt;br /&gt;
        /* Pin wurde auf Masse gezogen, 100ms warten   */&lt;br /&gt;
        _delay_ms(50);   // Maximalwert des Parameters an _delay_ms &lt;br /&gt;
        _delay_ms(50);   // beachten, vgl. Dokumentation der avr-libc&lt;br /&gt;
        if ( *port &amp;amp; (1 &amp;lt;&amp;lt; pin) )&lt;br /&gt;
        {&lt;br /&gt;
            /* Anwender Zeit zum Loslassen des Tasters geben */&lt;br /&gt;
            _delay_ms(50);&lt;br /&gt;
            _delay_ms(50); &lt;br /&gt;
            return 1;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    DDRB &amp;amp;= ~( 1 &amp;lt;&amp;lt; PB0 );        /* PIN PB0 auf Eingang Taster)  */&lt;br /&gt;
    PORTB |= ( 1 &amp;lt;&amp;lt; PB0 );        /* Pullup-Widerstand aktivieren */&lt;br /&gt;
    ...&lt;br /&gt;
    if (debounce(&amp;amp;PINB, PB0))&lt;br /&gt;
    {&lt;br /&gt;
        /* Falls Taster an PIN PB0 gedrueckt     */&lt;br /&gt;
        /* LED an Port PD7 an- bzw. ausschalten: */&lt;br /&gt;
        PORTD = PORTD ^ ( 1 &amp;lt;&amp;lt; PD7 );&lt;br /&gt;
    }&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die obige Routine hat leider mehrere Nachteile:&lt;br /&gt;
* sie detektiert nur das Loslassen (unergonomisch)&lt;br /&gt;
* sie verzögert die Mainloop immer um 100ms bei gedrückter Taste&lt;br /&gt;
* sie verliert Tastendrücke, je mehr die Mainloop zu tun hat.&lt;br /&gt;
&lt;br /&gt;
Eine ähnlich einfach zu benutzende Routine, aber ohne all diese Nachteile findet sich im Forenthread&lt;br /&gt;
[http://www.mikrocontroller.net/topic/164194#new Entprellung für Anfänger]&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;DEBOUNCE&#039;&#039; Befehl in dem BASIC-Dialekt BASCOM für AVR ist ebenfalls nach dem Warteschleifen-Verfahren programmiert. Die Wartezeit beträgt standardmäßig 25 ms, kann aber vom Anwender überschrieben werden. Vgl.  [http://avrhelp.mcselec.com/bascom-avr.html?DEBOUNCE BASCOM Online-Manual zu DEBOUNCE].&lt;br /&gt;
&lt;br /&gt;
Eine C-Implementierung für eine Tastenabfrage mit Warteschleife ist im Artikel [[AVR-GCC-Tutorial#IO-Register_als_Parameter_und_Variablen|AVR-GCC-Tutorial: IO-Register als Parameter und Variablen]] angeben.&lt;br /&gt;
&lt;br /&gt;
Der Nachteil dieses Verfahrens ist, dass der Controller durch die Warteschleife blockiert wird. Günstiger ist die Implementierung mit einem Timer-Interrupt.&lt;br /&gt;
&lt;br /&gt;
==== Warteschleifenvariante mit Maske und Pointer (nach Christian Riggenbach) ====&lt;br /&gt;
&lt;br /&gt;
Hier eine weitere Funktion, um Taster zu entprellen: Durch den zusätzlichen Code kann eine Entprellzeit von durchschnittlich 1-3ms (mindestens 8*150µs = 1ms) erreicht werden. Grundsätzlich prüft die Funktion den Pegel der Pins auf einem bestimmten Port. Wenn die/der Pegel 8 Mal konstant war, wird die Schleife verlassen. Diese Funktion kann sehr gut eingesetzt werden, um in einer Endlosschleife Taster anzufragen, da sie, wie erwähnt, eine kurze Wartezeit hat.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void entprellung( volatile uint8_t *port, uint8_t maske ) {&lt;br /&gt;
  uint8_t   port_puffer;&lt;br /&gt;
  uint8_t   entprellungs_puffer;&lt;br /&gt;
&lt;br /&gt;
  for( entprellungs_puffer=0 ; entprellungs_puffer!=0xff ; ) {&lt;br /&gt;
    entprellungs_puffer&amp;lt;&amp;lt;=1;&lt;br /&gt;
    port_puffer = *port;&lt;br /&gt;
    _delay_us(150);&lt;br /&gt;
    if( (*port &amp;amp; maske) == (port_puffer &amp;amp; maske) )&lt;br /&gt;
      entprellungs_puffer |= 0x01;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion wird wie folgt aufgerufen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  // Bugfix 20100414&lt;br /&gt;
  // http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass&lt;br /&gt;
  entprellung( &amp;amp;PINB, (1&amp;lt;&amp;lt;PINB2) ); // ggf. Prellen abwarten &lt;br /&gt;
  if( PINB &amp;amp; (1&amp;lt;&amp;lt;PINB2) )           // dann stabilen Wert einlesen&lt;br /&gt;
  {&lt;br /&gt;
    // mach was&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    // mach was anderes&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Als Maske kann ein beliebiger Wert übergeben werden. Sie verhindert, dass nichtverwendete Taster die Entprellzeit negativ beeinflussen.&lt;br /&gt;
&lt;br /&gt;
==== Debounce-Makro von Peter Dannegger ====&lt;br /&gt;
&lt;br /&gt;
Peter Dannegger hat in [http://www.mikrocontroller.net/topic/164194#1566921 &amp;quot;Entprellen für Anfänger&amp;quot;] folgende vereinfachtes Entprellverfahren beschrieben. Das Makro arbeitet in der Originalversion mit &#039;&#039;active low&#039;&#039; geschalteten Tastern, kann aber einfach für  &#039;&#039;active high&#039;&#039; geschaltete Taster angepasst werden ([[Pollin Funk-AVR-Evaluationsboard#Tasty Reloaded|Tasty Reloaded]]). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*                      Not so powerful Debouncing Example              */&lt;br /&gt;
/*                      No Interrupt needed                             */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*              Author: Peter Dannegger                                 */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
// Target: ATtiny13&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#define F_CPU 9.6e6&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define debounce( port, pin )                                         \&lt;br /&gt;
({                                                                    \&lt;br /&gt;
  static uint8_t flag = 0;     /* new variable on every macro usage */  \&lt;br /&gt;
  uint8_t i = 0;                                                      \&lt;br /&gt;
                                                                      \&lt;br /&gt;
  if( flag ){                  /* check for key release: */           \&lt;br /&gt;
    for(;;){                   /* loop ... */                         \&lt;br /&gt;
      if( !(port &amp;amp; 1&amp;lt;&amp;lt;pin) ){  /* ... until key pressed or ... */     \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                       \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                     \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms released */     \&lt;br /&gt;
        flag = 0;              /* clear press flag */                 \&lt;br /&gt;
        i = 0;                 /* 0 = key release debounced */        \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
    }                                                                 \&lt;br /&gt;
  }else{                       /* else check for key press: */        \&lt;br /&gt;
    for(;;){                   /* loop ... */                         \&lt;br /&gt;
      if( (port &amp;amp; 1&amp;lt;&amp;lt;pin) ){   /* ... until key released or ... */    \&lt;br /&gt;
        i = 0;                 /* 0 = bounce */                       \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
      _delay_us( 98 );         /* * 256 = 25ms */                     \&lt;br /&gt;
      if( --i == 0 ){          /* ... until key &amp;gt;25ms pressed */      \&lt;br /&gt;
        flag = 1;              /* set press flag */                   \&lt;br /&gt;
        i = 1;                 /* 1 = key press debounced */          \&lt;br /&gt;
        break;                                                        \&lt;br /&gt;
      }                                                               \&lt;br /&gt;
    }                                                                 \&lt;br /&gt;
  }                                                                   \&lt;br /&gt;
  i;                           /* return value of Macro */            \&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
   Testapplication&lt;br /&gt;
 */&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB  &amp;amp;= ~(1&amp;lt;&amp;lt;PB0);&lt;br /&gt;
  PORTB |=   1&amp;lt;&amp;lt;PB0;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
  DDRB  &amp;amp;= ~(1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
  PORTB |=   1&amp;lt;&amp;lt;PB1;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  for(;;){&lt;br /&gt;
    if( debounce( PINB, PB1 ) )&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
    if( debounce( PINB, PB0 ) )&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn das Makro für die gleiche Taste (Pin) an mehreren Stellen aufgerufen werden soll, muss eine Funktion angelegt werden, damit beide Aufrufe an die gleiche Zustandsvariable &#039;&#039;flag&#039;&#039; auswerten [http://www.mikrocontroller.net/topic/195914#1918727]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Hilfsfunktion&lt;br /&gt;
uint8_t debounce_C1( void )&lt;br /&gt;
{&lt;br /&gt;
  return debounce(PINC, PC1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Beispielanwendung&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
  DDRB  |=   1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  DDRC  &amp;amp;= ~(1&amp;lt;&amp;lt;PC1);&lt;br /&gt;
  PORTC |=   1&amp;lt;&amp;lt;PC1; // Pullup für Taster&lt;br /&gt;
&lt;br /&gt;
  for(;;){&lt;br /&gt;
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB2;&lt;br /&gt;
    if( debounce_C1() )  // nicht: debounce(PINC, PC1)&lt;br /&gt;
      PORTB ^= 1&amp;lt;&amp;lt;PB3;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Timer-Verfahren (nach Peter Dannegger) ===&lt;br /&gt;
&lt;br /&gt;
==== Grundroutine (AVR Assembler) ====&lt;br /&gt;
&lt;br /&gt;
Siehe dazu: [http://www.mikrocontroller.net/forum/read-4-20435.html#new Forum] &lt;br /&gt;
&lt;br /&gt;
Vorteile&lt;br /&gt;
* besonders kurzer Code&lt;br /&gt;
* schnell&lt;br /&gt;
&lt;br /&gt;
Außerdem können 8 Tasten (aktiv low) gleichzeitig bearbeitet werden, es dürfen also&lt;br /&gt;
alle exakt zur selben Zeit gedrückt werden. Andere Routinen können z.&amp;amp;nbsp;B. nur eine Taste verarbeiten, d.h. die zuerst oder zuletzt gedrückte gewinnt, oder es kommt Unsinn heraus.&lt;br /&gt;
&lt;br /&gt;
Die eigentliche Einlese- und Entprellroutine ist nur 8 Instruktionen&lt;br /&gt;
kurz. Der entprellte Tastenzustand ist im Register &#039;&#039;key_state&#039;&#039;. Mit nur 2 weiteren Instruktionen wird dann der Wechsel von &#039;&#039;Taste offen&#039;&#039; zu&lt;br /&gt;
&#039;&#039;Taste gedrückt&#039;&#039; erkannt und im Register &#039;&#039;key_press&#039;&#039; abgelegt. Im Beispielcode werden dann damit 8 LEDs ein- und ausgeschaltet. Jede Taste entspricht einem Bit in den Registern, d.h. die Verarbeitung erfolgt bitweise mit logischen Operationen. Zum Verständnis empfiehlt es sich daher, die Logikgleichungen mit Gattern für ein Bit = eine Taste aufzumalen. Die Register kann man sich als Flipflops denken, die mit der Entprellzeit als Takt arbeiten. D.h. man kann das auch so z.&amp;amp;nbsp;B. in einem GAL22V10 realisieren.&lt;br /&gt;
&lt;br /&gt;
Als Kommentar sind neben den einzelnen Instruktionen alle 8 möglichen&lt;br /&gt;
Kombinationen der 3 Signale dargestellt.&lt;br /&gt;
&lt;br /&gt;
Beispielcode für AVR (Assembler):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.nolist&lt;br /&gt;
.include &amp;quot;c:\avr\inc\1200def.inc&amp;quot;&lt;br /&gt;
.list&lt;br /&gt;
.def  save_sreg         = r0&lt;br /&gt;
.def  iwr0              = r1&lt;br /&gt;
.def  iwr1              = r2&lt;br /&gt;
&lt;br /&gt;
.def  key_old           = r3&lt;br /&gt;
.def  key_state         = r4&lt;br /&gt;
.def  key_press         = r5&lt;br /&gt;
&lt;br /&gt;
.def  leds              = r16&lt;br /&gt;
.def  wr0               = r17&lt;br /&gt;
&lt;br /&gt;
.equ  key_port          = pind&lt;br /&gt;
.equ  led_port          = portb&lt;br /&gt;
&lt;br /&gt;
      rjmp   init&lt;br /&gt;
.org OVF0addr		;timer interrupt 24ms&lt;br /&gt;
      in     save_sreg, SREG&lt;br /&gt;
get8key:                               ;/old      state     iwr1      iwr0&lt;br /&gt;
      mov    iwr0, key_old             ;00110011  10101010            00110011&lt;br /&gt;
      in     key_old, key_port         ;11110000&lt;br /&gt;
      eor    iwr0, key_old             ;                              11000011&lt;br /&gt;
      com    key_old                   ;00001111&lt;br /&gt;
      mov    iwr1, key_state           ;                    10101010&lt;br /&gt;
      or     key_state, iwr0           ;          11101011&lt;br /&gt;
      and    iwr0, key_old             ;                              00000011&lt;br /&gt;
      eor    key_state, iwr0           ;          11101000&lt;br /&gt;
      and    iwr1, iwr0                ;                    00000010&lt;br /&gt;
      or     key_press, iwr1           ;store key press detect&lt;br /&gt;
;&lt;br /&gt;
;			insert other timer functions here&lt;br /&gt;
;&lt;br /&gt;
      out    SREG, save_sreg&lt;br /&gt;
      reti&lt;br /&gt;
;-------------------------------------------------------------------------&lt;br /&gt;
init:&lt;br /&gt;
      ldi    wr0, 0xFF&lt;br /&gt;
      out    ddrb, wr0&lt;br /&gt;
      ldi    wr0, 1&amp;lt;&amp;lt;CS02 | 1&amp;lt;&amp;lt;CS00    ;divide by 1024 * 256&lt;br /&gt;
      out    TCCR0, wr0&lt;br /&gt;
      ldi    wr0, 1&amp;lt;&amp;lt;TOIE0             ;enable timer interrupt&lt;br /&gt;
      out    TIMSK, wr0&lt;br /&gt;
&lt;br /&gt;
      clr    key_old&lt;br /&gt;
      clr    key_state&lt;br /&gt;
      clr    key_press&lt;br /&gt;
      ldi    leds, 0xFF&lt;br /&gt;
main: cli&lt;br /&gt;
      eor    leds, key_press           ;toggle LEDs&lt;br /&gt;
      clr    key_press                 ;clear, if key press action done&lt;br /&gt;
      sei&lt;br /&gt;
      out    led_port, leds&lt;br /&gt;
      rjmp   main&lt;br /&gt;
;-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Komfortroutine (C für AVR) ====&lt;br /&gt;
&lt;br /&gt;
Siehe dazu: [http://www.mikrocontroller.net/forum/read-4-310276.html Forum]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung&#039;&#039;&#039; Wenn statt active-low (Ruhezustand High) active-high (Ruhezustand Low) verwendet wird muss eine Zeile geändert werden siehe:&lt;br /&gt;
[http://www.mikrocontroller.net/forum/read-4-310276.html gesamter Beitrag im Forum], &lt;br /&gt;
[http://www.mikrocontroller.net/topic/48465#606555 Stelle 1 im Beitrag], ([http://www.mikrocontroller.net/topic/48465#2306398 Stelle 2 im Beitrag] muss *nicht* geändert werden, da hier die Polarität gar keinen Einfluß hat).&lt;br /&gt;
&lt;br /&gt;
Funktionsprinzip wie oben plus zusätzliche Features:  &lt;br /&gt;
* Kann Tasten sparen durch unterschiedliche Aktionen bei kurzem oder langem Drücken&lt;br /&gt;
* Wiederholfunktion, z.&amp;amp;nbsp;B. für die Eingabe von Werten&lt;br /&gt;
&lt;br /&gt;
Das Programm ist für avr-gcc/avr-libc geschrieben, kann aber mit ein paar Anpassungen auch mit anderen Compilern und Mikrocontrollern verwendet werden. Eine Portierung für den AT91SAM7 findet man [http://www.google.com/codesearch?q=show:ac2viP-2E2Y:pzkOO5QRsoc:RPICuprYy-A&amp;amp;sa=N&amp;amp;cd=1&amp;amp;ct=rc&amp;amp;cs_p=svn://mikrocontroller.net/mp3dec/trunk&amp;amp;cs_f=keys.c#a0 hier] (aus dem Projekt [[ARM MP3/AAC Player]]).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*                      Debouncing 8 Keys                               */&lt;br /&gt;
/*                      Sampling 4 Times                                */&lt;br /&gt;
/*                      With Repeat Function                            */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/*              Author: Peter Dannegger                                 */&lt;br /&gt;
/*                      danni@specs.de                                  */&lt;br /&gt;
/*                                                                      */&lt;br /&gt;
/************************************************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.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;
&lt;br /&gt;
#ifndef F_CPU&lt;br /&gt;
#define F_CPU           1000000                   // processor clock frequency&lt;br /&gt;
#warning kein F_CPU definiert&lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
#define KEY_DDR         DDRB&lt;br /&gt;
#define KEY_PORT        PORTB&lt;br /&gt;
#define KEY_PIN         PINB&lt;br /&gt;
#define KEY0            0&lt;br /&gt;
#define KEY1            1&lt;br /&gt;
#define KEY2            2&lt;br /&gt;
#define ALL_KEYS        (1&amp;lt;&amp;lt;KEY0 | 1&amp;lt;&amp;lt;KEY1 | 1&amp;lt;&amp;lt;KEY2)&lt;br /&gt;
 &lt;br /&gt;
#define REPEAT_MASK     (1&amp;lt;&amp;lt;KEY1 | 1&amp;lt;&amp;lt;KEY2)       // repeat: key1, key2&lt;br /&gt;
#define REPEAT_START    50                        // after 500ms&lt;br /&gt;
#define REPEAT_NEXT     20                        // every 200ms&lt;br /&gt;
&lt;br /&gt;
#define LED_DDR         DDRA&lt;br /&gt;
#define LED_PORT        PORTA&lt;br /&gt;
#define LED0            0&lt;br /&gt;
#define LED1            1&lt;br /&gt;
#define LED2            2&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t key_state;                                // debounced and inverted key state:&lt;br /&gt;
                                                  // bit = 1: key pressed&lt;br /&gt;
volatile uint8_t key_press;                                // key press detect&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t key_rpt;                                  // key long press and repeat&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER0_OVF_vect )                            // every 10ms&lt;br /&gt;
{&lt;br /&gt;
  static uint8_t ct0, ct1, rpt;&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms&lt;br /&gt;
 &lt;br /&gt;
  i = key_state ^ ~KEY_PIN;                       // key changed ?&lt;br /&gt;
  ct0 = ~( ct0 &amp;amp; i );                             // reset or count ct0&lt;br /&gt;
  ct1 = ct0 ^ (ct1 &amp;amp; i);                          // reset or count ct1&lt;br /&gt;
  i &amp;amp;= ct0 &amp;amp; ct1;                                 // count until roll over ?&lt;br /&gt;
  key_state ^= i;                                 // then toggle debounced state&lt;br /&gt;
  key_press |= key_state &amp;amp; i;                     // 0-&amp;gt;1: key press detect&lt;br /&gt;
 &lt;br /&gt;
  if( (key_state &amp;amp; REPEAT_MASK) == 0 )            // check repeat function&lt;br /&gt;
     rpt = REPEAT_START;                          // start delay&lt;br /&gt;
  if( --rpt == 0 ){&lt;br /&gt;
    rpt = REPEAT_NEXT;                            // repeat delay&lt;br /&gt;
    key_rpt |= key_state &amp;amp; REPEAT_MASK;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key has been pressed. Each pressed key is reported&lt;br /&gt;
// only once&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_press( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read and clear atomic !&lt;br /&gt;
  key_mask &amp;amp;= key_press;                          // read key(s)&lt;br /&gt;
  key_press ^= key_mask;                          // clear key(s)&lt;br /&gt;
  sei();&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key has been pressed long enough such that the&lt;br /&gt;
// key repeat functionality kicks in. After a small setup delay&lt;br /&gt;
// the key is reported being pressed in subsequent calls&lt;br /&gt;
// to this function. This simulates the user repeatedly&lt;br /&gt;
// pressing and releasing the key.&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_rpt( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read and clear atomic !&lt;br /&gt;
  key_mask &amp;amp;= key_rpt;                            // read key(s)&lt;br /&gt;
  key_rpt ^= key_mask;                            // clear key(s)&lt;br /&gt;
  sei();&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
// check if a key is pressed right now&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_state( uint8_t key_mask )&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
  key_mask &amp;amp;= key_state;&lt;br /&gt;
  return key_mask;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_short( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  cli();                                          // read key state and key press atomic !&lt;br /&gt;
  return get_key_press( ~key_state &amp;amp; key_mask );&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
///////////////////////////////////////////////////////////////////&lt;br /&gt;
//&lt;br /&gt;
uint8_t get_key_long( uint8_t key_mask )&lt;br /&gt;
{&lt;br /&gt;
  return get_key_press( get_key_rpt( key_mask ));&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
int main( void )&lt;br /&gt;
{&lt;br /&gt;
  LED_PORT = 0xFF;&lt;br /&gt;
  LED_DDR = 0xFF;                     &lt;br /&gt;
&lt;br /&gt;
  // Configure debouncing routines&lt;br /&gt;
  KEY_DDR &amp;amp;= ~ALL_KEYS;                // configure key port for input&lt;br /&gt;
  KEY_PORT |= ALL_KEYS;                // and turn on pull up resistors&lt;br /&gt;
&lt;br /&gt;
  TCCR0 = (1&amp;lt;&amp;lt;CS02)|(1&amp;lt;&amp;lt;CS00);         // divide by 1024&lt;br /&gt;
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms&lt;br /&gt;
  TIMSK |= 1&amp;lt;&amp;lt;TOIE0;                   // enable timer interrupt&lt;br /&gt;
&lt;br /&gt;
  sei();&lt;br /&gt;
&lt;br /&gt;
  while(1){&lt;br /&gt;
    if( get_key_short( 1&amp;lt;&amp;lt;KEY1 ))&lt;br /&gt;
      LED_PORT ^= 1&amp;lt;&amp;lt;LED1;&lt;br /&gt;
 &lt;br /&gt;
    if( get_key_long( 1&amp;lt;&amp;lt;KEY1 ))&lt;br /&gt;
      LED_PORT ^= 1&amp;lt;&amp;lt;LED2;&lt;br /&gt;
 &lt;br /&gt;
    // single press and repeat&lt;br /&gt;
 &lt;br /&gt;
    if( get_key_press( 1&amp;lt;&amp;lt;KEY2 ) || get_key_rpt( 1&amp;lt;&amp;lt;KEY2 )){&lt;br /&gt;
      uint8_t i = LED_PORT;&lt;br /&gt;
 &lt;br /&gt;
      i = (i &amp;amp; 0x07) | ((i &amp;lt;&amp;lt; 1) &amp;amp; 0xF0);&lt;br /&gt;
      if( i &amp;lt; 0xF0 )&lt;br /&gt;
        i |= 0x08;&lt;br /&gt;
      LED_PORT = i;      &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das single-press-und-repeat-Beispiel geht nicht in jeder Beschaltung; folgendes Beispiel sollte universeller sein (einzelne LED an/aus):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// single press and repeat&lt;br /&gt;
if( get_key_press( 1&amp;lt;&amp;lt;KEY2 ) || get_key_rpt( 1&amp;lt;&amp;lt;KEY2 ))&lt;br /&gt;
    LED_PORT ^=0x08;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Funktionsweise =====&lt;br /&gt;
Der Code basiert auf 8 parallelen vertikalen Zählern, die über die Variablen ct0 und ct1 aufgebaut werden&lt;br /&gt;
&lt;br /&gt;
[[Bild:VertCount.png|framed|center|&#039;&#039;&#039;8 vertikale Zähler in 2 8-Bit Variablen&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
wobei jeweils ein Bit in ct0 mit dem gleichwertigen Bit in ct1 zusammengenommen einen 2-Bit-Zähler bildet.&lt;br /&gt;
Der Code der sich um die 8 Zähler kümmert, ist so geschrieben, daß er alle 8 Zähler gemeinsam parallel behandelt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i = key_state ^ ~KEY_PIN;                       // key changed ?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
i enthält an dieser Stelle für jede Taste, die sich im Vergleich mit dem vorhergehenden entprellten Zustand (keystate) verändert hat, ein 1 Bit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  ct0 = ~( ct0 &amp;amp; i );                             // reset or count ct0&lt;br /&gt;
  ct1 = ct0 ^ (ct1 &amp;amp; i);                          // reset or count ct1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese beiden Anweisungen erniedrigen den 2-Bit Zähler ct0/ct1 für jedes Bit um 1, welches in i gesetzt ist. Liegt an der entsprechenden Stelle in i ein 0 Bit vor (keine Änderung des Zustands), so wird der Zähler ct0/ct1 für dieses Bit auf 1 gesetzt.&lt;br /&gt;
Der Grundzustand des Zählers ist als ct0 == 1 und ct1 == 1 (Wert 3). Der Zähler zählt daher mit jedem ISR Aufruf, bei dem die Taste im Vergleich zu keystate als verändert erkannt wurde&lt;br /&gt;
&lt;br /&gt;
   ct1   ct0&lt;br /&gt;
     1    1   // 3&lt;br /&gt;
     1    0   // 2&lt;br /&gt;
     0    1   // 1&lt;br /&gt;
     0    0   // 0&lt;br /&gt;
     1    1   // 3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  i &amp;amp;= ct0 &amp;amp; ct1;                                 // count until roll over ?&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
in i bleibt nur dort ein 1-Bit erhalten, wo sowohl in ct1 als auch in ct0 ein 1 Bit vorgefunden wird, der betreffenede Zähler also bis 3 zählen konnte. Durch die zusätzliche Verundung mit i wird der Fall abgefangen, dass ein konstanter Zählerwert von 3 in i ein 1 Bit hinterlässt. Im Endergebnis bedeutet dass, dass nur ein Zählerwechsel von 0 auf 3 zu einem 1 Bit an der betreffenden Stelle in i führt, aber auch nur dann, wenn in i an dieser Bitposition ebenfalls ein 1 Bit war (welches wiederrum deswegen auf 1 war, weil an diesem Eingabeport eine Veränderung zum letzten bekannten entprellten Zustand festgestellt wurde). alles zusammengenommen heißt das, dass ein Tastendruck dann erkannt wird, wenn die Taste 4 mal hintereinander in einem anderen Zustand vorgefunden wurde als dem zuletzt bekannten entprellten Tastenzustand.&lt;br /&gt;
&lt;br /&gt;
An dieser Stelle ist i daher ein Vektor von 8 Bits, von denen jedes einzelne der Bits darüber Auskunft gibt, ob die entsprechende Taste mehrmals hintereinander im selben Zustand angetroffen wurde, der nicht mit dem zuletzt bekannten Tastenzustand übereinstimmt. Ist das der Fall, dann wird eine entsprechende Veränderung des Tastenzustands in key_state registriert&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  key_state ^= i;                                 // then toggle debounced state&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
und wenn sich in key_state das entsprechende Bit von 0 auf 1 verändert hat, wird dieses Ereignis als &#039;Taste wurde niedergedrückt&#039; gewertet.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
  key_press |= key_state &amp;amp; i;                     // 0-&amp;gt;1: key press detect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit ist der Tasteneingang entprellt. Und zwar sowohl beim Drücken einer Taste als auch beim Loslassen (damit Tastenpreller beim Loslassen nicht mit dem Niederdrücken einer Taste verwechselt werden). Der weitere Code beschäftigt sich dann nur noch damit, diesen entprellten Tastenzustand weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Codeteil sieht durch die Verwendung der vielen bitweisen Operationen relativ komplex aus. Behält man aber im Hinterkopf, dass einige der bitweisen wie ein &#039;paralles If&#039; gleichzeitig auf allen 8 Bits eingesetzt werden, dann vereinfacht sich vieles. Ein&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    key_press |= key_state &amp;amp; i;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
ist nichts anderes als ein&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
    // teste ob Bit 0 sowohl in key_state als auch in i gesetzt ist&lt;br /&gt;
    // und setze Bit 0 in key_press, wenn das der Fall ist&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 0 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 0 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // Bit 1&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 1 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 1 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 1 );&lt;br /&gt;
&lt;br /&gt;
    // Bit 2&lt;br /&gt;
    if( ( key_state &amp;amp; ( 1 &amp;lt;&amp;lt; 2 ) ) &amp;amp;&amp;amp;&lt;br /&gt;
        ( i &amp;amp; ( 1 &amp;lt;&amp;lt; 2 ) )&lt;br /&gt;
       key_press |= ( 1 &amp;lt;&amp;lt; 2 );&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
nur als wesentlich kompaktere Operation ausgeführt und für alle 8 Bits gleichzeitig.&lt;br /&gt;
Die Kürze und Effizienz dieser paar Codezeilen ergibt sich aus dem Umstand, dass jedes Bit in den Variablen für eine Taste steht und alle 8 (maximal möglichen) Tasten gleichzeitig die Operationen durchlaufen.&lt;br /&gt;
&lt;br /&gt;
===== Reduziert auf lediglich 1 Taste =====&lt;br /&gt;
Diskussionen im Forum zeigen immer wieder, dass viele eine Abneigung gegen diesen Code haben, weil er ihnen sehr kompliziert vorkommt.&lt;br /&gt;
&lt;br /&gt;
Der Code ist nicht leicht zu analysieren und er zieht alle Register dessen, was möglich ist, um sowohl Laufzeit als auch Speicherverbrauch einzusparen. Oft hört man auch das Argument: Ich benötige ja nur eine Entprellung für 1 Taste, gibt es da nichts Einfacheres?&lt;br /&gt;
&lt;br /&gt;
Hier ist die &#039;Langform&#039; des Codes, so wie man das für lediglich 1 Taste schreiben würde, wenn man exakt dasselbe Entprellverfahren einsetzen würde. Man sieht: Da ist keine Hexerei dabei: In key_state wird der letzte bekannte entprellte Zustand der Taste gehalten. Der Pin-Eingang wird mit diesem Zustand verglichen und wenn sich die beiden unterscheiden, dann wird ein Zähler heruntergezählt. Produziert dieses herunterzählen einen Unterlauf des Zählers, dann gilt die Taste als entprellt und wenn dann auch noch die Taste gerade gedrückt ist, dann wird dieses in key_press entsprechend vermerkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t key_state;&lt;br /&gt;
uint8_t key_counter;&lt;br /&gt;
volatile uint8_t key_press;&lt;br /&gt;
&lt;br /&gt;
ISR( ... Overflow ... )&lt;br /&gt;
{&lt;br /&gt;
  uint8_t input = KEY_PIN &amp;amp; ( 1 &amp;lt;&amp;lt; KEY0 );&lt;br /&gt;
&lt;br /&gt;
  if( input != key_state ) {&lt;br /&gt;
    key_counter--;&lt;br /&gt;
    if( key_counter == 0xFF ) {&lt;br /&gt;
      key_counter = 3;&lt;br /&gt;
      key_state = input;&lt;br /&gt;
      if( input )&lt;br /&gt;
        key_press = TRUE;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
    key_counter = 3;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t get_key_press()&lt;br /&gt;
{&lt;br /&gt;
  uint8_t result;&lt;br /&gt;
&lt;br /&gt;
  cli();&lt;br /&gt;
  result = key_press;&lt;br /&gt;
  key_press = FALSE;&lt;br /&gt;
  sei();&lt;br /&gt;
&lt;br /&gt;
  return result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der vollständige Entprellcode, wie weiter oben gelistet, besticht jetzt aber darin, dass er compiliert kleiner ist als diese anschaulichere Variante für lediglich 1 Taste. Und das bei gleichzeitig erhöhter Funktionalität. Denn zb. ein Autorepeat ist in diesem Code noch gar nicht eingebaut. Und spätestens wenn man dann eine 2.te Taste entprellen möchte, dann ist auch der SRAM-Speicherverbrauch dieser Langform höher als der des Originals für 8 Tasten. Daraus folgt: Selbst für lediglich 1 Taste ist die Originalroutine die bessere Wahl.&lt;br /&gt;
&lt;br /&gt;
Und wegen der Komplexität mal eine Frage: Sind Sie selbst in der Lage eine entsprechend effiziente sqrt() Funktion zu schreiben, wie die, die sie in der Standard-C-Bibliothek vorfinden? Nein? Dann dürften Sie eigentlich Ihrer Argumentation nach die Bibliotheksfunktion sqrt() nicht verwenden, sondern müssten sich statt dessen selbst eine Wurzel-Funktion schreiben.&lt;br /&gt;
&lt;br /&gt;
=== Selbstsättigender Filter (nach Jürgen Schuhmacher) ===&lt;br /&gt;
Durch die Nutzung der diskreten Signalanalyse in Software kann die Funktionalität einer einfachen Entprellung mit einem Widerstand, einem Kondensator und einem Schmitttrigger wie in Hardware nachgebildet werden, indem ein abstrakter IIR-Filter benutzt wird, der eine Kondensatorladekurve emuliert. Mit der Vorschrift Y(t) = k Y(t-1) + Input wird ein einfaches Filter erzeugt, dass dem Eingangswert träge folgt. Bei Überschreiten eines bestimmten Wertes erfolgt mit einer einfachen Abfrage das Schalten des Ausgangssignals.&lt;br /&gt;
&lt;br /&gt;
Für Assembler und VHDL bei FPGAs eignet sich aufgrund der leicht zu implementierenden binären Operationen folgende Darstellung mit einer Auflösung des Filterwertspeichers von nur 8 bit: Wert_Neu = Wert_Alt - Wert_Alt/16 + 16*(Taste = True). Der Filterwert bildet dann den gedämpften Verlauf des Eingangs (flankenverschliffen) ab und kann Prellen bis nahe an den Grenzbereich zum schnellen Tasten unterdrücken. Der Ausgangswert ist dann einfach das höchstwertige Bit des Filterwertes.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Entprellung mit IIR-Filter.gif]]&lt;br /&gt;
&lt;br /&gt;
Dazu muss das Signal des Tasters idealerweise um den Faktor 10-20 schneller abgetastet werden, als die höchste gewünschte Tippgeschwindigkeit vorgibt. Noch schneller abzutasten ist möglich, führt aber zu mehr Bedarf an Bits beim Filter. Die Schmittriggerfunktion kann dadurch gebildet werden, dass eine 1 am Ausgang bei z.B. Überschreiten einer 55% Grenze und eine 0 bei Unterschreitung der 45%-Grenze ausgeben wird. Im Zwischenbereich wird der alte Wert gehalten.&lt;br /&gt;
&lt;br /&gt;
=== Einfacher Mittelwertfilter (nach Lothar Miller) ===&lt;br /&gt;
Für digitale Schaltungen oder PLDs empfiehlt sich ein FIR-Filter mit aneinandergereihten FlipFlops. Man schiebt das Eingangssignal in eine FlipFlop-Kette und schaltet oberhalb der Mitte um:&lt;br /&gt;
&lt;br /&gt;
SignalInput -&amp;gt; FF1 -&amp;gt; FF2 -&amp;gt; FF3 -&amp;gt; FF4 -&amp;gt; FF5 -&amp;gt; FF6 -&amp;gt; FF7 -&amp;gt; FF8&lt;br /&gt;
&lt;br /&gt;
Wenn alle FFs = 1 (Summe der FFs=8) dann SignalOutput = 1&amp;lt;br /&amp;gt;&lt;br /&gt;
Wenn alle FFs = 0 (Summe der FFs=0) dann SignalOutput = 0&lt;br /&gt;
&lt;br /&gt;
Dieses Verfahren kann sehr einfach in Logik abgebildet werden, weil für die Berechnung des Ausgangs nur ein NOR bz. ein AND Gatter nötig ist.&lt;br /&gt;
&lt;br /&gt;
== Gegenüberstellung der Verfahren ==&lt;br /&gt;
* HW - &amp;quot;entprellte Schalter&amp;quot;: Sehr teuer, grosse Bauform, verschleissbelastet, geringe Haltbarkeit&lt;br /&gt;
* HW - &amp;quot;Umschalter&amp;quot; : benötigt aufwändigeren Schalter, benötigt Elektronik&lt;br /&gt;
* HW - &amp;quot;Umschalter ohne FF&amp;quot; : benötigt aufwändigeren Schalter und kleiner Kondensator&lt;br /&gt;
* HW - &amp;quot;Kondensatorentprellung&amp;quot; : benötigt etwas mehr Platz, kommt mit schlechten Schaltern zurecht&lt;br /&gt;
&lt;br /&gt;
* SW - Flankenverfahren:&lt;br /&gt;
* SW - Warteschleife: Durch die Warteschleifen eine nicht zu vernachlässigende Verzögerung im Code. Speziell wenn mehrere Tasten zu überwachen sind, nicht unproblematisch&lt;br /&gt;
* SW - Timer: Universalfunktionalität, die durch geringen Speicherverbrauch, geringen Rechenzeitverbrauch und gute Funktion besticht. Der &#039;Verbrauch&#039; eines Timers sieht auf den ersten Blick schlimmer aus, als er ist, denn in den meisten Programmen hat man sowieso einen Basistimer für die Zeitsteuerung des Programms im Einsatz, der für die Tastenentprellung mitbenutzt werden kann.&lt;br /&gt;
* SW - Filter: sehr geringer Platzbedarf in FPGAs, relativ gute Wirkung&lt;br /&gt;
* SW - Filter 2: sehr geringer Platzbedarf, gute Wirkung&lt;br /&gt;
&lt;br /&gt;
== Links zum Thema ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/design/analog/4324067/Contact-debouncing-algorithm-emulates-Schmitt-trigger Contact-debouncing algorithm (Artikel)],  [http://www.edn.com/file/13370-70705di.pdf als PDF]&lt;br /&gt;
* [[AVR-Tutorial: Tasten]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#.28Tasten-.29Entprellung|AVR-GCC-Tutorial Tastenentprellung]]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-20435.html Beitrag im Forum, AVR Assembler]&lt;br /&gt;
* [http://www.ganssle.com/debouncing.pdf A guide to debouncing (engl.), praktische Erläuterungen zum Entprellen in Soft- und Hardware]&lt;br /&gt;
* [http://www.pololu.com/docs/0J16/all Understanding Destructive LC Voltage Spikes]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR]]&lt;br /&gt;
[[Kategorie:Signalverarbeitung]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_und_Linux&amp;diff=81581</id>
		<title>AVR und Linux</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_und_Linux&amp;diff=81581"/>
		<updated>2014-02-14T09:25:44Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Was ist zu beachten, wenn man mit einer Linux-Distribution AVRs programmieren möchte? Hier ist eine offene Liste von verschiedenen Tipps dazu...&lt;br /&gt;
&lt;br /&gt;
==Toolchain beschaffen==&lt;br /&gt;
&lt;br /&gt;
Eventuell sind die benötigten Pakete in der verwendeten Distribution enthalten. Einfach mal die Paketnamen und -beschreibungen nach &amp;quot;avr&amp;quot; durchsuchen.&lt;br /&gt;
&lt;br /&gt;
Diese fertigen Pakete sind aber nicht selten mit Vorsicht zu genießen, denn es gibt ein ganz grundsätzliches Problem. Im Prinzip kann man einen avr-gcc &amp;quot;bauen&amp;quot;, indem man sich die offiziellen GCC-Quellen besorgt und dann einfach für das Target avr übersetzt. Und in der Tat scheinen die in den Distributionspaketen enthaltenen Compiler nicht selten auf genau diesem Wege entstanden zu sein. Das Problem dabei ist, dass ein solcher avr-gcc niemals vollständig/aktuell ist, denn es gibt immer zusätzliche AVR spezifische Patches, die nicht in den offiziellen GCC-Quellen enthalten sind. Zum einen, weil der jeweilige Patch noch zu neu ist (die Übernahme in die offiziellen Quellen dauert seine Zeit), zum anderen weil er grundsätzlich nicht offiziell übernommen werden kann, da der Autor des Patches kein FSF-Assignment hat.&lt;br /&gt;
Ähnliches gilt auch für die weiteren Komponenten der Toolchain, z.B. binutils.&lt;br /&gt;
&lt;br /&gt;
Wer eine Distribution benutzt, die keine AVR-Pakete enthält, oder wer obigem Problem aus dem Weg gehen will, kann sich selber eine Toolchain mit den aktuellen Patches übersetzen. Ziemlich einfach geht das mit einem Skript von Bingo600, das man hier bekommen kann:&lt;br /&gt;
* http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=95328 (mit avr-libc 1.7.x)&lt;br /&gt;
* http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=42631 (mit avr-libc 1.6.x)&lt;br /&gt;
&lt;br /&gt;
Es gibt auch fertige Deb-Pakete, die auf der Basis dieses Skripts entstanden sind:&lt;br /&gt;
* http://www.wrightflyer.co.uk/avr-gcc/&lt;br /&gt;
&lt;br /&gt;
==Editor==&lt;br /&gt;
&lt;br /&gt;
Der Editor sollte das Bearbeiten vom Programm einfach machen. Evtl. nützlich sind Features, um z.&amp;amp;nbsp;B. per Knopfdruck zu kompilieren und das Programm auf den AVR zu übertragen. Ansonsten: Editoren sind Geschmackssache, d.h. ausprobieren. Hier eine Auswahl (alphabetisch):&lt;br /&gt;
&lt;br /&gt;
: [http://www.codeblocks.org/ Codeblocks] [gtk]&lt;br /&gt;
: [[AVR_Eclipse|Eclipse]] [http://www.eclipse.org/] [Java]&lt;br /&gt;
: [http://www.gnu.org/software/emacs/ Emacs] bzw. [http://www.xemacs.org/ XEmacs] [Textterminal oder X]&lt;br /&gt;
: [http://www.geany.org/ Geany] [gtk]&lt;br /&gt;
: [http://www.gnome.org/projects/gedit/ gedit] [GNOME]&lt;br /&gt;
: [http://kate-editor.org/ kate] [KDE]&lt;br /&gt;
: [http://www.kdevelop.org/ KDevelop] [KDE]&lt;br /&gt;
: [http://www.cadmaniac.org/projectMain.php?projectName=kontrollerlab Kontrollerlab] [KDE]&lt;br /&gt;
: [http://quanta.kdewebdev.org/ Quanta+] [KDE] (eigentlich Webentwicklungsumgebung)&lt;br /&gt;
: [http://www.scintilla.org/ SciTE]&lt;br /&gt;
: [http://www.sublimetext.com/ Sublime Text 2] (Windows, Linux und OS X)&lt;br /&gt;
: [http://www.vim.org/ Vim bzw. GVim] [Textterminal oder X]&lt;br /&gt;
&lt;br /&gt;
Es lassen sich aber Programme auch in &#039;fremden&#039; Desktopumgebungen ausführen, z.B KDevelop in GNOME.&lt;br /&gt;
&lt;br /&gt;
==C-Compiler: avr-gcc==&lt;br /&gt;
&lt;br /&gt;
Macht aus dem C-Code den vom AVR ausführbaren binären Programmcode.&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* [[AVR-GCC]]&lt;br /&gt;
* [[AVR-GCC-Tutorial]]&lt;br /&gt;
&lt;br /&gt;
==Laufzeitbibliothek: avr-libc==&lt;br /&gt;
&lt;br /&gt;
Enthält Makros und Hardwaredefinitionen wie Registernamen für (die meisten) AVRs. Man muss sich deswegen um die echten Adressen in der Prozessor-Logik nicht mehr selbst kümmern, sondern kann direkt die Namen und Werte aus dem Datenblatt benutzen.&lt;br /&gt;
&lt;br /&gt;
http://www.nongnu.org/avr-libc/user-manual/index.html&lt;br /&gt;
&lt;br /&gt;
==Makefile==&lt;br /&gt;
&lt;br /&gt;
Enthält Informationen zum Kompilieren. Das Programm make startet damit den angegeben Compiler. Im einfachsten Fall muss man also nur &#039;&#039;&#039;make all&#039;&#039;&#039; im entsprechenden Projektverzeichnis aufrufen, der Rest läuft automatisch. Die Datei heisst einfach nur Makefile.&lt;br /&gt;
&lt;br /&gt;
Hier ein Skript, mit dem man die Einstellungen zusammenklicken kann (benötigt aber auch wieder anderes, z.&amp;amp;nbsp;B. TCL/TK, ..): http://www.sax.de/~joerg/mfile/ Dessen Aufruf erfolgt dann z.&amp;amp;nbsp;B. mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ wish mfile.tcl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über ein Menü kann man dann die Einstellungen zusammenklicken. Das Editieren des Makefiles für Spezialanpassungen lässt sich aktivieren.&lt;br /&gt;
&lt;br /&gt;
Wer sie von Windows kennt und sucht: Die Definition F_CPU der Taktfrequenz ist in der aktuellen Linux-Version nicht vorgesehen. Macht nichts, siehe dazu http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Taktfrequenz&lt;br /&gt;
&lt;br /&gt;
==Programmierprogramm==&lt;br /&gt;
&lt;br /&gt;
Das Programmierprogramm schreibt mit entsprechender Hardware auf den Mikrocontroller bzw. liest ihn aus. AVR Programmierprogramme unter Linux sind z.&amp;amp;nbsp;B. [[AVRDUDE]] oder [[uisp]].&lt;br /&gt;
&lt;br /&gt;
Mit dem Makefile-Generator mfile kann man das Programmierprogramm und dessen Aufrufzeile in das Makefile eintragen lassen und später z.&amp;amp;nbsp;B. mit &#039;&#039;make program&#039;&#039; starten.&lt;br /&gt;
&lt;br /&gt;
Für eine interaktive Bedienung von AVRDUDE mit einer grafischen Benutzerschnittstelle (GUI) gibt es [http://avr8-burn-o-mat.aaabbb.de/ avr8-burn-o-mat].&lt;br /&gt;
&lt;br /&gt;
Für &#039;&#039;&#039;USB-Programmer&#039;&#039;&#039; ist die Einstellung der non-root Zugriffsrechte im Artikel [[AVRDUDE#Aufruf_unter_Linux_als_user_.28non-root.29|Aufruf unter Linux als User (non-root)]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
===Fuses, Lockbits===&lt;br /&gt;
&#039;&#039;(In Bearbeitung)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wer bislang Atmels AVR Studio unter Windows zum Programmieren der [[AVR_Fuses]] benutzt hat, kann das z.&amp;amp;nbsp;B. mit dem oben beschriebenen grafischen [[Burn-o-mat|AVR Burn-O-Mat]] oder eben mit AVRDUDE (über die Shell) machen. Wer die Dokumentation sucht, findet sie mit Sicherheit im [http://download.savannah.gnu.org/releases/avrdude/ gepackten Programmpaket]. &lt;br /&gt;
&lt;br /&gt;
*TODO: Ein aktuelles PDF&lt;br /&gt;
&lt;br /&gt;
Z.B. bekommt man als Resultat folgenden Aufrufes den Hexwert der lfuse eines Tiny13 über einen [http://www.mikrocontroller.net/articles/AVR_In_System_Programmer AVR-ISP] mit aktueller Firmware, der an einem USB-Seriell-Wandler angeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Durch die Option -v ([http://en.wiktionary.org/wiki/verbose verbose]) wird aber noch viel mehr [http://www.mikrocontroller.net/forum/read-1-399733.html?#399834 angezeigt]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
avrdude -p t13 -c avrispv2 -P /dev/ttyUSB0 -v -U lfuse:r:-:i&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &#039;-&#039; anstelle von FILENAME leitet die Ausgabe auf &amp;lt;stdout&amp;gt; (Bildschirm).&lt;br /&gt;
&lt;br /&gt;
Anstelle des &#039;i&#039; können verwendet werden:&lt;br /&gt;
* &#039;h&#039; - hexadezimal, z.B. 0xdf&lt;br /&gt;
* &#039;o&#039; - oktal, z.B. 0337&lt;br /&gt;
* &#039;b&#039; - binär, z.B. 0b11011111&lt;br /&gt;
&lt;br /&gt;
[Quelle: man avrdude, Beschreibung des Schalters &#039;-U&#039;]&lt;br /&gt;
&lt;br /&gt;
*TODO: Frontend?&lt;br /&gt;
&lt;br /&gt;
Eine Anleitung zum Umgang:&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-152502.html&lt;br /&gt;
&lt;br /&gt;
Eine 0 steht für eine gesetzte Fuse.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der Bits findet sich im Datenblatt unter &amp;quot;Memory Programming -&amp;gt; Fuse Bytes&amp;quot; (tiny13).&lt;br /&gt;
&lt;br /&gt;
===Program/Burn Flash===&lt;br /&gt;
&#039;&#039;(In Bearbeitung)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller wird wie beim Kompilieren mithilfe von make und der eingestellten Programmiersoftware mit dem Befehl &#039;&#039;make program&#039;&#039; geflasht&lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Anwendung&lt;br /&gt;
&lt;br /&gt;
==Konfiguration, Probleme, Erfahrungen==&lt;br /&gt;
&lt;br /&gt;
===Debian===&lt;br /&gt;
&lt;br /&gt;
* Toolchain-Build: http://blog.coldtobi.de/index.php?op=ViewArticle&amp;amp;articleId=21&amp;amp;blogId=1&lt;br /&gt;
&lt;br /&gt;
==== Etch (Netinst) ====&lt;br /&gt;
* Installation: gcc-avr, libc-avr, avrdude aus Standardquellen&lt;br /&gt;
* Emacs21 als Editor&lt;br /&gt;
* Makefile&lt;br /&gt;
... läuft problemlos.&lt;br /&gt;
&lt;br /&gt;
===Gentoo===&lt;br /&gt;
Gentoo bietet mit dem Shellscript &amp;quot;crossdev&amp;quot; eine einfache automatisierte Möglichkeit die Toolchain zu erstellen. Genauere Versionen von binutils/gcc/libc können via parameter übergeben werden.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
emerge crossdev&lt;br /&gt;
crossdev --target avr&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Falls &#039;crossdev --target avr&#039; nicht fehlerfrei durchlaufen sollte mit &#039;crossdev -t avr --without-headers&#039; versuchen (2009-01-28).&lt;br /&gt;
&lt;br /&gt;
crossdev ist nich empfehlenswert, da anscheinend ein compiler erstellt wird ohne passende patches. Das kann zu problemen führen, muss aber nicht, z.B. kann der , damit erstellte, gcc keine 64bit werte verarbeiten.&lt;br /&gt;
&lt;br /&gt;
===Mandrake===&lt;br /&gt;
&lt;br /&gt;
* Toolchain-Build: http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=42631&lt;br /&gt;
* 2007: benötigt &amp;quot;texinfo&amp;quot; Package &lt;br /&gt;
&lt;br /&gt;
===openSUSE===&lt;br /&gt;
&lt;br /&gt;
* Halbwegs aktuelle RPMs für openSUSE gibt es unter http://download.opensuse.org/repositories/CrossToolchain:/avr/ . Entsprechend der verwendeten openSUSE-Version das jeweilige Verzeichnis als Repository zu yast hinzufuegen (Achtung: Pfade fuer gcc-Binaries sind unterschiedlich zu den von openSUSE normalerweise mitgebrachten Paketen!).&lt;br /&gt;
&lt;br /&gt;
===Ubuntu 6.06===&lt;br /&gt;
&lt;br /&gt;
* Pakete veraltet; ist bereits als bug gemeldet&lt;br /&gt;
* Toolchain selbst kompilieren:&lt;br /&gt;
** default system path steht in /etc/environment&lt;br /&gt;
** Toolchain-Build: http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=42631&lt;br /&gt;
&lt;br /&gt;
===Ubuntu 7.04===&lt;br /&gt;
&lt;br /&gt;
* Toolchain-Build: http://www.avrfreaks.net/index.php?name=PNphpBB2&amp;amp;file=viewtopic&amp;amp;t=42631&lt;br /&gt;
&lt;br /&gt;
===Ubuntu 8.04===&lt;br /&gt;
* einfach eine universe-Paketquelle von Ubuntu 8.10 (Intrepid Ibex) hinzufügen. Dort gibt es dann aktuelle AVR-Pakete (gcc 4.3, libc 1.6.2 usw.). Anleitung hier: http://packages.ubuntu.com/de/intrepid/i386/gcc-avr/download&lt;br /&gt;
&lt;br /&gt;
===Ubuntu neuer===&lt;br /&gt;
* Die Pakete sind im Paketmanagement System schon enthalten. Ein &amp;quot;sudo apt-get install gcc-avr avr-libc&amp;quot; installiert sie.&lt;br /&gt;
&lt;br /&gt;
===rCOS LiveCD===&lt;br /&gt;
&lt;br /&gt;
Die [http://wiki.rcos.eu/index.php/RCOS-LiveCD rCOS LiveCD] (Download: http://www.rcos.eu/tools/livecd/index.html) ist eine angenehm schlanke Debian-LiveCD mit etlichen installierten Toolchains ([[AVR]], [[ARM]], [[Cortex-M3]], ...) extra für die µC Arbeit. Auf der LiveCD befindet sich auch ein [[AVRDUDE]] v5.5.&lt;br /&gt;
&lt;br /&gt;
===Arch Linux===&lt;br /&gt;
installation mittels: pacman -S avr-binutils avr-gcc avr-gdb avr-libc avrdude&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;br /&gt;
[[Kategorie:Linux]]&lt;br /&gt;
[[Kategorie:Entwicklungstools]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=81580</id>
		<title>AVR-Tutorial: Interrupts</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Interrupts&amp;diff=81580"/>
		<updated>2014-02-14T09:23:05Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Definition==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein sogenannter &#039;&#039;&#039;&amp;lt;i&amp;gt;Interrupt&amp;lt;/i&amp;gt;&#039;&#039;&#039; ausgelöst. Interrupts machen es möglich, beim Eintreten eines Ereignisses sofort informiert zu werden, ohne permanent irgendeinen Status abzufragen, was teure Rechenzeit kosten würde. Dabei wird das Programm unterbrochen und ein Unterprogramm aufgerufen. Wenn dieses beendet ist, läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
==Mögliche Auslöser==&lt;br /&gt;
&lt;br /&gt;
Bei Mikrocontrollern werden Interrupts z.&amp;amp;nbsp;B. ausgelöst wenn: &lt;br /&gt;
&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Wert von High auf Low ändert (oder umgekehrt) &lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine serielle Übertragung abgeschlossen ist ([[UART]])&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
Der ATmega8 besitzt 18 verschiedene Interruptquellen. Standardmäßig sind diese alle deaktiviert und müssen über verschiedene IO-Register einzeln eingeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
==INT0, INT1 und die zugehörigen Register==&lt;br /&gt;
&lt;br /&gt;
Wir wollen uns hier erst mal die beiden Interrupts &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039;  anschauen. INT0 wird ausgelöst, wenn sich der an PD2 anliegende Wert ändert, INT1 reagiert auf Änderungen an PD3. &lt;br /&gt;
&lt;br /&gt;
Als erstes müssen wir die beiden Interrupts konfigurieren. Im Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; wird eingestellt, ob die Interrupts bei einer steigenden Flanke (low nach high) oder bei einer fallenden Flanke (high nach low) ausgelöst werden. Dafür gibt es in diesem Register die Bits &#039;&#039;&#039;ISC00&#039;&#039;&#039;, &#039;&#039;&#039;ISC01&#039;&#039;&#039; (betreffen INT0) und &#039;&#039;&#039;ISC10&#039;&#039;&#039; und &#039;&#039;&#039;ISC11&#039;&#039;&#039; (betreffen INT1). &lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht über die möglichen Einstellungen und was sie bewirken: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! ISC11 o. ISC01 || ISC10 o. ISC00 ||Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Low-Level am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Jede Änderung am Pin löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0&lt;br /&gt;
||Eine fallende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
||Eine steigende Flanke löst den Interrupt aus&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach müssen diese beiden Interrupts aktiviert werden, indem die Bits &#039;&#039;&#039;INT0&#039;&#039;&#039; und &#039;&#039;&#039;INT1&#039;&#039;&#039; im Register &#039;&#039;&#039;GICR&#039;&#039;&#039; auf &#039;&#039;&#039;1&#039;&#039;&#039; gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
Die Register &#039;&#039;&#039;MCUCR&#039;&#039;&#039; und &#039;&#039;&#039;GICR&#039;&#039;&#039; gehören zwar zu den IO-Registern, können aber nicht wie andere mit den Befehlen &#039;&#039;&#039;cbi&#039;&#039;&#039; und &#039;&#039;&#039;sbi&#039;&#039;&#039; verwendet werden. Diese Befehle wirken nur auf die IO-Register bis zur Adresse 0x1F (welches Register sich an welcher IO-Adresse befindet, steht in der Include-Datei, hier &amp;quot;m8def.inc&amp;quot;, und im Datenblatt des Controllers). Somit bleiben zum Zugriff auf diese Register nur die Befehle &#039;&#039;&#039;in&#039;&#039;&#039; und &#039;&#039;&#039;out&#039;&#039;&#039; übrig.&lt;br /&gt;
&lt;br /&gt;
==Interrupts generell zulassen==&lt;br /&gt;
&lt;br /&gt;
Schließlich muss man noch das Ausführen von Interrupts allgemein aktivieren, was man durch einfaches Aufrufen des Assemblerbefehls &#039;&#039;&#039;sei&#039;&#039;&#039; bewerkstelligt.&lt;br /&gt;
&lt;br /&gt;
==Die Interruptvektoren==&lt;br /&gt;
&lt;br /&gt;
Woher weiß der Controller jetzt, welche Routine aufgerufen werden muss wenn ein Interrupt ausgelöst wird? &lt;br /&gt;
&lt;br /&gt;
Wenn ein Interrupt auftritt, dann springt die Programmausführung an eine bestimmte Stelle im Programmspeicher. Diese Stellen sind festgelegt und können nicht geändert werden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
{| {{Tabelle}}&lt;br /&gt;
|-  style=&amp;quot;background-color:#ffddcc&amp;quot;&lt;br /&gt;
! Nr. || Adresse || Interruptname || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  1&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x000&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  RESET&lt;br /&gt;
||Reset bzw. Einschalten der Stromversorgung&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  2&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x001&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT0&lt;br /&gt;
||Externer Interrupt 0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  3&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x002&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  INT1&lt;br /&gt;
||Externer Interrupt 1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  4&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x003&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_COMP&lt;br /&gt;
||Timer/Counter2 Compare Match&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  5&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x004&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER2_OVF&lt;br /&gt;
||Timer/Counter2 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  6&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x005&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_CAPT&lt;br /&gt;
||Timer/Counter1 Capture Event&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  7&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x006&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPA&lt;br /&gt;
||Timer/Counter1 Compare Match A&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  8&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x007&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_COMPB&lt;br /&gt;
||Timer/Counter1 Compare Match B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  9&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x008&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER1_OVF&lt;br /&gt;
||Timer/Counter1 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  10&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x009&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TIMER0_OVF&lt;br /&gt;
||Timer/Counter0 Overflow&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  11&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00A&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPI_STC&lt;br /&gt;
||SPI-Übertragung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  12&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00B&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_RX&lt;br /&gt;
||USART-Empfang abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  13&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00C&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_UDRE&lt;br /&gt;
||USART-Datenregister leer&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  14&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00D&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  USART_TX&lt;br /&gt;
||USART-Sendung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  15&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00E&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ADC&lt;br /&gt;
||AD-Wandlung abgeschlossen&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  16&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x00F&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  EE_RDY&lt;br /&gt;
||EEPROM bereit&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  17&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x010&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  ANA_COMP&lt;br /&gt;
||Analogkomparator&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  18&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x011&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  TWI&lt;br /&gt;
||Two-Wire Interface&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  19&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  0x012&lt;br /&gt;
|align=&amp;quot;center&amp;quot; |  SPM_RDY&lt;br /&gt;
||Store Program Memory Ready&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, wir wissen jetzt, dass der Controller zu Adresse 0x001 springt, wenn &#039;&#039;&#039;INT0&#039;&#039;&#039; auftritt. Aber dort ist ja nur Platz für einen Befehl, denn die nächste Adresse ist doch für INT1 reserviert. Wie geht das? Ganz einfach: Dort kommt ein Sprungbefehl rein, z.&amp;amp;nbsp;B. &#039;&#039;&#039;rjmp interrupt0&#039;&#039;&#039;. Irgendwo anders im Programm muss in diesem Fall eine Stelle mit &amp;lt;i&amp;gt;interrupt0:&amp;lt;/i&amp;gt; gekennzeichnet sein, zu der dann gesprungen wird. Diese durch den Interrupt aufgerufene Routine nennt man &#039;&#039;&#039;&amp;lt;i&amp;gt;Interrupthandler&amp;lt;/i&amp;gt;&#039;&#039;&#039; (engl. &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine, &#039;&#039;&#039;ISR&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
==Beenden eines Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Und wie wird die Interruptroutine wieder beendet? Durch den Befehl &#039;&#039;&#039;reti&#039;&#039;&#039;. Wird dieser aufgerufen, dann wird das Programm ganz normal dort fortgesetzt, wo es durch den Interrupt unterbrochen wurde. Es ist dabei wichtig, daß hier der Befehl &#039;&#039;&#039;reti&#039;&#039;&#039; und nicht ein normaler &#039;&#039;&#039;ret&#039;&#039;&#039; benutzt wird. Wird ein Interrupt Handler betreten, so sperrt der Mikrocontroller automatisch alle weiteren Interrupts. Im Unterschied zu &#039;&#039;&#039;ret&#039;&#039;&#039;, hebt ein &#039;&#039;&#039;reti&#039;&#039;&#039; diese Sperre wieder auf.&lt;br /&gt;
&lt;br /&gt;
==Aufbau der Interruptvektortabelle==&lt;br /&gt;
&lt;br /&gt;
Jetzt müssen wir dem Assembler nur noch klarmachen, dass er unser &#039;&#039;&#039;rjmp interrupt0&#039;&#039;&#039; an die richtige Stelle im Programmspeicher schreibt, nämlich an den Interruptvektor für &#039;&#039;&#039;INT0&#039;&#039;&#039;. Dazu gibt es eine Assemblerdirektive. Durch &#039;&#039;&#039;.org 0x001&#039;&#039;&#039; sagt man dem Assembler, dass er die darauffolgenden Befehle ab Adresse 0x001 im Programmspeicher platzieren soll. Diese Stelle wird von &#039;&#039;&#039;INT0&#039;&#039;&#039; angesprungen. &lt;br /&gt;
&lt;br /&gt;
Damit man nicht alle Interruptvektoren immer nachschlagen muss, sind in der Definitionsdatei m8def.inc einfach zu merkende Namen für die Adressen definiert. Statt 0x001 kann man z.&amp;amp;nbsp;B. einfach &#039;&#039;&#039;&amp;lt;i&amp;gt;INT0addr&amp;lt;/i&amp;gt;&#039;&#039;&#039; schreiben. Das hat außerdem den Vorteil, dass man bei Portierung des Programms auf einen anderen AVR-Mikrocontroller nur die passende Definitionsdatei einbinden muss, und sich über evtl. geänderte Adressen für die Interruptvektoren keine Gedanken zu machen braucht. &lt;br /&gt;
&lt;br /&gt;
Nun gibt es nur noch ein Problem: Beim Reset (bzw. wenn die Spannung eingeschaltet wird) wird das Programm immer ab der Adresse 0x000 gestartet. Deswegen muss an diese Stelle ein Sprungbefehl zum Hauptprogramm erfolgen, z.&amp;amp;nbsp;B. &#039;&#039;&#039;rjmp RESET&#039;&#039;&#039; um an die mit &#039;&#039;&#039;&amp;lt;i&amp;gt;RESET:&amp;lt;/i&amp;gt;&#039;&#039;&#039; markierte Stelle zu springen.&lt;br /&gt;
&lt;br /&gt;
Wenn man mehrere Interrupts verwenden möchte, kann man auch, anstatt jeden Interruptvektor einzeln mit .org an die richtige Stelle zu rücken, die gesamte Sprungtabelle ausschreiben: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000                    ; kommt ganz an den Anfang des Speichers&lt;br /&gt;
         rjmp RESET           ; Interruptvektoren überspringen&lt;br /&gt;
                              ; und zum Hauptprogramm&lt;br /&gt;
         rjmp EXT_INT0        ; IRQ0 Handler&lt;br /&gt;
         rjmp EXT_INT1        ; IRQ1 Handler&lt;br /&gt;
         rjmp TIM2_COMP&lt;br /&gt;
         rjmp TIM2_OVF&lt;br /&gt;
         rjmp TIM1_CAPT       ; Timer1 Capture Handler&lt;br /&gt;
         rjmp TIM1_COMPA      ; Timer1 CompareA Handler&lt;br /&gt;
         rjmp TIM1_COMPB      ; Timer1 CompareB Handler&lt;br /&gt;
         rjmp TIM1_OVF        ; Timer1 Overflow Handler&lt;br /&gt;
         rjmp TIM0_OVF        ; Timer0 Overflow Handler&lt;br /&gt;
         rjmp SPI_STC         ; SPI Transfer Complete Handler&lt;br /&gt;
         rjmp USART_RXC       ; USART RX Complete Handler&lt;br /&gt;
         rjmp USART_DRE       ; UDR Empty Handler&lt;br /&gt;
         rjmp USART_TXC       ; USART TX Complete Handler&lt;br /&gt;
         rjmp ADC             ; ADC Conversion Complete Interrupthandler&lt;br /&gt;
         rjmp EE_RDY          ; EEPROM Ready Handler&lt;br /&gt;
         rjmp ANA_COMP        ; Analog Comparator Handler&lt;br /&gt;
         rjmp TWSI            ; Two-wire Serial Interface Handler&lt;br /&gt;
         rjmp SPM_RDY         ; Store Program Memory Ready Handler&lt;br /&gt;
&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier ist es unbedingt nötig, bei unbenutzten Interruptvektoren statt des Sprungbefehls den Befehl &#039;&#039;&#039;reti&#039;&#039;&#039; (bzw. &#039;&#039;&#039;reti nop&#039;&#039;&#039;, wenn jmp 4 Byte lang ist) reinzuschreiben. Wenn man einen Vektor einfach weglässt stehen die nachfolgenden Sprungbefehle sonst alle an der falschen Adresse im Speicher.&lt;br /&gt;
&lt;br /&gt;
Wer auf Nummer sicher gehen möchte kann aber auch alle Vektoren einzeln mit .org adressieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
       rjmp RESET&lt;br /&gt;
.org INT0addr                 ; External Interrupt0 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org INT1addr                 ; External Interrupt1 Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC2addr                  ; Output Compare2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF2addr                 ; Overflow2 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ICP1addr                 ; Input Capture1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Aaddr                 ; Output Compare1A Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OC1Baddr                 ; Output Compare1B Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF1addr                 ; Overflow1 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org OVF0addr                 ; Overflow0 Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPIaddr                  ; SPI Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org URXCaddr                 ; USART Receive Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UDREaddr                 ; USART Data Register Empty Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org UTXCaddr                 ; USART Transmit Complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ADCCaddr                 ; ADC Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ERDYaddr                 ; EEPROM Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org ACIaddr                  ; Analog Comparator Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
.org TWIaddr                  ; Irq. vector address for Two-Wire Interface&lt;br /&gt;
       reti                   &lt;br /&gt;
.org SPMRaddr                  ; SPM complete Interrupt Vector Address&lt;br /&gt;
       reti                   &lt;br /&gt;
&lt;br /&gt;
.org INT_VECTORS_SIZE&lt;br /&gt;
RESET:                        ; hier beginnt das Hauptprogramm &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt die unbenutzten Interruptvektoren mit &#039;&#039;&#039;reti&#039;&#039;&#039; zu füllen könnte man sie hier auch einfach weglassen, da die &#039;&#039;&#039;.org&#039;&#039;&#039;-Direktive dafür sorgt dass jeder Vektor in jedem Fall am richtigen Ort im Speicher landet.&lt;br /&gt;
&lt;br /&gt;
==Beispiel==&lt;br /&gt;
So könnte ein Minimal-Assemblerprogramm aussehen, das die Interrupts INT0 und INT1 verwendet. An die Interrupt Pins können zb Taster nach bewährter Manier angeschlossen werden. Die Interrupts werden auf fallende Flanke konfiguriert, da ja die Taster so angeschlossen sind, dass sie im Ruhezustand eine 1 liefern und bei einem Tastendruck nach 0 wechseln.&lt;br /&gt;
&lt;br /&gt;
[http://www.mikrocontroller.net/sourcecode/tutorial/extinttest.asm Download extinttest.asm]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt;  &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = r16&lt;br /&gt;
&lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
&lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
&lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
&lt;br /&gt;
int0_handler:&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&lt;br /&gt;
int1_handler:&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für dieses Programm braucht man nichts weiter als eine LED an PB0 und je einen Taster an PD2 (INT0) und PD3 (INT1). Wie diese angeschlossen werden, steht in [[AVR-Tutorial: IO-Grundlagen|Teil 2]] des Tutorials. &lt;br /&gt;
&lt;br /&gt;
Die Funktion ist auch nicht schwer zu verstehen: Drückt man eine Taste, wird der dazugehörige Interrupt aufgerufen und die LED an- oder abgeschaltet. Das ist zwar nicht sonderlich spektakulär, aber das Prinzip sollte deutlich werden. &lt;br /&gt;
&lt;br /&gt;
Meistens macht es keinen Sinn, Taster direkt an einen Interrupteingang anzuschließen. Das kann bisweilen sogar sehr schlecht sein, siehe [[Entprellung]]. Häufiger werden Interrupts in Zusammenhang mit dem UART verwendet, um z.&amp;amp;nbsp;B. auf ein empfangenes Zeichen zu reagieren. Wie das funktioniert, wird im Kapitel über den [[AVR-Tutorial:_UART|UART]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
==Besonderheiten des Interrupthandlers==&lt;br /&gt;
&lt;br /&gt;
Der Interrupthandler kann ja mehr oder weniger zu jedem beliebigen Zeitpunkt unabhängig vom restlichen Programm aufgerufen werden. Dabei soll das restliche Programm auf keinen Fall durch den Interrupthandler negativ beeinflusst werden, das heißt das Hauptprogramm soll nach dem Beenden des Handlers weiterlaufen als wäre nichts passiert. Insbesondere muss deshalb darauf geachtet werden, dass im Interrupthandler Register, die vom Programmierer nicht ausschließlich nur für den Interrupthandler reserviert wurden, auf dem Stack gesichert und zum Schluss wieder hergestellt werden müssen.&lt;br /&gt;
&lt;br /&gt;
Ein Register, das gerne übersehen wird, ist das &#039;&#039;&#039;&amp;lt;i&amp;gt;Status Register&amp;lt;/i&amp;gt;&#039;&#039;&#039;. In ihm merkt sich der Prozessor bestimmte Zustände von Berechnungen, z. B. ob ein arithmetischer Überlauf stattgefunden hat, ob das letzte Rechenergebnis 0 war, etc. Sobald ein Interrupthandler etwas komplizierter wird als im obigen Beispiel, tut man gut daran, das &#039;&#039;&#039;SREG&#039;&#039;&#039; Register auf jeden Fall zu sichern. Ansonsten kann das Hinzufügen von weiterem Code zum Interrupthandler schnell zum Boomerang werden: Die dann möglicherweise notwendige Sicherung des &#039;&#039;&#039;SREG&#039;&#039;&#039; Registers wird vergessen. Überhaupt empfiehlt es sich, in diesen Dingen bei der Programmierung eines Interrupthandlers eher vorausschauend, übervorsichtig und konservativ zu programmieren. Wird dies getan, so vergeudet man höchstens ein bischen Rechenzeit. Im anderen Fall handelt man sich allerdings einen Super-GAU ein: Man steht dann vor einem Programm, das sporadisch nicht funktioniert und keiner weiss warum. Solche Fehler sind nur sehr schwer und oft nur mit einem Quäntchen Glück zu finden.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wäre zwar das Sichern und Wiederherstellen der Register &#039;&#039;&#039;temp&#039;&#039;&#039; und &#039;&#039;&#039;SREG&#039;&#039;&#039; nicht wirklich notwendig, aber hier soll die grundsätzliche Vorgehensweise gezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;avrasm&amp;quot;&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp = r16&lt;br /&gt;
 &lt;br /&gt;
.org 0x000&lt;br /&gt;
         rjmp main            ; Reset Handler&lt;br /&gt;
.org INT0addr&lt;br /&gt;
         rjmp int0_handler    ; IRQ0 Handler&lt;br /&gt;
.org INT1addr&lt;br /&gt;
         rjmp int1_handler    ; IRQ1 Handler&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
main:                         ; hier beginnt das Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
         ldi temp, LOW(RAMEND)&lt;br /&gt;
         out SPL, temp&lt;br /&gt;
         ldi temp, HIGH(RAMEND)&lt;br /&gt;
         out SPH, temp&lt;br /&gt;
 &lt;br /&gt;
         ldi temp, 0x00&lt;br /&gt;
         out DDRD, temp&lt;br /&gt;
 &lt;br /&gt;
         ldi temp, 0xFF&lt;br /&gt;
         out DDRB, temp&lt;br /&gt;
 &lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;ISC01) | (1&amp;lt;&amp;lt;ISC11) ; INT0 und INT1 auf fallende Flanke konfigurieren&lt;br /&gt;
         out MCUCR, temp&lt;br /&gt;
 &lt;br /&gt;
         ldi temp, (1&amp;lt;&amp;lt;INT0) | (1&amp;lt;&amp;lt;INT1) ; INT0 und INT1 aktivieren&lt;br /&gt;
         out GICR, temp&lt;br /&gt;
 &lt;br /&gt;
         sei                   ; Interrupts allgemein aktivieren&lt;br /&gt;
 &lt;br /&gt;
loop:    rjmp loop             ; eine leere Endlosschleife&lt;br /&gt;
 &lt;br /&gt;
int0_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         sbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
 &lt;br /&gt;
int1_handler:&lt;br /&gt;
         push temp             ; Das SREG in temp sichern. Vorher&lt;br /&gt;
         in   temp, SREG       ; muss natürlich temp gesichert werden&lt;br /&gt;
&lt;br /&gt;
         cbi PORTB, 0&lt;br /&gt;
&lt;br /&gt;
         out SREG, temp        ; Die Register SREG und temp wieder&lt;br /&gt;
         pop temp              ; herstellen&lt;br /&gt;
         reti&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
[[Interrupt | Interrupt: Anderer Wiki-Artikel über Interrupts]]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=LCD|&lt;br /&gt;
zurücklink=AVR-Tutorial: LCD|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Vergleiche|&lt;br /&gt;
vorlink=AVR-Tutorial:_Vergleiche}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Tutorial|Interrupts]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=I%C2%B2C&amp;diff=81279</id>
		<title>I²C</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=I%C2%B2C&amp;diff=81279"/>
		<updated>2014-02-03T11:49:27Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;I²C&#039;&#039;&#039; ist ein synchroner serieller Zweidraht-[[Bus]] (eine Daten- und eine Taktleitung), der für die Kommunikation zwischen [[IC]]s über kleine Distanzen geeignet ist. Entwickelt wurde er Anfang der 80er Jahre von Philips.&lt;br /&gt;
Gesprochen &amp;quot;I quadrat C&amp;quot; steht für IIC = Inter [[IC]] Bus. Aus Lizenzgründen heißt der I²C Bus bei manchen Herstellern auch &#039;&#039;&#039;TWI&#039;&#039;&#039;, two wire interface.&lt;br /&gt;
&lt;br /&gt;
== I2C ==&lt;br /&gt;
In einem I²C-Bus gibt es mindestens einen Master mit bis zu 127 Slaves und einen Broadcast Kanal. Ein I²C-Bus mit mehreren Mastern wird als &amp;quot;Multi-Master-Bus&amp;quot; bezeichnet.&lt;br /&gt;
Der (oder die) Master sprechen die Slaves an; ein Slave kann NIE selbstständig Daten senden. Dazu ü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. 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 Protokoll sicher, dass sich diese nicht gegenseitig stören.&lt;br /&gt;
&lt;br /&gt;
=== I2C - Übertragungsraten  ===&lt;br /&gt;
Die Übertragungsrate beträgt beim standard mode bis zu 100 kbit/s, beim fast mode bis zu 400 kbit/s und beim high-speed mode bis zu 3,4 MBit/s. Falls die Taktrate für einen Slave zu hoch ist, kann er die Clock-Leitung auf Null ziehen und die Übertragung damit verlangsamen (sog. Clock Stretching). Dies ist auf Bit- wie auf Byte-Ebene möglich; ersteres allerdings nicht im high-speed mode.&lt;br /&gt;
Um längere Übertragungswege zu realisieren, kann man die Taktrate fast beliebig vermindern (einige Bausteine erzeugen aber irgendwann ein Time-Out). Mit einer Taktfrequenz von nur 5 kbit/s können dann durchaus mehrere Meter überbrückt werden.&lt;br /&gt;
&lt;br /&gt;
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;
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;
* 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;
hat der Bus ohne Probleme funktioniert.&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://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;
* [[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;
=== 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&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;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[AT91-TWI]]&lt;br /&gt;
* [[AVR TWI]]&lt;br /&gt;
* [[I2C als Hausbus]]&lt;br /&gt;
* Beitrag zu [http://www.mikrocontroller.net/topic/181115#1748880 I2C-Adressen] im Forum&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://ics.nxp.com/interface/ 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 FAQ, Einführung, Hintergrundinformationen]&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://homepage.hispeed.ch/peterfleury/avr-software.html AVR TWI/I2C MASTER in C]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/87597 AVR TWI Master und Slave Funtionen 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;
&lt;br /&gt;
===I²C-Interface===&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/155136#1462320 Beitrag &amp;quot;i2c usb interface&amp;quot;] (Linux)&lt;br /&gt;
* [http://sprut.de/electronic/pic/projekte/usb4all/usb4all.htm USB4ALL bei sprut.de] universeller USB-Baustein, auch für I2C&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/ConniiMM20 Connii MM 2.0 - I²C Monitor with USB Interface]&lt;br /&gt;
* [http://www.telos.info/traciixl20 Tracii XL - 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;
* [http://www.harbaum.org/till/i2c_tiny_usb/index.shtml i2c-tiny-usb open source/open hardware zum Auslesen mit einem PC]&lt;br /&gt;
&lt;br /&gt;
[[Category:I2C| ]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/LCD-Ansteuerung&amp;diff=79876</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=79876"/>
		<updated>2013-12-04T13:51:06Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &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 3686400&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&lt;br /&gt;
void lcd_generatechar( uint8_t code, 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 code, const uint8_t *data )&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;
 &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;
}&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;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-software.html#libs Lib zur HD44780 Ansteuerung (AVR)]&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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Erweiterte_LCD-Ansteuerung&amp;diff=79875</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=79875"/>
		<updated>2013-12-04T13:50:44Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &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://homepage.hispeed.ch/peterfleury/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;
&lt;br /&gt;
[[Kategorie:LCD]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=79874</id>
		<title>HD44780</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=79874"/>
		<updated>2013-12-04T13:50:21Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &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;
!Pin || Funktion || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|1 || | Vee || Versorgungsspannung GND (es gibt auch Displays mit Vcc an Pin 1)&lt;br /&gt;
|-&lt;br /&gt;
|2 || | Vcc || Versorgungsspannung, meistens 5V (es gibt auch Displays mit Vee an Pin 2 oder an Pin 14, im letzten Fall rutschen die Bezeichnungen dann entsprechend nach unten)&lt;br /&gt;
|-&lt;br /&gt;
|3 || | V0 ||Kontrastspannung, zwischen VEE und VCC, kann auf VSS gelegt oder via Poti angeschlossen werden.&amp;lt;BR&amp;gt;Bei großflächigen LCDs oder LCDs für den erweiterten Temperaturbereich kann auch eine negative Kontrastspannung 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/W || Lese- oder Schreibzugriff; kann fest auf GND gelegt werden, wenn Busy-Auswertung durch Timing ersetzt wird&amp;lt;BR&amp;gt;0 = Schreiben, 1 = Lesen &lt;br /&gt;
|-&lt;br /&gt;
|6 || | E || 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 || Kathode 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;
!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://homepage.hispeed.ch/peterfleury/avr-software.html LCD library for HD44870 based LCDs]&lt;br /&gt;
* Kai Klenovseks [http://kk.elektronik-4u.de/index.php?Sid=9 LCD library for HD44870 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;
=== 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.darreltaylor.com/files/CustChar.htm Custom Character Generator] by Darrel Taylor (online, JavaScript)&lt;br /&gt;
* http://www.parallax.com/ProductInfo/Microcontrollers/BASICStampSoftware/LCDCharacterCreator/tabid/482/Default.aspx&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 Ermittlung der Startadresse der einzelnen Zeilen]&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/wiki/index.php?title=Ansteuern_des_HD44780_mit_den_C18-Libraries Ansteuern des HD44780 mit den 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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/LCD-Ansteuerung&amp;diff=79873</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=79873"/>
		<updated>2013-12-04T13:48:01Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &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 3686400&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&lt;br /&gt;
void lcd_generatechar( uint8_t code, 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 code, const uint8_t *data )&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;
 &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;
}&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;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://homepage.hispeed.ch/peterfleury/avr-software.html#libs Lib zur HD44780 Ansteuerung (AVR)]&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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=POV-Display&amp;diff=79108</id>
		<title>POV-Display</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=POV-Display&amp;diff=79108"/>
		<updated>2013-10-24T07:05:29Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In diesem Artikel wird die Realisierung eines POV-Displays (persistence of vision, siehe http://de.wikipedia.org/wiki/Nachbildwirkung) beschrieben.&lt;br /&gt;
Der besondere Effekt eines solchen Displays liegt darin, dass das Bild&lt;br /&gt;
erst durch die Trägheit des Auges entsteht. Bei Bauarten wie dieser&lt;br /&gt;
kann damit ein Bild mehr oder weniger schwebend in der Luft erzeugt werden.&lt;br /&gt;
Die Technik wird in verschiedensten Varianten eingesetzt, beispielhaft&lt;br /&gt;
wird hier ein Globus mit 40 RGB-[[LED]]s gezeigt.&lt;br /&gt;
&lt;br /&gt;
Der Artikel soll vor allem Anregungungen zum Aufbau, zu speziellen &lt;br /&gt;
Details und Bauteilen bieten wie auch als Codegrundlage dienen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:globe_11_1024.jpg|right|300px]]&lt;br /&gt;
&lt;br /&gt;
Demoanwendung mit einigen Animationen:&lt;br /&gt;
http://youtu.be/ziD_1fVdDWQ&lt;br /&gt;
&lt;br /&gt;
== Randdaten ==&lt;br /&gt;
* 40 RGB LEDs, Bildfläche 80x40 Pixel mit 7 Farben (+ echtem Alphachannel)&lt;br /&gt;
* Globus mit 20cm Durchmesser&lt;br /&gt;
* ATMega644&lt;br /&gt;
* selbstregelnde Refresh-Rate über optischen Sensor&lt;br /&gt;
* ca. 1/2 ASCII-Text Support&lt;br /&gt;
* Bildkonverter&lt;br /&gt;
&lt;br /&gt;
== Mechanische Konstruktion ==&lt;br /&gt;
Es gibt neben dem hier beschriebenen Rotor auch noch weitere Techniken wie &lt;br /&gt;
geschlitzte Abdeckungen.&lt;br /&gt;
=== Rotor ===&lt;br /&gt;
[[Bild:globe_04_1024.jpg|thumb|right|Trägerkonstruktion aus Alu]]&lt;br /&gt;
Grundsätzlich sind 3 Arten von Rotoren typisch: flach, zylindrisch und kugelförmig.&lt;br /&gt;
Zylindrische und flache Rotoren sind recht simpel zu bauen, wobei bei letzterem natürlich&lt;br /&gt;
noch der Softwareaufwand für die Umrechnung kartesisch-&amp;gt;polar hinzukommt.&lt;br /&gt;
Kugelförmige Rotoren haben das Problem, dass es unter einer gewissen Groesse kaum möglich&lt;br /&gt;
ist, Treiber und LEDs auf eine Platine zu setzen. Mein Rotor war mit d=20cm dafür zu klein, &lt;br /&gt;
deshalb kommt hier eine etwas wüste, fliegende Konstruktion zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
Bei allen Rotoren ist natürlich auf eine mechanisch stabile Auslegung zu achten, besonders&lt;br /&gt;
bei größeren Durchmessern. Ein Rotor mit einem Durchmesser von 50cm hätte bei 25Hz bereits &lt;br /&gt;
eine Zentrifugalbeschleunigung von über 628g im Rand!&lt;br /&gt;
Dementsprechend wichtig ist auch das genaue Auswuchten des fertigen Rotors.&lt;br /&gt;
&lt;br /&gt;
=== Antrieb ===&lt;br /&gt;
[[Bild:globe_12_1024.jpg|thumb|right|Innenaufnahme Gehäuse]]&lt;br /&gt;
Am besten würde sich natürlich ein Antrieb eignen, der seine Drehzahl sehr genau&lt;br /&gt;
halten kann, was aber zumindest eine entsprechende Regelstrecke voraussetzt. &lt;br /&gt;
&lt;br /&gt;
Als einfache Alternative habe ich deshalb einen Speed 700BB 12V verwendet, der&lt;br /&gt;
für meinen Rotor ausreicht wenn er aktiv gekühlt wird.&lt;br /&gt;
Ein guter Richtwert für die Drehzahl sind etwa 20-25Hz.&lt;br /&gt;
Die statische Steuerung erfolgt über eine unspektakuläre [[PWM]] aus einem ATTiny45&lt;br /&gt;
und grossem [[FET]]. Da die Drehzahl bei dieser Methode nicht konstant ist,&lt;br /&gt;
wird eine Regelung der Bildwiederholrate notwendig.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Stromversorgung ===&lt;br /&gt;
[[Bild:globe_13_1024.jpg|thumb|right|Simpler Schleifkontakt]]&lt;br /&gt;
Die Stromversorgung des Rotors lässt sich am einfachsten über Schleifkontakte&lt;br /&gt;
bewerkstelligen. Wenn man auf eine Spannungsregelung auf der Controllerplatine&lt;br /&gt;
verzichten will muss die Spannung dort unbedingt gut gepuffert werden. Dazu mehr&lt;br /&gt;
im entsprechenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
Ich habe 2 Schleifringe aus Messingrohr mit Epoxy aufgeklebt, gross genug&lt;br /&gt;
um darunter das Kabel des darunterliegenden Schleifrings verlegen zu können.&lt;br /&gt;
Die schwarze Kunststoffplatte drückt die 2 Schleifkontakte dagegen.&lt;br /&gt;
Man könnte natürlich kleinere Ringe nehmen und das 2. Kabel im Trägerrohr&lt;br /&gt;
verlegen.&lt;br /&gt;
&lt;br /&gt;
Eine verschleiß- und geräuschfreie Alternative wären Induktionsspulen.&lt;br /&gt;
&lt;br /&gt;
Als Stromquelle dient die 5V Schiene eines Computernetzteils.&lt;br /&gt;
&lt;br /&gt;
=== Gehäuse ===&lt;br /&gt;
Das Gehäuse ist bei mir reines Mittel zum Zweck (wie man vermutlich sieht).&lt;br /&gt;
Ich habe dicke Spanplatten verbaut, um es verwindungssteif und gleichzeitig&lt;br /&gt;
schwer zu machen, da ein 100%iges Auswuchten fast unmoeglich ist.&lt;br /&gt;
&lt;br /&gt;
== LEDs und Treiber ==&lt;br /&gt;
[[Bild:globe_06_1024.jpg|thumb|right|aussen LEDs, innen Schieberegister]]&lt;br /&gt;
Als LEDs kamen die verbreiteten RGB-LEDs der Bauform 5050 zum Einsatz, die sich&lt;br /&gt;
sehr gut in den Punkten Helligkeit, Abstrahlwinkel, Farbmischung und&lt;br /&gt;
Ansteuerbarkeit (keine gemeinsame Anode/Kathode) eignen.&lt;br /&gt;
&lt;br /&gt;
Um die LEDs gleichmäßig aufzukleben habe ich einen Papierstreifen zugeschnitten,&lt;br /&gt;
mit Bleistift Markierungen gesetzt und diesen mit Sekundenkleber (getränkt) auf&lt;br /&gt;
den Ring aufgeklebt. Die LEDs wurden dann mit etwas Epoxy darauf geklebt.&lt;br /&gt;
&lt;br /&gt;
Getrieben wird alles mit 15 Schieberegistern 74HC595, diese sind pro Farbe&lt;br /&gt;
auf jeweils 5 in Serie aufgeteilt. Siehe dazu den -&amp;gt; Schaltplan für eine &lt;br /&gt;
&amp;quot;Stufe&amp;quot; aus 8 LEDs, dort sind auch die entsprechenden Ausgangspins am &lt;br /&gt;
Atmel angegeben.&lt;br /&gt;
Da die Schieberegister auf Vcc und GND nur 70mA aushalten, habe ich den auch&lt;br /&gt;
schon im [[AVR-Tutorial: Schieberegister]] Trick angewendet und jeweils&lt;br /&gt;
4 LEDs mit Anoden links und 4 mit Kathoden links abgewechselt.&lt;br /&gt;
&lt;br /&gt;
Dadurch treten allerdings 3 Effekte auf: Zum einen liegen die einzelnen Chips&lt;br /&gt;
in den LEDs nicht mehr exakt auf einer Linie, was bei genauerer Betrachtung bzw. &lt;br /&gt;
ohne zusätzlichen Diffusor auffällt.&lt;br /&gt;
Zum anderen muss das jeweilige Vertauschen von Blau und Grün entweder in der&lt;br /&gt;
Verkabelung oder im Code (meine Methode) beachtet werden, genau wie die&lt;br /&gt;
Tatsache, dass dann je 4 LEDs active high und 4 active low sind.&lt;br /&gt;
[[Bild:rotor_scheme.png|thumb|right|Schaltplan LEDs+Register]]&lt;br /&gt;
Versorgungs- und Steuerleitungen habe ich im Trägerrohr verlegt.&lt;br /&gt;
Nach dem Funktionstest habe ich die fliegende Konstruktion noch mit einer&lt;br /&gt;
entsprechenden Menge Epoxy vergossen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Controllerplatine ==&lt;br /&gt;
[[Bild:povglobe_scheme.png|thumb|right|Schaltplan Controllerplatine]]&lt;br /&gt;
Die Controllerplatine befindet sich im Gehäuse und ist sehr simpel gehalten,&lt;br /&gt;
sie beinhaltet im wesentlichen den ATMega644 mit ISP-Header und Anschlüssen für die&lt;br /&gt;
Schieberegister, einer Pufferung für die Versorungsspannung und 2 OPAMP-Stufen,&lt;br /&gt;
die mit einer Fotodiode + Schmitt-Trigger den externen Interrupt aktivieren.&lt;br /&gt;
Die Pufferung ist wichtig, da die Schleifkontakte keine saubere Versorgung&lt;br /&gt;
gewährleisten können.&lt;br /&gt;
&lt;br /&gt;
Zu meiner Dektektorschaltung noch ein Hinweis: Am besten vorher am Breadboard&lt;br /&gt;
die Werte prüfen und danach auch am Controller testen ob die Triggerung auf &lt;br /&gt;
steigende oder fallende Flanke besser funktioniert. Ausserdem könnte man noch &lt;br /&gt;
einen kleinen Kondensator parallel zu R9 schalten (Tiefpass).&lt;br /&gt;
&lt;br /&gt;
Layout und der Bestückungsplan (von Kupferseite aus gesehen) finden sich im .tgz.&lt;br /&gt;
Auch die Platine sollte ausgewuchtet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Firmware ==&lt;br /&gt;
Die Firmware für den [[AVR-GCC]] geschrieben worden.&lt;br /&gt;
Als Buffer für das aktuelle Bild dient das uint8_t globe[80][5][3] Array,&lt;br /&gt;
welches in x-Position (in Drehrichtung), Zeile und Farbindex unterteilt ist. &lt;br /&gt;
Das Byte repräsentiert die 8 vertikalen Bits einer Zeile, ähnlich wie bei einem &lt;br /&gt;
graphischen LCD.&lt;br /&gt;
&lt;br /&gt;
=== Anpassung der Bildwiederholrate ===&lt;br /&gt;
Ich habe den 16bit-Timer-Overflow als Zeitgeber missbraucht, der im Sollfall&lt;br /&gt;
320x (80 x-Werte x4) so schnell auslöst wie die Rotordrehzahl. Die vierfache&lt;br /&gt;
Zählgeschwindigkeit dient dabei der Anpassung der Geschwindigkeit an die&lt;br /&gt;
tatsächliche Drehzahl.&lt;br /&gt;
Bei jedem 4. Timerüberlauf wird dann der entsprechende Längengrad auf die Register geschoben.&lt;br /&gt;
Die [[ISR]] ist natürlich sehr lang, dafür muss aber bei komplexeren Transformationen/Animationen &lt;br /&gt;
nicht auf Laufzeiten geachtet werden. Wichtig dabei: Alle anderen Operationen werden als nicht &lt;br /&gt;
zeitkritisch betrachtet.&lt;br /&gt;
&lt;br /&gt;
Eine weitere ISR wird beim Auftreten des Detektorinterrupts ausgelöst. Diese setzt&lt;br /&gt;
Index und Zählerregister zurück und korrigiert die Wiederholrate anhand des&lt;br /&gt;
tatsächlich erreichten Index.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Bildkonverter ==&lt;br /&gt;
Um ganze Bilder, wie z.B. die Weltkarte einfach auf den Globus zu bekommen, habe ich&lt;br /&gt;
einen kleinen quick&amp;amp;dirty (!) Bildkonverter mit perl und perl-imagemagick geschrieben, der&lt;br /&gt;
direkt einen passenden Header (PROGMEM) generiert. Das Script befindet sich ebenfalls &lt;br /&gt;
im .tgz und sollte hoffentlich mit perl auch unter Windows funktionieren.&lt;br /&gt;
Ein Bild benötigt etwas über 1,2kB Flash. In der animations.c befindet sich die &lt;br /&gt;
passende Ladefunktion + Beispielaufruf.&lt;br /&gt;
&lt;br /&gt;
Es sei darauf hingewiesen, dass das Programm ungefragt die Zieldateien überschreibt!&lt;br /&gt;
Erwartet wird 80x40pix Bild, am besten als .bmp. &lt;br /&gt;
&lt;br /&gt;
Im Script oben können die Schwellenwerte für die Farben eingestellt werden &lt;br /&gt;
(0-1, Werte &amp;gt;1 blenden die Farbe komplett aus), dazu wird noch ein Bild generiert, um&lt;br /&gt;
diese Werte testen zu können, ohne es live auf dem Globus ausprobieren zu müssen.&lt;br /&gt;
&lt;br /&gt;
Aufruf: img2globe.pl image.bmp &amp;lt;Name des Arrays&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Was aufgefallen ist/weitere Hinweise ==&lt;br /&gt;
* Zur Videoaufnahme: Es ist recht schwer richtige Lichtverhältnisse zu finden, da die Kamera ständig nachregeln will. Beste Erfahrungen habe ich mit sehr viel Licht gemacht. Ausserdem sollte dazu die Drehzahl etwas hochgefahren werden, um schwarze Balken zu vermeiden. &lt;br /&gt;
* Der Atmel ist mal wieder sehr an seiner Grenze. Würde ich nochmal einen Globe bauen, käme vermutlich ein Cortex-M3 rein.&lt;br /&gt;
&lt;br /&gt;
== Downloads &amp;amp; Links ==&lt;br /&gt;
Projektseite auf meiner Website:&lt;br /&gt;
* http://leyanda.de/light/povglobe.php&lt;br /&gt;
* [http://www.leyanda.de/light/files/povglobe_v10.tgz Source, Schaltpläne, Layout etc.]&lt;br /&gt;
[[Category:AVR-Projekte]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Latch&amp;diff=79088</id>
		<title>Latch</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Latch&amp;diff=79088"/>
		<updated>2013-10-22T06:58:54Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:Transparent Latch Symbol.svg|thumb|D-Latch]]&lt;br /&gt;
Ein Latch (engl. für &#039;&#039;Schnappschloss&#039;&#039;, &#039;&#039;Auffangregister&#039;&#039;) hat zwei Eingänge (D - Data und E - Enable) und einen Ausgang (Q). Solange E aktiv ist, wird D auf Q durchgeschaltet. Sobald E inaktiv wird, speichert Q den letzen Zustand an D. Ein Latch ist ein zustandsgesteuertes Speicherelement.&lt;br /&gt;
&lt;br /&gt;
Die ICs 74xx373 und 74xx573 enthalten jeweils 8 Latches mit einer gemeinsamen Enable-Leitung. Mit diesen ICs kann man eine Porterweiterung vornehmen, beispielsweise um einen gemultiplexten Adress-/Datenbus anzusteuern.&lt;br /&gt;
&lt;br /&gt;
In Digitalschaltungen in FPGAs oder CPLDs müssen Latches möglichst vermieden werden, da sie ungünstigere Eigenschaften bezüglich des Zeitverhaltens aufweisen als [[FlipFlop]]s.&lt;br /&gt;
&lt;br /&gt;
{| {{Tabelle}} style=&amp;quot;text-align:center&amp;quot;&lt;br /&gt;
|+ &amp;lt;big&amp;gt;&#039;&#039;&#039;Wahrheitstabelle für D-Latch&#039;&#039;&#039;&amp;lt;/big&amp;gt;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Eingänge&lt;br /&gt;
!&lt;br /&gt;
! Ausgang&lt;br /&gt;
|-&lt;br /&gt;
! D&lt;br /&gt;
! E&lt;br /&gt;
!&lt;br /&gt;
! Q&lt;br /&gt;
|-&lt;br /&gt;
|L || H || || L&lt;br /&gt;
|-&lt;br /&gt;
|H ||H || ||H&lt;br /&gt;
|-&lt;br /&gt;
|x ||L || ||Q&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=64436</id>
		<title>AVR Net-IO Bausatz von Pollin</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Net-IO_Bausatz_von_Pollin&amp;diff=64436"/>
		<updated>2012-02-20T14:04:36Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* AVR Net IO */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hier steht eine Beschreibung des Pollin Bausatzes [http://www.pollin.de/shop/shop.php?cf=detail.php&amp;amp;pg=NQ==&amp;amp;a=MTQ5OTgxOTk= AVR-NET-IO. Best.Nr. 810 058], oder als aufgebautes Fertigmodul, Best.Nr. 810 073. &lt;br /&gt;
&lt;br /&gt;
Einige Features: Ethernet-Platine mit ATmega32 und Netzwerkcontroller ENC28J60. Die Platine verfügt über 8 digitale Ausgänge, 4 digitale und 4 ADC-Eingänge, welche alle über einen Netzwerkanschluss (TCP/IP) abgerufen bzw. geschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
[[Datei:AVR-NET-IO ADD-ON.JPG|thumb|right|400px|AVR-NET-IO (links) mit zusätzlicher SUB-D Anschlussplatine (rechts, nicht im Lieferumfang)und Add-On-Board (oben, mit aufgelötetem RFM12-433-Modul, beides nicht im Lieferumfang). Ebenso ist zusätzlich ein nicht im Lieferumfang enthaltener kleiner Kühlkörper auf einem der Spannungsregler montiert und die Schraubklemmen zur Stromversorgung wurden durch Buchsen ersetzt.]]&lt;br /&gt;
&lt;br /&gt;
== Technische Daten ==&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 9 V AC/DC&lt;br /&gt;
* Stromaufnahme ca. 190 mA&lt;br /&gt;
* 8 digitale Ausgänge (0/5 V) [PC0-PC7 an J3]&lt;br /&gt;
* 4 digitale Eingänge (0/5 V) [PA0-PA3 an J3]&lt;br /&gt;
* 4 ADC-Eingänge (10 Bit) [PA4-PA7 an Schraubklemmen]&lt;br /&gt;
* LCD-Anschluss (HD44780 komp. Controller nötig) [PD2-7,PB0,PB3 an EXT]&lt;br /&gt;
* [[ENC28J60]]&lt;br /&gt;
* [http://www.atmel.com/dyn/Products/Product_card.asp?part_id=2014 ATmega32] Mikrocontroller&lt;br /&gt;
&lt;br /&gt;
Maße (L×B×H): 108×76×22 mm.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Die Schaltung des AVR-NET-IO ist recht einfach:&lt;br /&gt;
* Ein ATmega32 Mikrocontroller enthält die gesamte Software&lt;br /&gt;
* Ein ENC28J60 Ethernet-Controller für das Senden und Empfangen von Ethernet Frames (MAC und PHY Ethernet Layer) ist über [[SPI]] mit dem ATmega32 verbunden&lt;br /&gt;
* Ein Ethernet RJ-45 MagJack TRJ 0011 BA NL von [http://www.trxcom.com/ Trxcom] mit eingebautem Übertrager und Anzeige-LEDs am ENC28J60.&lt;br /&gt;
* Ein MAX232 für die serielle Schnittstelle&lt;br /&gt;
* Zwei Spannungsregler, 5 V und 3,3 V&lt;br /&gt;
* &amp;quot;Hühnerfutter&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Fast alle I/O Pins des ATmega32 sind irgendwo auf Anschlüssen herausgeführt. Entweder auf dem SUB-D Stecker, dem EXT oder ISP Wannensteckern oder den blauen Anschlussklemmen. Eine Schutzbeschaltung gibt es nicht.&lt;br /&gt;
&lt;br /&gt;
Die blauen Anschlussklemmen haben eine Nut und eine Feder mit denen man&lt;br /&gt;
sie zusammenstecken kann, dadurch ist das Anlöten wesentlich leichter&lt;br /&gt;
und sie stehen auch sauber in der Reihe.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterungsplatine ===&lt;br /&gt;
&lt;br /&gt;
Seit Januar 2010 gibt es auch eine Erweiterungsplatine &lt;br /&gt;
&lt;br /&gt;
[http://www.pollin.de/shop/dt/Nzg4OTgxOTk-/Bausaetze/Diverse/Bausatz_Add_on_fuer_AVR_NET_IO.html Add-on für AVR-NET-IO-Board Best.Nr. 810 112]&lt;br /&gt;
&lt;br /&gt;
Diese Platine erweitert das NET-IO um:&lt;br /&gt;
&lt;br /&gt;
* SD-Karten-Slot über SPI&lt;br /&gt;
* Display über PCF 8574&lt;br /&gt;
* Infrarot&lt;br /&gt;
* [[RFM12]] Funkmodul (nicht im Lieferumfang enthalten)&lt;br /&gt;
&lt;br /&gt;
Ausserdem soll es die 3.3V Versorgung der Hauptplatine verbessern. Dazu sollte man einen 4,7 kOhm Widerstand parallel zu R2 schalten. Sonst beträgt die Ausgangsspannung nur ca. 2,8V. (Tipp aus dem u.g. Thread im Forum)&lt;br /&gt;
&lt;br /&gt;
Um bei einem Neuaufbau parallele Widerstände zu vermeiden, sollten folgende Änderungen auf dem Addon-Board gemacht werden:&lt;br /&gt;
*R2 1,5kΩ ersetzen mit 2kΩ&lt;br /&gt;
*R3 1,8K ersetzen mit 3,3kΩ&lt;br /&gt;
*R19 470kΩ ersetzen zu 470Ω&lt;br /&gt;
*Q1 BC548 ersetzen durch BC327 oder BC328 (Hauptsache PNP! und mehr als 100mA)&lt;br /&gt;
&lt;br /&gt;
Stand Feb. 2011: R2 wird mit 2,2kΩ und R3 wird mit 3,6kΩ ausgeliefert. Somit werden die 3,3 V richtig erzeugt. R19 hat 470Ω.&lt;br /&gt;
Der ISP-Anschluß ist nicht vollständig durchgeschleift, es besteht keine Verbindung der RESET-Leitung zwischen ISP und ISP1 (Abhilfe: Drahtbrücke einlöten, [http://www.mikrocontroller.net/topic/161354#1600385 Quelle]). &lt;br /&gt;
&lt;br /&gt;
Stand Nov. 2011: bei mir ist die RESET-Leitung korrekt zw. ISP und ISP1 verbunden. Es gibt jetzt auch einen R24 (470Ohm) und R11 (1KOhm), der in der bei mir mitgelieferten Bauanleitung fehlt, in der zum Download (V1.1) angebotenen  aber drin steht.&lt;br /&gt;
Es wird immer noch der falsche Q1 BC548C (NPN) mitgeliefert. Das Schaltsymbol für einen PNP ist richtig im Schaltplan gezeichnet.&lt;br /&gt;
&lt;br /&gt;
Stand Dez. 2011: &#039;&#039;&#039;R24&#039;&#039;&#039; (470Ohm) sollte mit 0 Ohm ersetzt&lt;br /&gt;
und &#039;&#039;&#039;R11&#039;&#039;&#039; (1KOhm) völlig weggelassen werden! &amp;lt;br&amp;gt;&lt;br /&gt;
Diese Widerstände bilden einen überflüssigen Spannungsteiler in der MISO Leitung. &amp;lt;br&amp;gt;                                                              &lt;br /&gt;
[http://son.ffdf-clan.de/include.php?path=forumsthread&amp;amp;threadid=1167&amp;amp;postid=9203 Fehler im Add-On V1.1]&lt;br /&gt;
&lt;br /&gt;
Erste Erfahrungsberichte im Forum http://www.mikrocontroller.net/topic/161354&lt;br /&gt;
&lt;br /&gt;
=== Hardware-Umbauten &amp;amp; -Verbesserungen ===&lt;br /&gt;
&lt;br /&gt;
* Kühlkörper auf dem 7805 - (Vorsicht: Der LM317T ist rückseitig spannungsführend. Den Kühlkörper also keinesfalls an beide Spannungsregler anschließen!)&lt;br /&gt;
* MAX232 nach anfänglicher Konfiguration nicht bestücken um Strom zu sparen oder um zwei weitere I/O-Pins zu gewinnen&lt;br /&gt;
* 10µF-Elkos für MAX232N (C14-C17) durch 1µF ersetzen. Eine 10µF-Version für den MAX232 gibt es nicht. Die 10µF-Elkos können auch Ursache einer nicht funktionierenden RS232 sein.&lt;br /&gt;
** Laut Spezifikation sind auch mehr als 1µF erlaubt. Selbst Atmel verwendet beim STK500 10µF. Dies führt keinesfalls dazu, dass die RS232 nicht mehr funktioniert.&lt;br /&gt;
* Die IC-Fassungen aus &amp;quot;Pollins Resterampe&amp;quot; durch Fassungen mit gedrehten Kontakten ersetzen. &lt;br /&gt;
* &#039;&#039;Netz&#039;&#039; LED nicht bestücken oder größere Widerstände einlöten um Strom zu sparen (R3)&lt;br /&gt;
* Vorwiderstände der Ethernet-LEDs größer machen (z.&amp;amp;nbsp;B. verdoppeln) um Strom zu sparen (R6,R7)&lt;br /&gt;
* Linear-Spannungsregler ersetzen&lt;br /&gt;
* Kondensator an AREF-Pin des ATmega32 (ATmega32 Datenblatt) (100nF gegen Masse)&lt;br /&gt;
* Kondensator an den RESET-Pin des ATmega32 ([http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Atmel Application Note AVR042: AVR Hardware Design Considerations]) Wenn man diese Quelle genauer liest, ist das aber eher unnötig. - Kondensator bei selbstbau-ISP empfehlenswert.&lt;br /&gt;
* Umbau auf 3,3 V:&lt;br /&gt;
** Ersatz der Spannungsregler durch einen einzigen 3,3 V Regler&lt;br /&gt;
** Anpassen (verkleinern) des LED-Vorwiderstands R3 für 3,3 Volt Betrieb&lt;br /&gt;
** Reduktion der Taktfrequenz (Austausch von Q2) auf den bei 3,3V erlaubten Bereich des ATmega32 ( ATmega32(L)  3.3V /8.0 Mhz Takt )&lt;br /&gt;
** Ersatz des MAX232 durch einen MAX3232&lt;br /&gt;
[[Bild:POWER.JPG|thumb|400px|5V Stromversorgung über USB Kabel, ohne 5 V Spannungsregler und Gleichrichterdioden, Vorsicht: kein Verpolungsschutz!  ]]&lt;br /&gt;
* ATmega32 vom ENC28J60 takten (OSC2)&lt;br /&gt;
* Betrieb mit Gleichspannung:&lt;br /&gt;
** Dioden D2 und D5 durch Drahtbrücken ersetzen, D1 und D4 nicht bestücken (komplette Entfernung des Brückengleichrichters, beinhaltet Verlust des Verpolungsschutzes)&lt;br /&gt;
** Diode D2 bestücken, D5 durch Drahtbrücke ersetzen, D1 und D4 nicht bestücken (Brückengleichrichter durch Verpolungsschutze ersetzen)&lt;br /&gt;
*** ??? Ist dies nicht kontraproduktiv? Bei mir wurde durch D2-Bestückung die Eingangsspannung von ca. 5,2 V am LM317T auf ca. 4,6 V gedrückt, so dass am ENC28J60 nur ca. 2,6 V ankamen (außerhalb der lt. Datenblatt &amp;quot;Operating voltage range of 3.14V to 3.45V&amp;quot;). Man müsste also ein geregeltes Netzteil mit ca. 5,5 V anschließen um 5 und 3,3 V zu erzielen. Dann lieber den Verpolungsschutz durch andere Maßnahmen sicherstellen.&lt;br /&gt;
** Beim Betrieb von USB beachten, dass USB-Spezifikation keinesfalls 5V garantiert, sondern Spannung bis runter 4.4V erlaubt und dann u.U. durch den LM317 nicht mehr genügend Spannung am ENC anliegt. Das äußert sich so, dass zwar der Atmega einwandfrei funktioniert, die Ethernet-Kommunikation aber nicht oder nur sehr sporadisch.&lt;br /&gt;
* Ersatz des ATmega32 durch einen ATmega644 oder ATmega1284p mit mehr FLASH-Speicher.&lt;br /&gt;
* 100nF über alle drei IC Störunterdrückung zusätzlich bestücken&lt;br /&gt;
&lt;br /&gt;
== Inbetriebnahme der Originalsoftware ==&lt;br /&gt;
=== Einleitung ===&lt;br /&gt;
&lt;br /&gt;
Die bei Auslieferung (Stand September 2008) in den ATmega32 gebrannte Firmware stellt sich manchmal recht zickig an. Es scheint dann die Netzwerk-Schnittstelle, ggf. auch  die seriell Schnittstelle, nicht zu funktionieren. Falls es Probleme geben sollte, sollte man erst einmal einen Firmwareupdate versuchen. Dies geschieht über die serielle Schnittstelle mittels des Programmes NetServer (aktuelle Version 1.03, Februar 2010), die dem Bausatz beiliegt. &lt;br /&gt;
&lt;br /&gt;
Falls die serielle Schnittstelle ebenfalls nicht zugänglich ist, kann mit den im folgenden beschriebenen Schritten die Inbetriebnahme der Software möglich sein. Dazu benötigt man:&lt;br /&gt;
&lt;br /&gt;
* Einen Windows-PC mit Ethernet-Schnittstelle und RS232-Schnittstelle (ein Prolific RS232-USB Konverter funktioniert)&lt;br /&gt;
* Entweder&lt;br /&gt;
**zwei normale (&#039;&#039;straight through&#039;&#039;) Ethernet-Kabel und einen Ethernet Switch/Hub, oder&lt;br /&gt;
**ein gekreuztes(&#039;&#039;cross over&#039;&#039;) Ethernet-Kabel&lt;br /&gt;
* Einen AVR Programmer (Hardware und Software). Zum Beispiel einen [[AVR Dragon]] oder [[STK500]] mit [[AVR Studio]] oder das [[Pollin ATMEL Evaluations-Board]] und [[avrdude]].&lt;br /&gt;
* Die [http://www.pollin.de/shop/ds/MTQ5OTgxOTk-.html Pollin NetServer Software], Version 1.03 (oder neuer)&lt;br /&gt;
&lt;br /&gt;
=== Gelieferten ATmega32 richtig einstellen ===&lt;br /&gt;
[[image:fuse_bits_avr_studio.jpg|thumb|right|250px|Einstellungen der Fuse-Bits mittels AVR Studio 4]]&lt;br /&gt;
Die Fuses der gelieferten ATmega32s scheinen nicht immer mit den im Handbuch auf Seite 12 als erforderlich angegebenen Fuse-Einstellungen übereinzustimmen.&lt;br /&gt;
&lt;br /&gt;
Dies kann man mittels eines Programmers ändern. LFuse = 0xBF, HFuse = 0xD2. Das genaue Vorgehen hängt dabei vom verwendeten Programmer ab. Bei der Gelegenheit kann man ebenfalls eine Sicherheitskopie des ursprünglichen Flash-Inhalts und des EEPROMs anfertigen. Das EEPROM scheint die MAC-Adresse des Ethernet-Ports zu enthalten.&lt;br /&gt;
&lt;br /&gt;
Entgegen der Spezifikation im Handbuch von Pollin sollten die &#039;&#039;&#039;HFuses auf 0xC2&#039;&#039;&#039; gesetzt werden, d. h. CKOPT-Fuse programmiert (dies ist in der Software Version 1.03 bereits vollzogen). Das sorgt für einen stabilen Betrieb des AVR-Oszillators im &amp;quot;full rail-to-rail swing&amp;quot;-Mode bei 16 MHz. Atmel garantiert ansonsten nur stabilen Betrieb bis 8 MHz. Siehe ATmega32-Datenblatt, Kapitel 8.4, Crystal Oscillator.&lt;br /&gt;
{{Absatz}}&lt;br /&gt;
&lt;br /&gt;
==== Funktionsfähige Konfiguration - AVR-Prog ====&lt;br /&gt;
&lt;br /&gt;
[[Bild:Avrprog.png|thumb|right|250px]]&lt;br /&gt;
Benutzer von AVR-Prog können die nachfolgenden Einstellungen für die Lock- und Fuse-Bits verwenden. Hierbei handelt es sich um die ausgelesenen Einstellungen eines funktionsfähigen Controllers. Allerdings sollte, laut Handbuch des AVR-NET-IO-Boards, das Fuse-Bit EESAVE eigentlich gesetzt sein. &lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch per avrdude die Einstellung getroffen werden:&lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U lfuse:w:0xBF:m&lt;br /&gt;
und &lt;br /&gt;
 avrdude -c stk500v2 -pm32 -U hfuse:w:0xC2:m&lt;br /&gt;
&lt;br /&gt;
Anschließend muß noch der Bootloader und die Firmware aktualisiert werden (siehe Handbuch AVR-NET-IO-Board Seite 12 Punkt 3).&lt;br /&gt;
&lt;br /&gt;
=== PC Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
==== PC normalerweise nicht im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
Betreibt man den PC nicht im 192.168.0.0/24 Subnetz, muss er wie folgt umkonfiguriert werden, oder die IP Adresse des Boards wird entsprechend angepasst. ( Siehe Handbuch Seite 14ff. Das ist meist sinnvoller und auch einfacher. ) &lt;br /&gt;
&lt;br /&gt;
Den PC vom normalen Netzwerk abstecken[1]. Zur Umkonfiguration dazu bei Windows XP in der Systemsteuerung &#039;&#039;Netzwerkverbindungen&#039;&#039; aufrufen und die lokale &#039;&#039;LAN-Verbindung&#039;&#039; markieren. Dann in der rechten Leiste &#039;&#039;Einstellungen dieser Verbindung ändern&#039;&#039; aufrufen. &lt;br /&gt;
&lt;br /&gt;
Es erscheint der Dialog &#039;&#039;Eigenschaften von &amp;lt;Verbindungsname&amp;gt;&#039;&#039;. In der Liste im Dialog zu &#039;&#039;Internetprotokoll (TCP/IP)&#039;&#039; gehen. Ein Doppelklick auf den Eintrag öffnet den &#039;&#039;Eigenschaften von Internetprotokoll (TCP/IP)&#039;&#039; Dialog.&lt;br /&gt;
&lt;br /&gt;
In diesem Dialog &#039;&#039;Folgende IP-Adresse verwenden:&#039;&#039; auswählen und zum Beispiel&lt;br /&gt;
&lt;br /&gt;
IP-Adresse: &#039;&#039;&#039;192.168.0.100&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Subnetzmaske: &#039;&#039;&#039;255.255.255.0&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Standardgateway: &#039;&#039;&#039;192.168.0.1&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
eingeben. &lt;br /&gt;
&lt;br /&gt;
Anmerkung von bitman:&lt;br /&gt;
[1] Dies ist spätestens ab Windows XP nicht mehr notwendig, wenn das Netz 192.168.0.0/24 noch frei ist. Dann kann man einfach den Client &#039;&#039;zusätzlich&#039;&#039; in diesem Netzwerk zusätzlich einbinden über Einstellungen/Netzwerkverbindungen/Lanverbindung/Eigenschaften/TCP-IP/Eigenschaften/Erweitert/IP-Adresse hinzufügen. Es werden dann eben mehrere IP-Adressen an den NIC gebunden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle geöffneten Dialoge nacheinander mit OK schließen.&lt;br /&gt;
&lt;br /&gt;
Alternativ bietet sich das Umprogrammieren des Boards über die serielle Schnittstelle an. Die Werte für IP-Adresse, Netzmaske und Standard-Gateway werden mit den dokumentierten SETxx-Befehlen geändert, das Board neu gestartet und ans vorhandene Netzwerk gesteckt.&lt;br /&gt;
&lt;br /&gt;
Im EEPROM sind folgende Werte vorprogrammiert:&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
3EE - 3F3 MAC-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
3F4 - 3F7 GATEWAY&amp;lt;br&amp;gt;&lt;br /&gt;
3F8 - 3FB NETMASK&amp;lt;br&amp;gt;&lt;br /&gt;
3FC - 3FF IP-ADRESSE&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PC bereits im 192.168.0.0/24 Subnetz ====&lt;br /&gt;
&lt;br /&gt;
In diesem Fall muss man prüfen, ob die IP-Adresse 192.168.0.90 bereits im Subnetz verwendet wird. Ist dies der Fall, muss das verwendete Gerät mit dieser IP vorübergehend aus dem Subnetz entfernt werden. Es sei denn, dabei handelt es sich um den PC. In diesem Fall muss er wie zuvor umkonfiguriert werden. Ansonsten kann der unverändert im Netz verbleiben.&lt;br /&gt;
&lt;br /&gt;
Dem AVR-NET-IO gibt man eine neue, zuvor unbenutzte Adresse (siehe unten). Dann kann das abgekoppelte Gerät wieder angeschlossen werden, beziehungsweise der PC zurückkonfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
=== AVR-NET-IO anschließen ===&lt;br /&gt;
&lt;br /&gt;
Musste man den PC umkonfigurieren, so werden jetzt nur der PC und der AVR-NET-IO über Ethernet miteinander verbunden. Je nach Ethernet-Kabel benötigt man dazu einen Switch/Hub oder nicht.&lt;br /&gt;
&lt;br /&gt;
Musste man den PC nicht umkonfigurieren, so kann man den AVR-NET-IO wie einen normalen Rechner an das vorhandenen Netz anschließen.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich schließt man die serielle Schnittstelle des AVR-NET-IO an den PC an.&lt;br /&gt;
&lt;br /&gt;
=== Firmware 1.03 einspielen ===&lt;br /&gt;
&lt;br /&gt;
Laut Handbuch sollte der AVR-NET-IO jetzt über Ethernet funktionieren. Ebenso sollte er über die serielle Schnittstelle und ein Terminalprogramm konfigurierbar sein. Beides ist offensichtlich im Auslieferungszustand selten der Fall.&lt;br /&gt;
&lt;br /&gt;
Auch wenn sich Pollins NetServer Software nicht mit dem AVR-NET-IO verbinden lässt, so ist sie jedoch in der Lage eine neue Firmware 1.03 einzuspielen. Das Vorgehen ist im Handbuch auf Seite 12 beschrieben. NetServer präsentiert dabei ein paar einfache Anweisungen denen man folgen sollte.&lt;br /&gt;
&lt;br /&gt;
Wenn sich nach dem scheinbar erfolgreichem Einspielen der Firmware, später nichts tut, so sollte die Firmware mit gesetztem &amp;quot;FailSafe&amp;quot; in der mitgelieferten NetServer-Software nochmals Eingespielt werden.&lt;br /&gt;
&lt;br /&gt;
=== Abschluss ===&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte sich die NetServer Software mit dem AVR-NET-IO über Ethernet verbinden lassen. Dies macht es wiederum möglich, den AVR-NET-IO mit einer anderen IP-Adresse zu versehen. Will man den AVR-NET-IO in einem anderen Subnetz betreiben kann man dies jetzt einstellen.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die IP-Adresse neu eingestellt hat, muss man den PC zurückkonfigurieren und kann dann sowohl den AVR-NET-IO und den PC zusammen betreiben.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Fehler ==&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[#Hardware-Umbauten_.26_-Verbesserungen|Hardware-Umbauten und Verbesserungen]]&amp;lt;br&amp;gt;&lt;br /&gt;
Käufer berichten von fehlenden Bauteilen im Bausatz (Wannenstecker, Widerstände, Kondensatoren, Induktivitäten). Für Reklamationen: [https://www.pollin.de/shop/kontakt_service/reklamation.html]&lt;br /&gt;
&lt;br /&gt;
* Die Stückliste auf Seite 4 in den Anleitung mit den Versionsangaben&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
:ist falsch. Pollin legt dem Bausatz irgendwann ab September 2008 einen gedruckten Korrekturzettel bei. Die Online-Version der Anleitung ist korrigiert.&lt;br /&gt;
* Im Schaltplan auf Seite 7 in den Anleitungen mit den Versionen&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, kloiber, #1100, wpe&#039;&#039; (gedruckt im Bausatz)&lt;br /&gt;
** &#039;&#039;Stand 20.08.2008, cd, #all, wpe&#039;&#039; (auf der CD)&lt;br /&gt;
** &#039;&#039;Stand 03.09.2008, online, #all, wpe&#039;&#039; (Online)&lt;br /&gt;
:ist eine 25-polige SUB-D Buchse gezeichnet. Geliefert wird und in der Stückliste verzeichnet ist ein Stecker.&lt;br /&gt;
&lt;br /&gt;
* Die September 2008 ausgelieferte Firmware im ATmega32  funktioniert bei vielen nicht und muss erst upgedatet werden (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Im Flash der gelieferten AVR ist anders als beschrieben nur der Bootloader enthalten, die eigentliche Firmware muss erst mit Hilfe der Updatefunktion geladen werden. Wenn zusätzlich auch die Fuses falsch gebrannt sind, dann funktioniert das Update nicht, auch wenn das PC Programm was anderes behauptet.&lt;br /&gt;
&lt;br /&gt;
* Die Fuse-Einstellungen des ausgelieferten ATmega32 entspricht nicht der Anleitung (siehe [[#Inbetriebnahme der Originalsoftware|Inbetriebnahme der Originalsoftware]])&lt;br /&gt;
&lt;br /&gt;
* Bausatz, gekauft am 27.10.08, Anleitungsversion 19.09.08, ohne Probleme oder erkennbare Fehler zusammengebaut und in Betrieb genommen.&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft 29.09.2008, Pinbelegung des 25 poligen D-Sub &amp;quot;Anschlusses&amp;quot; stimmt nicht mit der Anleitung überein. Der Aufdruck auf der Platine ist falsch. Pin1 &amp;lt;-&amp;gt; Pin13, Pin2 &amp;lt;-&amp;gt; Pin12 usw. Setzt man den D-Sub Stecker ein, so sind dessen Pinnummern korrekt. Bei einem Bausatz gekauft 10/2010 ist dies ebenfalls noch der Fall.&lt;br /&gt;
&lt;br /&gt;
* 3 Bausätze Anf. Oktober 2008 gekauft, bei einem waren 2 LM317 dabei, dafür fehlte der 7805 - aus der Bastelkiste ersetzt. Alle haben jedoch auf Anhieb funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz gekauft Ende Januar 2009. Die Lock-Bits (u.a. für PonyProg2000) werden falsch beschrieben. Die in Klammern aufgeführten Werte stimmen bei einem Bit nicht. Die Texte &amp;quot;Programmiert/Unprogrammiert&amp;quot; hingegen schon. Bei den Bauteilen gab es 4 Kondensatoren mit der Aufschrift &amp;quot;220&amp;quot;, ich habe diese durch welche mit 22p ersetzt, da ich nicht sicher war ob wirklich 22p geliefert wurden. Dafür wurden statt einem zwei 7805 und statt einem mindestens vier LM317 mitgeliefert.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 22.4.2009. Alles vollständig, zusammengebaut, läuft. Software-Version 1.03. Für den oben schon genannten Steckverbinder wurde eine Buchse geliefert. Allerdings stimmen die PIN-Nummern im Schaltplan nicht mit den PIN-Nummern auf der Buchse überein (sie sind gespiegelt), daher liefen die Test-LEDs zunächst nicht.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 11.7.2009. Spannungsregler LM317T fehlt, grüne statt roter LED. Ein Kondensator 22pF zu viel. LM317T wurde auf Anfrage kostenlos nachgeliefert (27.7.). Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 24.7.2009. Ein Quarz 16MHz zu viel, ebenfalls grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz geliefert 20.08.2009. Ein Kondensator 22pF zuviel und grüne statt rote LED.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juli &#039;09 gekauft, grüne statt rote LED&lt;br /&gt;
&lt;br /&gt;
* Bausatz 25.09.09 geliefert, grüne Betriebs-LED, ein ELKO zuviel, Fehler 1µF am MAX232 statt 100nF behoben, richtiger C wird mitgeliefert, Aufbau komplett nach Pollin Anleitung durchgeführt, auf Anhieb fehlerfrei!&lt;br /&gt;
&lt;br /&gt;
* Bausatz 17.10.09 geliefert, grüne Betriebs-LED, zwei 100nF Kondensatoren zu wenig. Aufbau und Inbetriebnahme problemlos.&lt;br /&gt;
&lt;br /&gt;
* Bausatz 21.10.09 gekauft, grüne Betriebs-LED. Aufbau problemlos, RS232 läuft nicht. LAN läuft&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Nov. 09 gekauft, grüne LED, ENC28J60, MAX232 und ATmega32 fehlen, Nachlieferung nach einer Woche&lt;br /&gt;
&lt;br /&gt;
*Bausatz Nov. 09 gekauft,Bauteile komplett.Verbindungsaufbau Seriell klappt erst nach mehreren Versuchen.Problem gelöst:Spannung an MAX und Mega zu niedrig&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dez. 09 gekauft, grüne LED, 100µF Kondensator fehlt, alles o.k.&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 09 gekauft, alle teile da nach Einstellen der fusebits lief alles perfekt&lt;br /&gt;
&lt;br /&gt;
* Bausatz Okt. 09 gekauft, ein 100nF Kondensator und 25MHz Quarz fehlten ... hab beim lokalen Elektronikhändler keinen 25Mhz Grundton Quarz sondern nur im 3. Oberton bekommen aber mit R2.2k parallel zum Quarz schwingt er in der Schaltung schön bei 25Mhz. Mit 1µF am MAX232 funktioniert jetzt auch die RS232.&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Feb. 10 gekauft, bei beiden fehlten 7805, L1+L2 je 100µH sowie 4x falscher Wert Kondensator an Max232 vorhanden. Fehlende Bauteile nachgelötet und Funktion getestet. Hat alles einwandfrei funktioniert!!!&lt;br /&gt;
&lt;br /&gt;
* Bausatz März. 10 gekauft, RS232 Printbuchse fehlt, dafür 1x 10pol Wannenstecker zuviel. Grüne LED statt Rot. Funktioniert ansonsten einwandfrei.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Jan. 10 gekauft, gelbe LED statt rot, C14...C17: 10µF, weder seriell noch via Ethernet Konnektivität. Nach Austausch von C14-C17 gegen 1µF, wenigstens serielle Kontaktaufnahme möglich, kein Ethernet auch nach Flash von 1.03 mit NetServer.&lt;br /&gt;
&lt;br /&gt;
* Bausatz Feb. 10 gekauft, Spannungsregler LM317T fehlte&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft, gelbe statt rote LED geliefert, aber Aufbau und inbetriebnahme lt. Handbuch ohne Probleme&lt;br /&gt;
&lt;br /&gt;
* Bausatz März 10 gekauft und gelbe statt rote LED geliefert, funzt wunderbar gemäß Anleitung&lt;br /&gt;
&lt;br /&gt;
* Fertig gelötete Platine gekauft. µC war falsch im Sockel.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - Funktion erst nach Ersetzung des ADM durch nen MAX&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft und gelbe statt rote LED geliefert, ADM232LJN statt MAX232 - funktionierte sofort auch mit dem ADM232LJN.&lt;br /&gt;
&lt;br /&gt;
* Bausatz April 10 gekauft wurde mit grüner statt roter LED Ausgeliefert&lt;br /&gt;
&lt;br /&gt;
* Bausatz Juni 10 gekauft: wurde mit grüner statt roter Netz-LED ausgeliefert, 2x 22pF Kerko zuviel&lt;br /&gt;
&lt;br /&gt;
* Bausatz August 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
* Bausatz Juli 10 gekauft: 2 Quarze mit 16 MHz geliefert, statt 1x 16MHz und   1x25MHz.&lt;br /&gt;
* Bausatz September 10 gekauft: hat sofort funktioniert. 1x 3,3k und 1x 10k Widerstand zuviel. Statt 100nF Kondensatoren wurden 1µF geliefert -&amp;gt; Platzprobleme auf der Platine durch grössere Bauform. LED grün.&lt;br /&gt;
* Bausatz Oktober  6 gekauft: alles funktioniert. LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Fertigmodul Oktober 10 gekauft: Auf Anhieb alles funktioniert!&lt;br /&gt;
* Bausatz Oktober 10 gekauft: komplett und sofort funktioniert&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft: komplett und sofort funktioniert (sogar mit der neusten Pollin Firmware 1.03 schon drauf) LED grün statt rot.&lt;br /&gt;
&lt;br /&gt;
* Bausatz November 10 gekauft. Nach Bezug neuer Feinst-Lötspitzen konnte ich ihn dann auch zusammenlöten. Es hat sofort alles funktioniert, obwohl ich nur 12V bzw. 9V Gleichspannung zur Verfügung hatte, und nicht sicher war, wieviel die Komponenten wirklich benötigen. Der Regler wird auch bei 9V Gleichspannungsversorgung noch sehr warm. Da muss auf jeden Fall ein Kühlkörper dran! Ich habe auch eine grüne LED bekommen, ist mir aber wurscht :-)&lt;br /&gt;
&lt;br /&gt;
* Bausatz Dezember 10 gekauft: komplett und funktionierte sofort. Firmware 1.03, grüne LED (Ein Quarz und ein IC-Sockel zu viel)&lt;br /&gt;
&lt;br /&gt;
*Bausatz Januar 2011 gekauft: nur genau die richtige Anzahl Teile dabei, Firmware 1.03, grüne LED, ging auf Anhieb&lt;br /&gt;
&lt;br /&gt;
* 2x Bausatz Januar 2011 gekauft: beide grüne LED, und 1x doppelter Satz Jumper/Stiftleiste, 22PF und Anschlussklemmen. Rest vollständig und beide haben sofort nach zusammenbau funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: die Diode D5 fehlt, 10 µF gegen 1 µF für MAX232 getauscht, Flash im ATmega32 war programmiert, passende IP-Adr über serielle Schnittstelle eingestellt; ADD-ON: für R1 war 22Ω statt 0,2Ω beigelegt, durch richtigen ersetzt, Beschreibung der LED Bestückung mangelhaft / oe1smc&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 1 Diode zuviel, 2 Spulen fehlen, LED grün. Die fehlenden Spulen wurden durch welche aus der Bastelkiste ersetzt - funktioniert. Der 7805 bekam einen kleinen Kühlkörper spendiert.&lt;br /&gt;
&lt;br /&gt;
* Februar 2011: AVR-NET-IO: 2x 10k Widerstände fehlen. Dafür eine Diode zu viel.&lt;br /&gt;
&lt;br /&gt;
* Ende Februar 2011: Zwei Bausaetze an jeweils zwei Adressen, alles in Ordnung.&lt;br /&gt;
&lt;br /&gt;
* Ende März 2011: 2x 25 Mhz Quarz statt 1x16 u. 1x25 Mhz. LED fehlt. Bausatz funktioniert nach Tausch des Quarz den mir mein Freund oe9rsv aus seinem Fundus spendiert hat. Danke auch für die Hilfe beim Fehler suchen.&lt;br /&gt;
&lt;br /&gt;
* Mitte 2010 gekauft: 1x 100nF fehlt&lt;br /&gt;
&lt;br /&gt;
* Mitte Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, Pollin wollte, dass ich das ganze Paket zurückschicke für einen Austausch. Ein 51Ω zu viel. 16Mhz im Handel und 25Mhz vom alten Mobo ausgelötet. Läuft wunderbar.&lt;br /&gt;
&lt;br /&gt;
* Anfang Juni 2011: Beide Quarze fehlen und beide Spannungsregler fehlen, nach kurzer Mail an Pollin (leider ohne Antwort) wurden diese nach ca. 1 Woche in einem Brief nachgeliefert.&lt;br /&gt;
&lt;br /&gt;
* August 2011: alles 1a...&lt;br /&gt;
* August 2011: Platine fehlte -&amp;gt; in Nachlieferung&lt;br /&gt;
* 30 August 2011: alles 1a...&lt;br /&gt;
* 06 Sep. 2011: alles 1a...&lt;br /&gt;
* August 2011: 6 Stück bestellt, bei einem haben die 100nF Kondensatoren gefehlt, bei einem zwei LM317 statt 7805 und LM317. Angerufen, 3 Tage später Nachlieferung erhalten.&lt;br /&gt;
* Nov. 2011: &#039;&#039;&#039;Net_IO&#039;&#039;&#039; vollständig. Einspielen der Firmware 1.03 war erforderlich. Bei &#039;&#039;&#039;Add-On&#039;&#039;&#039; immer noch falscher Q1 BC548 (NPN) in Stückliste und Lieferung. BC327-40 oder BC328-40 (PNP) nachgefordert. R11 und R24 mitgeliefert entsprechend Beschreibung V1.1 von Pollin&#039;s Download. Beiliegende Beschreibung war älter, ohne diese Widerstände.&lt;br /&gt;
&lt;br /&gt;
* Ende Nov.2011, alle Teile dabei, Firmware war drauf, sofort funktioniert.&lt;br /&gt;
&lt;br /&gt;
* Anfang Dez.2011, komplett bestückte Platine gekauft. Auf 7805 Kühlkörper gebaut da er nach 1Minute schon ausgestiegen ist (LED hat das Pumpen angefangen). Firmware 1.03 musste noch aufgespielt werden danach funktioniert alles einwandfrei. In betrieb mit 10V DC&lt;br /&gt;
* Ende Dez.2011 2xNET-IO und 2xADD bestellt 4 verschieden volle Kisten bekommen... WSL16 ist mit verriegelung geht nicht aufs board jumper fehlen spannungsregler doppelt potti löcher zu klein lsb3 fehlt sd-slot hab ich jetzt 3 100nF hab ich jetzt 4 übrig ... also immnoch lustig  HW stand immernoch 1.0 ( gab es überhaupt eine 1.1?)&lt;br /&gt;
&lt;br /&gt;
* Mitte Jan. 2012, 10pol. beide Wannenstecker nicht dabei, Firmware war drauf, sofort funktioniert. 1 LED zuviel. Unproblematische Nachlieferung bei Reklamation (wegen der beiden Wannenstecker kam ein PAKET!).&lt;br /&gt;
&lt;br /&gt;
== Andere Software für den Client-PC ==&lt;br /&gt;
=== NetIOLib ===&lt;br /&gt;
&lt;br /&gt;
In C# geschriebene Bibliothek zur Ansteuerung der Platine im Orginalzustand. Inkl. Beispielsoftware und Quellcode (GNU GPL) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DLL: [http://www.tware.org/downloads/NetIOLib_dll.zip Download-Link]&lt;br /&gt;
Source: [http://www.tware.org/downloads/NetIOLib_src.zip Download-Link]&lt;br /&gt;
&lt;br /&gt;
=== ControlIO ===&lt;br /&gt;
Einfache Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://www.mikrocontroller.net/topic/149695&lt;br /&gt;
&lt;br /&gt;
=== JAVA Lib ===&lt;br /&gt;
Einfache Java-Bibliothek zur Ansteuerung mit Originalfirmware.&lt;br /&gt;
http://son.ffdf-clan.de/?path=forumsthread&amp;amp;threadid=611&lt;br /&gt;
&lt;br /&gt;
=== App NetIO  ===&lt;br /&gt;
Frei verfügbare App für Windows Mobile zur Ansteuerung mit der Orginalsoftware. Das HTC HD2 wird damit zur Fernsteuerung für das AVR Net-IO Board.&lt;br /&gt;
http://www.heesch.net/netio.aspx&lt;br /&gt;
&lt;br /&gt;
=== PHP ===&lt;br /&gt;
PHP Klasse zur Ansteuerung mit der Originalfirmware. (Opensource Lizenz)&lt;br /&gt;
http://blog.coldtobi.de/1_coldtobis_blog/archive/298_pollin_net-io_php_library.html&lt;br /&gt;
&lt;br /&gt;
PHP Funktionen zum Ansteuern der Originalfirmware. (Free for All Lizenz)&lt;br /&gt;
http://defcon-cc.dyndns.org/projects/mikrocontroller/netio.php&lt;br /&gt;
&lt;br /&gt;
=== NET-IO Control ===&lt;br /&gt;
Eine Application für das Android Betriebssystem zur Steuerung des AVR Net-IO Boards &lt;br /&gt;
&lt;br /&gt;
[[Datei:NET-IO-Control.png|200px]] [[Datei:NET-IO-Control2.png|200px]] [[Datei:NET-IO-Control3.png|200px]]&lt;br /&gt;
&lt;br /&gt;
Team: [http://elektronik2000.de/ E2000-Team]&lt;br /&gt;
&lt;br /&gt;
=== iPhone ===&lt;br /&gt;
==== NetIO ====&lt;br /&gt;
[[Datei:NetIO_Screenshot1.png|160px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Schöne iPhone Fernbedienung für das Board. Konfigurierbar über eine JSON Datei. (Senden, Auslesen, Makros) &amp;lt;br&amp;gt;&lt;br /&gt;
AppStore Link: http://itunes.apple.com/app/netio/id464924297?mt=8 &amp;lt;br/&amp;gt;&lt;br /&gt;
Die gleiche Konfiguration kann auch mit einem [https://github.com/davideickhoff/NetIO-OSX-Dashboard-Widget OSX Widget] benutzt werden. &amp;lt;br/&amp;gt;&lt;br /&gt;
Die inzwischen übersichtliche Webseite dazu findet man unter http://netio.davideickhoff.de &lt;br /&gt;
&lt;br /&gt;
Hinweis: &amp;lt;br&amp;gt; Man findet es unter &amp;quot;NetIO&amp;quot; im AppStore, im Icon selbst und in iTunes wird es als &amp;quot;Controller&amp;quot; angezeigt. &amp;lt;br&amp;gt;&lt;br /&gt;
Den JSON (JavaScriptObjectNotation) Code kann man im [http://jsonlint.com/ JSONLint Validator] prüfen. &amp;lt;br&amp;gt;&lt;br /&gt;
Die Grafikausgabe der &amp;quot;Buttons&amp;quot; kann man mit [http://netio.davideickhoff.de/renderjson NetIO JSON Renderer] vorher am PC überprüfen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== AVR Net IO ====&lt;br /&gt;
[[Datei:AVRNetIO-Screenshot1.png|160px|rechts]]&lt;br /&gt;
Update 15.12.2011: Die Neue Version 1.3 ist seit gestern im AppStore. Fehlerkorrekturen und ein robusteres Handling machen die App nun zur universellen AVR-Net-IO-Steuerung.&lt;br /&gt;
&lt;br /&gt;
Mit der [http://itunes.apple.com/de/app/avr-net-io/id460991760?mt=8 iPhone App AVR-Net-IO] kann das Board ferngesteuert werden. Die Fernsteuerung umfasst in der Version 1.1 folgende Funktionen:&amp;lt;br&amp;gt;&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;ADC-Werte zyklisch auslesen und darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Inputs zyklisch darstellen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Digital-Outputs können über Buttons geschaltet werden. Die Werte der Digital-Outputs werden zuerst ausgelesen und zeigen den zuletzt gesetzten Wert an.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Terminal-Modus: Hier können beliebige AVR-Net-IO-Befehle eingegeben werden. Das Ergebnis wird 1:1 angezeigt, wie es vom Board kommt. Hilfreich für Tests bei Eigenentwicklungen und Konfigurationen.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Mehr Infos gibt&#039;s dazu direkt von den Entwicklern auf [http://www.facebook.com/pages/AVR-Net-IO/187799687958255?ref=nf AVR-net-IO Facebook-Page] oder direkt auf deren Homepage [http://www.ondics.de/apps/1001/ Homepage].&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Hinweis: MIt der aktuellen Version 1.1 gibt es noch bei manchen Boards Probleme beim auslesen und anzeigen der ADC- und Digital-Werte. Das Terminal funzt problemlos. Die Entwickler kümmern sich gerade drum und haben einen baldigen Update versprochen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== E2000 - Logik ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:E2000-Logik-Bedienoberflaeche.jpg|500px|rechts]]&lt;br /&gt;
&lt;br /&gt;
Anwenderfreundliche Logik-Software vom Elektronik2000.de zur Steuerung des AVR-NET-IO über den Computer oder durch ein Firmware-Update auch autark.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Version 2.0a6&#039;&#039;&#039; beinhaltet eine Firmware, durch die das NET-IO mit einem Firmwareupdate in einen autarken Betrieb versetzt werden kann. Dazu ist ein ATMega644 notwendig. Durch diese Erweiterung ist es möglich, eine Logikschaltung in das Board zu schreiben, welche das Board dann simuliert. Es unterstützt außerdem ein Webinterface, eine TelNet-Schnittstelle und eine Live-View-Ansicht mit der es möglich ist, live zu sehen, wie der Status des Boards zu einem bestimmten Zeitpunkt ist. Die aktuelle Systemzeit wird aus dem Internet ermittelt.&lt;br /&gt;
&lt;br /&gt;
Durch ein Erweiterungsboard von Elektronik2000.de ist es in &#039;&#039;&#039;Zukunft&#039;&#039;&#039; möglich, das Board ohne Internet laufen zu lassen durch eine RTC (RealTimeClock). Auf dieser Erweiterung wird auch ein EEPROM integriert sein, um Firmware-Updates über Netzwerk einzuspielen, damit kein direkter Eingriff in die Hardware notwendig ist. Diese Erweiterung bietet außerdem die Anbindung an das E2000-Bus-System.&lt;br /&gt;
&lt;br /&gt;
Das Designen von Schaltaufgaben wird in diesem Programm grafisch dargestellt. Außerdem besitzt die Software (in der Version für die Pollin Firmware) einen Kennlinienschreiber und ein Webinterface.&lt;br /&gt;
&lt;br /&gt;
Team: [http://www.elektronik2000.de E2000-Team]&lt;br /&gt;
&lt;br /&gt;
== Andere Software statt der Originalsoftware von Pollin ==&lt;br /&gt;
&lt;br /&gt;
Die Umrüstung auf einen Webserver durch Austausch der Software (und ev. des ATmega32) bietet sich an. Kleiner Hinweis dabei: wenn zum Flashen ein ISP-Adapter verwendet wird, diesen unbedingt vor dem Start der neuen Software abziehen! Der ISP arbeitet nämlich über dieselbe SPI-Schnittstelle über die auch der ENC28J60 angesteuert wird. Ein eventuell noch angeschlossener, wenn auch passiver ISP-Adapter stört diese Kommunikation, d.h. das Programm an sich scheint zu laufen, aber die Ethernet-Schnittstelle funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
=== Bascom Version von Hütti ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://bascom-forum.de/index.php/topic,1781.45.html )&lt;br /&gt;
dort am Ende der Seite.&lt;br /&gt;
&lt;br /&gt;
=== Ben&#039;s Bascom Quellcode ===&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://members.home.nl/bzijlstra/software/examples/enc28j60.htm )&lt;br /&gt;
&lt;br /&gt;
Muss aber für Bascom 1.11.9.3 angepasst werden, siehe Code von Hütti !&lt;br /&gt;
&lt;br /&gt;
=== U. Radigs Webserver ===&lt;br /&gt;
&lt;br /&gt;
Angepasster Sourcecode von U.Radig: http://www.mikrocontroller.net/attachment/40027/Webserver_MEGA32.hex&lt;br /&gt;
oder selbst anpassen: &lt;br /&gt;
Ändere im File ENC28J60.H&lt;br /&gt;
 #define ENC28J60_PIN_SS    3&lt;br /&gt;
 #define ENC28J60_PIN_CS    4&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#988386)&lt;br /&gt;
&lt;br /&gt;
Temporären Dateien (*.d, *,lst,*.o) vorher im Verzeichnis löschen &#039;&#039;make clean&#039;&#039;, damit neu compiliert wird.&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.0.99&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: uli1&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den orginal SourceCode gibt&#039;s übrigens hier:http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex&lt;br /&gt;
&lt;br /&gt;
Bei den Fuses BOOTRST ausschalten, da die Software keinen Bootloader enthält.&lt;br /&gt;
&lt;br /&gt;
Wer gerne als Link-LED die grüne nutzen möchte&lt;br /&gt;
(U.Radig-Source)&lt;br /&gt;
&lt;br /&gt;
enc28j60.c Zeile 150&lt;br /&gt;
&lt;br /&gt;
 &amp;quot;enc28j60_write_phy(ENC28J60_PHY_PHLCON, 0x347A)&lt;br /&gt;
&lt;br /&gt;
Wert 0x347A in 0x374A ändern&lt;br /&gt;
&lt;br /&gt;
(Quelle: http://www.mikrocontroller.net/topic/109988#994943)&lt;br /&gt;
&lt;br /&gt;
IP: 192.168.1.90&amp;lt;br&amp;gt;&lt;br /&gt;
User: admin&amp;lt;br&amp;gt;&lt;br /&gt;
Pass: tim&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://beitz-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
Test: http://pieper-online.dyndns.org&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weiterentwicklung des Radig-Codes von RoBue: &amp;lt;br&amp;gt;&lt;br /&gt;
- 1-Wire-Unterstützung (Anschlus an PORTA7) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA0-3 digitaler Eingang (ein/aus) &amp;lt;br&amp;gt;&lt;br /&gt;
- PORTA4-6 analoger Eingang (0 - 1023) &amp;lt;br&amp;gt;&lt;br /&gt;
- LCD an PORTC &amp;lt;br&amp;gt;&lt;br /&gt;
- Schalten in Abhängigkeit von Temperatur und analogem Wert &amp;lt;br&amp;gt;&lt;br /&gt;
- (Teilweise) Administration über Weboberfläche &amp;lt;br&amp;gt;&lt;br /&gt;
- Erweiterung des cmd-Befehlsatzes für telnet/rs232 &amp;lt;br&amp;gt;&lt;br /&gt;
Gedacht ist der Einsatz des AVR-NET-IO-Bausatzes für Heizungs- oder Haussteuerung) &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test: http://avrboard.eluhost.de/&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Quelle:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/43307/AVR-NET-IO_RoBue_V1.3.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/44569/AVR-NET-IO_RoBue_V1.4.zip&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/attachment/46720/AVR-NET-IO_RoBue_1.5-final_hoffentlich_.zip)&lt;br /&gt;
&lt;br /&gt;
Bei der Ver 1.5 sind die Ports PD2+3 fürs 4bit LCD (Ext.) vertauscht ! Gruß B.P&lt;br /&gt;
&lt;br /&gt;
=== Simon Ks Webserver (uip-Stack) ===&lt;br /&gt;
Angepasster Sourcecode von Simon K: http://www.mikrocontroller.net/attachment/39939/uWebSrv.zip&lt;br /&gt;
IP: 192.168.0.93:8080&amp;lt;br&amp;gt;&lt;br /&gt;
Um diesen Code mit einem Atmega1284P verwenden zu können, muss in der main.c in Zeile 38, &amp;quot;TIMSK&amp;quot; durch &amp;quot;TIMSK1&amp;quot; ersetzt werden.&lt;br /&gt;
Die Fusebits für den Atmega1284p ohne Bootloader sind:&lt;br /&gt;
lfuse=0xFF, hfuse=0xD9, efuse=0xFF&lt;br /&gt;
&lt;br /&gt;
=== Ethersex Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.ethersex.de - Einfach für atmega32 compilieren und funktioniert.&lt;br /&gt;
&lt;br /&gt;
Oder &lt;br /&gt;
[http://www.ethersex.de/firmware-builder/list.cgi hier] ein Firmware Image passend für das Pollin Net-IO mit eingebautem Webserver und Beispieldateien im Flash bauen lassen und flashen (firmware-builder). Einfacher geht´s nimmer. :-)&lt;br /&gt;
&lt;br /&gt;
=== Etherrape Server ===&lt;br /&gt;
&lt;br /&gt;
http://www.lochraster.org/etherrape/ &lt;br /&gt;
&lt;br /&gt;
ist in jedem Fall hier auch zu erwähnen zumal es sich beim etherrape um das Ursprungsprojekt von ethersex handelt.&lt;br /&gt;
Es scheint aber bei der Weiterentwicklung wenig zu passieren.&lt;br /&gt;
Ausführliche Dokumentation findet sich unter http://wiki.lochraster.org/wiki/Etherrape&lt;br /&gt;
&lt;br /&gt;
=== Mini SRCP Server (kommerziell, Closed-Source)===&lt;br /&gt;
&lt;br /&gt;
Damit wird die Platine zu einer Modellbahnsteuerung, die&lt;br /&gt;
über das Netzwerkprotokoll SRCP mit verschiedenen Programmen&lt;br /&gt;
gesteuert werden kann.&lt;br /&gt;
&lt;br /&gt;
[http://www.7soft.de/de/mini_srcp_server/index.html Infoseite] zur Hardware&lt;br /&gt;
und das zugrundeliegende [http://www.der-moba.de/index.php/Digitalprojekt Digitalprojekt].&lt;br /&gt;
&lt;br /&gt;
=== AvrArtNode ===&lt;br /&gt;
&lt;br /&gt;
Hiermit kann die Platine zu einem Art-Net Node werden, mit dem sich ein DMX-Universe über Ethernet übertragen lässt. Basiert auf den Quellen von Ulrich Radig.&lt;br /&gt;
&lt;br /&gt;
Dokumentation: [http://www.dmxcontrol.de/wiki/Art-Net-Node_f%C3%BCr_25_Euro Art-Net-Node für 25 Euro]&lt;br /&gt;
&lt;br /&gt;
=== Webserver von G. Menke ===&lt;br /&gt;
&lt;br /&gt;
Ein Webserver (basierend auf den Sourcen von U. Radig), der so angepasst ist, dass alle Ein- und Ausgänge wie bei der originalen Pollin-Software genutzt werden können (8xDIGOUT, 4xDIGIN, 4xADIN). Der Webserver kann daher direkt auf das Net-IO geladen werden. Im ZIP-File sind ein ReadMe und alle C-Sourcen enthalten. Download:&lt;br /&gt;
[http://gm.stream-center.de/webserver/ Webserver mit passender IO]&lt;br /&gt;
&lt;br /&gt;
=== OpenMCP ===&lt;br /&gt;
&lt;br /&gt;
Tolles Projekt, welches viele Features bietet und stabil läuft. Hervorzuheben ist die Übersichtlichkeit der Programmteile/Module und die vielleicht nicht ganz komplette Dokumentation. Man merkt das viel Arbeit und Liebe in diesen Projekt steckt. Herausgekommen ist dabei eine einfach zu handhabende Entwicklungsumgebung. Anfänger können, dank des gut durchdachten CGI-System welches sich um alle wichtigen Sachen kümmert, leicht eigene CGI implementieren. Alle Ausgaben erfolgen nur mit printf über die Standardausgabe und werden automatisch richtig per Netzwerk übertragen, dadurch ist es auch für den Anfänger recht gut geeignet, da man sich nicht mit der Netzwerkprogrammierung auseinander setzen muss.&lt;br /&gt;
&lt;br /&gt;
Die Software belegt im Moment (Stand Juli 2010) ca. 55 Kb im Flash, so dass man das Board mit einem grösseren µC (z.B. ATMega644) aufrüsten muss.&lt;br /&gt;
&lt;br /&gt;
[http://wiki.neo-guerillaz.de Projekt und Doku]&lt;br /&gt;
&lt;br /&gt;
Der Autor stellt zwei über das Internet erreichbare Testboards bereit unter http://www.neo-guerillaz.de:81 und http://www.neo-guerillaz.de:82 die beide unter OpenMCP laufen, je auf einen AVR-NETIO mit einem ATmega644 und dem eigentlichen Board mit einem ATmega2561. Zusätzlich ist gerade eine Version für das myAVR in Arbeit die schon ordentlich Fortschritte macht.&lt;br /&gt;
&lt;br /&gt;
=== ENC28J60 I/O-Webserver von Thomas Heldt ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul-Webserver (Softwarekompatibel zum Pollin Webserver), der durch div. Module erweitert werden kann, Software in Bascom basierend auf dem Code von Ben Zijlsta wurde erweitert und angepasst:&lt;br /&gt;
&lt;br /&gt;
[http://mikrocontroller.heldt.eu/index.php?page=enc28j60-io-webserver Projekt und Software]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/topic/109988&lt;br /&gt;
* [http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en022889 ENC28J60 Produktseite]&lt;br /&gt;
* [http://ww1.microchip.com/downloads/en/DeviceDoc/39662c.pdf ENC28J60 Datenblatt(pdf)]&lt;br /&gt;
* [http://son.ffdf-clan.de Forum für AVR-Net-IO]&lt;br /&gt;
* [http://bascom-forum.de/index.php/topic,1781.0.html Bascom Forum ]&lt;br /&gt;
* [http://hobbyelektronik.org/w/index.php/AVR-NET-IO-Shield Shield für den NET-IO]&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Boards]]&lt;br /&gt;
[[Category:Ethernet|P]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=62354</id>
		<title>Operationsverstärker-Grundschaltungen</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Operationsverst%C3%A4rker-Grundschaltungen&amp;diff=62354"/>
		<updated>2011-12-13T09:01:16Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Spannungsfolger (Impedanzwandler) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Idealisiertes Modell eines OPV ==&lt;br /&gt;
&lt;br /&gt;
=== Anschlüsse ===&lt;br /&gt;
Ein Operationsverstärker hat zwei Eingänge (+) und (-) und einen Ausgang&lt;br /&gt;
(UA). Außerdem verfügt er über eine positive und eine negative&lt;br /&gt;
Spannungsversorgung (V+) und (V-).&lt;br /&gt;
&lt;br /&gt;
=== Spannungsversorgungen ===&lt;br /&gt;
Die Spannungsversorgungen sollen zunächst nicht interessieren. Sie&lt;br /&gt;
werden in Schaltungen oft nicht eingezeichnet. &lt;br /&gt;
In der Praxis ist es jedoch wichtig zu wissen, dass die Ausgangsspannung immer zwischen (V+) und (V-) liegt. Die Ausgangsspannung des OPV kommt schließlich  dadurch zustande, dass der Ausgang über einen Transistor mehr oder weniger hochohmig mit den beiden Versorgungsspannungen verbunden wird.&lt;br /&gt;
&lt;br /&gt;
Wenn man einen OPV also mit +5V versorgt, so kann der OPV im besten Fall am Ausgang +5V erzeugen. Man würde in diesem Fall von einem &amp;quot;Rail-to-Rail&amp;quot; Operationsverstärker sprechen.&lt;br /&gt;
Bei vielen Operationsverstärkern ist die maximal mögliche Ausgangsspannung geringer als die Versorgungsspannung. Ein mit +5V Spannungsversorgung beschalteter OPV kann dann beispielsweise nur +4V Ausgangsspannung erzeugen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    | \&lt;br /&gt;
    |  \&lt;br /&gt;
 -- | - \&lt;br /&gt;
    |    \_______&lt;br /&gt;
    |    /&lt;br /&gt;
 -- | + /&lt;br /&gt;
    |  /&lt;br /&gt;
    | /&lt;br /&gt;
&lt;br /&gt;
=== Ausgang ===&lt;br /&gt;
Der Ausgang des OPV ist eine ideale Spannungsquelle. Das bedeutet, dass die&lt;br /&gt;
Ausgangsspannung unabhängig davon ist, was ausgangsseitig an den OPV&lt;br /&gt;
angeschlossen wird.&lt;br /&gt;
In der Praxis gilt dieses Modell häufig nur bei &amp;quot;sinnvollen Anwendungen&amp;quot;. So ist beispielsweise der Ausgangsstrom des OPV nach oben begrenzt (typischerweise im mA-Bereich), und manche OPV schwingen sehr leicht, wenn man sie kapazitiv belastet.&lt;br /&gt;
&lt;br /&gt;
=== Eingänge ===&lt;br /&gt;
Die Eingänge eines OPV sind hochohmig, d. h., es handelt sich nur um &amp;quot;Messfühler&amp;quot;, die keinen Strom führen.&lt;br /&gt;
&lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
Der OPV mißt zu jeder Zeit die Differenz &amp;lt;math&amp;gt;U_D = U(+) - U(-)&amp;lt;/math&amp;gt; der&lt;br /&gt;
Eingangsspannungen.&lt;br /&gt;
&lt;br /&gt;
Ist die Spannung an (+) größer als an (-), so erhöht der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
Ist die Spannung an (+) niedriger als an (-), so vermindert der OPV die&lt;br /&gt;
Ausgangsspannung.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis dieses Vorgangs wird häufig über die Gleichung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = v \cdot U_D&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
beschrieben, wobei &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; eine sehr große Zahl (10^4...10^6) ist. &lt;br /&gt;
&lt;br /&gt;
Mit Hilfe der beschriebenen Funktionsweise lassen sich alle grundlegenden Schaltungen herleiten.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Betrachtet wird die invertierende Grundschaltung nach Abbildung a) im Abschnitt &amp;quot;Verstärkergrundschaltungen&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Für die Pfeilrichtungen der Spannungen und Ströme gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt;: von oben nach unten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt;: von links nach rechts&lt;br /&gt;
&lt;br /&gt;
Die Spannung am (+)Eingang ist gleich Null. Die Spannung am (-)Eingang wird durch die Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; und durch die im OPV befindliche und mit dem Ausgang verbundene Spannungsquelle des OPV manipuliert.&lt;br /&gt;
&lt;br /&gt;
* Ist die Spannung am (-)Eingang negativ, so erhöht der Operationsverstärker die Ausgangsspannung. Dadurch wird auch die Spannung am (-)Eingang positiver. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
* Ist die Spannung am (-)Eingang positiv, so vermindert der Operationsverstärker die Ausgangsspannung. Dadurch wird auch die Spannung am (-)Eingang negativer. Und zwar so lange, bis die Spannung am (-)Eingang gleich groß ist, wie die Spannung am (+)Eingang, also U(-)=0V.&lt;br /&gt;
&lt;br /&gt;
Der Operationsverstärker wird also die Spannungen an (+) und (-)&lt;br /&gt;
angleichen. Das passiert immer dann, wenn der Ausgang mit dem (-)Eingang&lt;br /&gt;
verbunden ist. Man nennt das Prinzip &amp;quot;Gegenkopplung&amp;quot;. Auf diese Art und&lt;br /&gt;
Weise funktionieren alle analogen OPV-Schaltungen.&lt;br /&gt;
&lt;br /&gt;
Da an (+) Massepotential anliegt, liegt an &amp;lt;math&amp;gt;R_3&amp;lt;/math&amp;gt; die Spannung &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; an, und es gilt: &lt;br /&gt;
&amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da der (-)Eingang hochohmig ist, fließt &amp;lt;math&amp;gt;I_{R3}&amp;lt;/math&amp;gt; über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; weiter zum OPV-Ausgang.&lt;br /&gt;
&lt;br /&gt;
Ua ist die Spannung vom Ausgang zur (virtuellen) Masse am (-)Eingang.&lt;br /&gt;
(--&amp;gt; Pfeil einzeichnen und klarmachen, daß es egal ist, ob der Pfeil vom&lt;br /&gt;
Ausgang zur Masse geht oder vom Ausgang &amp;quot;entgegen der Stromrichtung&amp;quot; zur&lt;br /&gt;
virtuellen Masse an (-)!)&lt;br /&gt;
&lt;br /&gt;
Mit Hilfe von &amp;lt;math&amp;gt;I_{R3}=\frac{U_e}{R_3}&amp;lt;/math&amp;gt; ergibt sich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U_a = -R_4 \cdot I_{R3} = -{{R_4} \over {R_3}} \cdot U_e.&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Energie für den Stromtransport über &amp;lt;math&amp;gt;R_4&amp;lt;/math&amp;gt; stammt vom OPV! Sobald die Ladungen ausgehend von der Spannungsquelle &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; die virtuelle Masse an (-) erreicht haben, hat &amp;lt;math&amp;gt;U_e&amp;lt;/math&amp;gt; seine gesamte Energie abgegeben.&lt;br /&gt;
&lt;br /&gt;
== Reale OPs / Kennwerte ==&lt;br /&gt;
Abweichend vom idealen OP besitzen reale OPs diverse Einschränkungen und Kennwerte, die sie für verschiedene Einsätze mehr oder weniger prädestinieren.&lt;br /&gt;
&lt;br /&gt;
=== Leerlaufverstärkung ===&lt;br /&gt;
Die Leerlaufverstärkung gibt an, wie stark sich das Ausgangssignal i.A. der Änderung eines Eingangsignals statisch ändert, bzw nach dem Einschwingen erreichen könnte, wenn es nicht durch die Betriebsgrenzen limitiert wäre.&lt;br /&gt;
&lt;br /&gt;
=== Anstiegszeit ===&lt;br /&gt;
Bestimmend für die dynamische Änderung des Signal ist wiederum die slew rate, die wesentlich das AC-Verhalten bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Frequenz-Bandbreiteprodukt ===&lt;br /&gt;
&lt;br /&gt;
=== Gleichtaktverstärkung ===&lt;br /&gt;
Infolge des inhomogenen Aufbaus der internen Verstärkerstufen werden die beiden Eingangssignale nicht exakt gleich verstärkt, was in einen Gleichanteil und einen Differenzaneil aufgeteilt werden kann. Der nicht erwünschte common mode ist dabei ein Mass für die Qualität des OP.&lt;br /&gt;
&lt;br /&gt;
== Verstärkergrundschaltungen ==&lt;br /&gt;
=== Grundbeschaltung mit Berechung ===&lt;br /&gt;
[[Bild:Op-verstaerker-a.png]] [[Bild:Op-verstaerker-b.png]]&lt;br /&gt;
&lt;br /&gt;
In a) und b) verwenden wir den OP als Verstärker und nutzen hier die Möglichkeit der Gegenkopplung, um definierte Verstärkungen zu erhalten. Wir gehen wieder davon aus, dass der OP ein ideales Bauteil ist und daher seine Leerlaufverstärkung unendlich ist. Ebenso betrachten wir den Eingangswiderstand als unendlich.&lt;br /&gt;
&lt;br /&gt;
In a) ist ein invertierender Verstärker mit einem OP dargestellt. Durch die Widerstände R3 und R4 wird die Verstärkung bestimmt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = \frac{U_a}{U_e} = -\frac{R_4}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Verhältnis der beiden Widerstände bestimmt also die Verstärkung und somit die Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -\frac{R_4}{R_3} \cdot U_e &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder auch&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = V \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das negative Vorzeichen drückt aus, dass es sich um einen invertierenden Verstärker handelt.&lt;br /&gt;
&lt;br /&gt;
Beim nichtinvertierenden Verstärker b) finden wir auch eine Rückkopplung über R6 zum invertierenden Eingang des OP. Die Verstärkung wird durch das Gegenkopplungsnetzwerk R6 und R7 exakt definiert. Hier ist:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R6}{R7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Verstärkung von 1 ist sinnvoll, wenn eingangsseitig eine Spannungsquelle mit hohem Innenwiderstand verwendet wird. Für &amp;lt;math&amp;gt;\frac{R6}{R7} \to 0&amp;lt;/math&amp;gt; heißt die Schaltung &amp;quot;Spannungsfolger&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Kleinere Werte als 1 lassen sich nicht realisieren. Die Ausgangsspannung errechnet sich also so:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: Eine Eingangsspannung von 0,5 V soll auf den Wert 5 V verstärkt werden, es ist also eine Verstärkung V von 10 benötigt. R7 ist mit 10 k&amp;amp;Omega; vorgegeben. Also ist das Verhältnis&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_7} = V - 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei einem Wert von 10 k&amp;amp;Omega; für R7 errechnet sich R6 zu&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
R6 = (V - 1) \cdot R7&lt;br /&gt;
   = (10 - 1) \cdot 10\,\mathrm{k\Omega}&lt;br /&gt;
   = 90\,\mathrm{k\Omega}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgangsspannung Ua wird also:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;&lt;br /&gt;
U_a =   U_e \cdot \left (1 + \frac{R_6}{R_7}\right )&lt;br /&gt;
   = 0,5\,\mathrm{V} \cdot \left (1 + \frac{90\,\mathrm{k\Omega}}{10\,\mathrm{k\Omega}}\right)&lt;br /&gt;
   = 5\,\mathrm{V}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spannungsfolger (Impedanzwandler) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger1.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Abart des nichtinvertierenden Verstärkers stellt der Spannungsfolger dar. Beim nichtinvertierenden Verstärker errechnet sich die Ausgangsspannung aus:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_e \cdot \left (1 + \frac{R_2}{R_1}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn wir R2 auf 0Ω (mit R1 &amp;gt; 0) oder R1 auf unendlich (mit R2 &amp;lt; ∞) ändern, erhalten wir daher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V = 1 + \frac{R_2}{R_1} = 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Spannungsfolger hat also eine Verstärkung V von 1.&lt;br /&gt;
&lt;br /&gt;
Umgezeichnet sieht die Schaltung so aus: &lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger2.png]]&lt;br /&gt;
&lt;br /&gt;
Was soll das nun? Wir nutzen die Eigenschaft, dass ein idealer OP einen unendlichen Eingangswiderstand und einen Ausgangswiderstand von 0Ω hat. Real sieht das natürlich anders aus: so liegt der Eingangswiderstand Re bei normalen OPs in der Größenordnung von 1MOhm bis &amp;lt;math&amp;gt;10^{15} \Omega&amp;lt;/math&amp;gt;, der Ausgangswiderstand Ra im Bereich 20Ω bis 1kOhm. Deshalb spricht man bei dieser Schaltung von einem Impedanzwandler. Eine solche Schaltung kann also aus einer relativ hochohmigen Spannungsquelle eine niederohmige, durch Folgeschaltungen belastbare Spannungsquelle machen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger3.png|left]]&lt;br /&gt;
&lt;br /&gt;
In dem nebenstehenden Beispiel ist eine einfache Möglichkeit zur Erzeugung einer Referenzspannung gezeigt. Es kommt eine normale Stabilisierungsschaltung mit einer Zenerdiode zur Anwendung, die aber nicht mehr die schlechten Eigenschaften der Standardbeschaltung mit lediglich Widerstand und Zenerdiode  hat. Bei einer Zenerdiode hängt die genaue Spannung davon ab, welcher Strom durch sie durchfliesst. Dieser Strom (und damit auch die Höhe der Zenerspannung) würde sich aber ändern, wenn ein Verbraucher die Zenerdiode direkt mit seinem Stromfluss belasten würde. Als Folge davon würde die Spannungslage der Zenerdiode je nach Verbraucher leicht schwanken. Durch den Spannungsfolger wird das verhindert, weil der jetzt den vom Verbraucher gezogenen Strom bereitstellt.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-spannungsfolger4.png|left]]&lt;br /&gt;
&lt;br /&gt;
Eine weitere Anwendungsmöglichkeit wäre das hochohmige Auskoppeln einer Brückenspannung. Die Brückenschaltung selbst wird durch Folgeschaltungen nicht mehr belastet, alle anderen Eigenschaften bleiben erhalten.&lt;br /&gt;
&lt;br /&gt;
{{Clear}}&lt;br /&gt;
&lt;br /&gt;
=== Der Komparator ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-komp-a.png]] [[Bild:Op-komp-b.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In der einfachsten Beschaltung des Operationsverstärkers erhält man einen Komparator. Es fällt auf, dass kein Gegenkopplungsnetzwerk vorhanden ist. Der OP arbeitet daher mit seiner vollen Leerlaufverstärkung Vo. Dies bedeutet, dass bereits eine kleine Eingangsspannung genügt, um den OP in die Begrenzung zu treiben. Das heißt, die Ausgangsspannung Ua wird annähernd die Betriebsspannung erreichen.&lt;br /&gt;
&lt;br /&gt;
Beim Komparator gibt es zwei Möglichkeiten der Beschaltung: die invertierende nach a) und die nichtinvertierende Beschaltung nach b). &lt;br /&gt;
&lt;br /&gt;
==== Berechnungsbeispiel für Schaltung b) ====&lt;br /&gt;
&lt;br /&gt;
Angenommen die Leerlaufverstärkung Vo von 40000 und eine Eingangsspannung von 0,1 Volt. Die Betriebsspannungen Vcc und Vee legen wir auf +/- 24 V fest. Damit ergibt sich theoretisch für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a =  V_0 \cdot U_e = 40000 \cdot 0,1\,\mathrm{V} = 4000\,\mathrm{V}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist natürlich ein unrealistischer Wert, da Ua nicht höher sein kann als die Betriebsspannung. Also anders ausgedrückt: Bei welcher Spannung Ue erreicht der OP seine Aussteuerungsgrenze?&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_e = V_{cc} / V_0 = 24\,\mathrm{V} / 40000 = 0,6\,\mathrm{mV}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, dass eine Spannung von 0,6 mV ausreicht um den Komparator in die Begrenzung zu treiben.&lt;br /&gt;
&lt;br /&gt;
Das gleiche gilt auch für den invertierenden Komparator, allerdings wird hier der OP in die negative Begrenzung gebracht.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = V_0 \cdot U_e&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Der Addierer (Summierverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein als invertierender Verstärker beschalteter OP lässt sich so beschalten, dass ein Summensignal aus den Eingangsspannungen gebildet wird. Um die Funktion deutlich zu machen, ist eine Betrachtung der einzelnen Ströme nötig.&lt;br /&gt;
&lt;br /&gt;
In einem invertierenden Verstärker wird sich die Ausgangsspannung immer so einstellen, dass der invertierende Eingang Massepotential hat. Die virtuelle Masse (VM) unterscheidet sich von einer &amp;quot;normalen&amp;quot; Masse dadurch, dass das Potential durch einen Regelungsvorgang zustande kommt. &lt;br /&gt;
An der virtuellen Masse (VM) gilt die Knotenpunktregel, wonach die Summe der zufließenden Ströme gleich der Summe der abfließenden Ströme ist.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_1 + I_2 = -I_3&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sofern &amp;lt;math&amp;gt;U_{e1}&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;U_{e2}&amp;lt;/math&amp;gt; bekannt sind, lässt sich die Gleichung umformen in:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{e1}}{R_1} + \frac{U_{e2}}{R_2} = -\frac{U_a}{R_3}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach Ua aufgelöst ergibt sich:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \left (U_{e1} \cdot \frac{R_3}{R_1}\right ) + \left (U_{e2} \cdot \frac{R_3}{R_2}\right ) + ... + \left (U_{en} \cdot \frac{R_3}{R_n}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das war&#039;s eigentlich schon. Einen Sonderfall gibt es, wenn die Widerstände R1 und R2 gleich sind. Dann gilt&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_1 = R_2 = R_x&amp;lt;/math&amp;gt; &lt;br /&gt;
&lt;br /&gt;
und damit&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;-U_a = \frac{R_3}{R_x} \cdot (U_{e1} + U_{e2})&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Der Subtrahierer (Differenzverstärker) ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-subtrahierer.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Subtrahierer ist die Zusammenschaltung eines invertierenden und eines nichtinvertierenden Verstärkers. Schliessen wir Punkt Ue1 nach Masse kurz und steuern Ue2 an, arbeitet die Schaltung als nichtinvertierender Verstärker. Wird Ue2 nach Masse verbunden und Ue1 angesteuert, verhält sich die Schaltung als invertierender Verstärker (R7 vorerst nicht beachten).&lt;br /&gt;
&lt;br /&gt;
Für den 1. Fall gilt:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für den 2. Fall:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der dritte Fall ist die Ansteuerung beider Eingänge:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = -U_{e1} \cdot \frac{R_6}{R_4} + U_{e_2} \cdot \left (1 + \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Schaltung ist gut für eine Erklärung, praktisch aber taugt sie nichts. Denn liegen an den Eingängen gleiche Spannungen an, ist die Ausgangsspannung nicht 0, wie eigentlich zu vermuten wäre. Deshalb ändern wir die Schaltung und fügen R7 ein. Jetzt stellt sich am Punkt + des OPs die Spannung&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{e2+} = U_{e2} \cdot \frac{R_7}{R_5 + R_7}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ein. Wenn wir das berücksichtigen, erhalten wir endlich einen richtigen Subtrahierer:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} \cdot \left (1 + \frac{R_6}{R_4}\right ) \cdot \frac{R_7}{R_5 + R_7} - \frac{R_6}{R_4} \cdot U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies gilt für alle Subtrahierer, obwohl es natürlich auch hier wieder zwei Sonderfälle gibt; nämlich a) wenn alle Gegenkopplungswiderstände gleich sind:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;R_6 = R_7 = R_4 = R_5&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann ist &lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = U_{e2} - U_{e1}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder b) wenn die Widerstandsverhältnisse gleich sind  :&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{R_6}{R_4} = \frac{R_7}{R_5}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann ergibt sich für Ua:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = \left (U_{e2} \cdot \frac{R_6}{R_4}\right ) - \left (U_{e1} \cdot \frac{R_6}{R_4}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder noch einfacher:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a = (U_{e2} - U_{e1}) \cdot \frac{R_6}{R_4}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Addierer/Subtrahierer mit unterschiedlichen Faktoren ===&lt;br /&gt;
Legt man nicht den + sondern den - Eingang des Operationsverstärkers als Bezugspunkt zur Masse mit einem Widerstand fest, übernimmt der Vorwiderstand vom - Eingang, R4 die Aufgabe von R5.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Op-addsub.png]]&lt;br /&gt;
&lt;br /&gt;
Hier die Schaltung die addieren und subtrahieren kann, mit unterschiedlichen Faktoren.&lt;br /&gt;
Sie kann verwendet werden für Aufgaben wie: Gesucht ist eine Schaltung, die aus 0...2.56 V eine Spannung von -10V...10V macht. Für dieses Beispiel wird hier die Dimensionierung durchgeführt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align: left&amp;quot; &lt;br /&gt;
|&#039;&#039;&#039;Ue1 = 5V&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, muss nur ein positiver Wert sein&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R6 = 200k&#039;&#039;&#039;&lt;br /&gt;
|Einfach festgelegt, könnte auch 100k oder 500k sein&lt;br /&gt;
|-&lt;br /&gt;
|Ue2i = 0V, &#039;&#039;&#039;Uai = -10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter momentaner &#039;&#039;Zustand 1&#039;&#039;, Ue2 = 0V ist günstig für Berechnung, Ua ist die dazupassende Ausgangsspannung&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Ue2ii = 2.56V&#039;&#039;&#039;, &#039;&#039;&#039;Uaii = 10V&#039;&#039;&#039;&lt;br /&gt;
|gewählter beliebiger &#039;&#039;Zustand 2&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Um = Ue2&lt;br /&gt;
|Gleichgewicht am Eingang&lt;br /&gt;
|-&lt;br /&gt;
|Ia + Ib = Ic&lt;br /&gt;
|In den Eingang fließt &amp;quot;kein&amp;quot; Strom&lt;br /&gt;
|-&lt;br /&gt;
|(Ua-Ue2)/R6 + (Ue1-Ue2)/R4 = Ue2/Rc&lt;br /&gt;
|Gleichung mit den Unbekannten R4 und Rc&lt;br /&gt;
|-&lt;br /&gt;
|(Uai-Ue2i)/R6 + (Ue1-Ue2i)/R4 = Ue2i/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 1&#039;&#039; eingesetzt, bildet 1. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|(Uaii-Ue2ii)/R6 + (Ue1-Ue2ii)/R4 = Ue2ii/Rc&lt;br /&gt;
|Variablen für &#039;&#039;Zustand 2&#039;&#039; eingesetzt, bildet 2. Gleichung&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;R4=-\frac{R6*U_{e1}}{U_{ai}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|2 Gleichungen mit 2 Unbekannten, Lösung durch Umformen der 1. Gleichung nach R4 und einsetzen von Ue2i=0 (freundlicherweise fällt die 2. Unbekannte dabei raus)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;R4 = 100k&#039;&#039;&#039;&lt;br /&gt;
|restliche Werte eingesetzt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;Rc=\frac{R6*U_{e1}*U_{e2ii}}{U_{aii}*U_{e1}-U_{ai}*(U_{e1}-U_{e2ii})-U_{e1}*U_{e2ii}}&amp;lt;/math&amp;gt;&lt;br /&gt;
|Ergebnis für R4 in die 2. Gleichung einsetzen und Umformen nach Rc&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Rc = 41.6k&#039;&#039;&#039;&lt;br /&gt;
|Werte eingesetzt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Der Instrumenten-Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:Instrument.png]]&lt;br /&gt;
&lt;br /&gt;
Ein Nachteil des Subtrahierer ist sein geringer Eingangswiderstand. Um den nahezu unendlichen Eingangswiderstand des verwendeten Operationsverstärkers zu erreichen, kann man einfach vor beide Eingänge je einen Impedanzwandler vorschalten.&lt;br /&gt;
Die hier beschriebene Schaltung ist um drei Widerstände erweitert und ermöglicht die Einstellung der Differenz-Verstärkung über nur einen Widerstand, nämlich R2.&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1A gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a1}-U_{e1}}{R_1}-\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am invertierenden Eingang von IC1C gilt (Knotenregel):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac{U_{a2}-U_{e2}}{R_1}+\frac{U_{e1}-U_{e2}}{R_2}=0&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Subtrahiert man die beiden Gleichungen voneinander, erhält man:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_{a2}-U_{a1}=(U_{e2}-U_{e1})\cdot\left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Letztere Differenz ist die Eingangsspannung eines normalen Subtrahierers mit der Verstärkung 1.&lt;br /&gt;
&lt;br /&gt;
Also ergibt sich als Ausgangsspannung:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;U_a=(U_{e2}-U_{e1}) \cdot \left (1+\frac{2\cdot R_1}{R_2}\right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Differenzverstärkung beträgt demnach:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;V=\left (1+\frac{2\cdot R_1}{R_2} \right )&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anwendung: Auswertung von Brückenschaltungen, wie Drucksensoren oder Dehnungsmessstreifen, die durch den Eingangswiderstand der Messschaltung nicht belastet werden dürfen.&amp;lt;br&amp;gt;&lt;br /&gt;
Instrumenten-Verstärker kann man auch fertig kaufen. Im INA102 ist die komplette Schaltung integriert. Für R2 sind 3 verschiedene Werte eingebaut, die bei passender Verschaltung eine Verstärkung von 1, 10, 100 oder 1000 ermöglichen.&lt;br /&gt;
&lt;br /&gt;
=== Der Multiplizierer (Mischer) ===&lt;br /&gt;
&lt;br /&gt;
=== Der Logarithmierer ===&lt;br /&gt;
&lt;br /&gt;
= Spannungsversorgung und Beschaltung =&lt;br /&gt;
&lt;br /&gt;
== Betrieb mit einfacher Versorgungsspannung ==&lt;br /&gt;
&lt;br /&gt;
Häufig möchte man Wechselspannung (z. B. Audiosignale) die auch negative Spannungen enthält mit einem Opamp verstärken, hat aber nur eine einfache Versorgungsspannung, eine positive in Bezug zu Masse, zur Verfügung. Dafür bieten sich folgende Schaltungen an, die in der Literatur leider häufig vernachlässigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Nichtinvertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp1.png]]&lt;br /&gt;
&lt;br /&gt;
Der negative Eingang wird mit einem Spannungsteiler auf die halbe Betriebsspannung gelegt und das Signal mit Kondensatoren am Ein- und Ausgang entkoppelt.&lt;br /&gt;
Die Verstärkung ist in diesem Beispiel für Wechselspannung 11 (Formel wie oben), für Gleichspannung aber 1, da C4 für Gleichspannung einen unendlichen Widerstand darstellt. C3 sollte dorthin gehen, wo das Eingangssignal seinen Bezugspunkt hat, also die Abschirmung der Cinch-Buchse, während R5 dorthin geht, wo der Operationsverstärker seine negative Versorgungsspannung her bekommt, falls das nicht die gleichen Potentiale, hier GND, sein sollten.&lt;br /&gt;
&lt;br /&gt;
=== Invertierender Verstärker ===&lt;br /&gt;
&lt;br /&gt;
Das Prinzip funktioniert analog auch für die invertierende Beschaltung:&lt;br /&gt;
&lt;br /&gt;
[[Bild: Ss_opamp2.png]]&lt;br /&gt;
&lt;br /&gt;
= Kaufempfehlung =&lt;br /&gt;
LM 158/258/358 2 OPs in einem Gehäuse Preis ca. 0,30€.&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Standardbauelemente#Operationsverst.C3.A4rker|Standardbauelemente - Operationsverstärker]].&lt;br /&gt;
&lt;br /&gt;
Wer Audio OpAmps sucht - tangentsoft.net hat mal welche unter die Lupe genommen: [http://www.tangentsoft.net/audio/opamps.html Notes on Audio OpAmps]&lt;br /&gt;
&lt;br /&gt;
= Weblinks =&lt;br /&gt;
*[http://www.eetkorea.com/ARTICLES/2003SEP/A/2003SEP19_AMD_AN07.PDF Op Amp Circuit Collection] - National Semiconductor Application Note 31 mit vielen weiteren OP-Schaltungen&lt;br /&gt;
*[http://www-s.ti.com/sc/psheets/slod006b/slod006b.pdf Op Amps for Everyone] - englischsprachiges, sehr umfangreiches Dokument zu OPV und deren Anwendung (empfehlenswert)&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/bau/0209092.htm Operationsverstärker im ElKo]&lt;br /&gt;
*[http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7a.pdf OP Teil 1], [http://www.inf.fu-berlin.de/lehre/WS00/peg/folien/Peg_v7b.pdf OP Teil 2] - OP-Schaltungen (deutsch)&lt;br /&gt;
*[http://www.blecken.de/download/opverst.zip Schaltungstechnik mit Operationsverstärkern] - Prof. K. Blecken, Skript zur Vorlesung (deutsch, *.doc-Format)&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/Operationsverst%C3%A4rker RN-Wissen Operationsverstärker]&lt;br /&gt;
* [http://www.national.com/onlineseminar/#amps Online Seminar] von National Semiconductor&lt;br /&gt;
* [http://www.franzis.de/elo-das-magazin/grundlagen-und-ausbildung/operationsverstaerker/der-operationsverstaerker ELO-Online-Magazin, Franzis-Verlag], [http://www.franzis.de/online-shop/elektronik/lernpakete-elektronik/lernpaket-elektronik-mit-ics Lernpaket Elektronik mit ICs] &amp;quot;Elektronische Experimente mit integriertem Schaltkreis&amp;quot;, Kasten mit Steckbrett/Bauelementen (ca. 40EUR), &lt;br /&gt;
* [[Schmitt-Trigger]]&lt;br /&gt;
* [[Aktiver RC-Bandpass|Aktiver RC-Bandpass auf Operationsverstärker-Basis]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=62209</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=62209"/>
		<updated>2011-12-05T12:48:03Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Softwarehandshake (XON/XOFF) */&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 -3V ... -12V (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;
== Die UART-Register ==&lt;br /&gt;
&lt;br /&gt;
Der 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;
| &#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 eine 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;c&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&amp;gt;&lt;br /&gt;
// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.&amp;amp;nbsp;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;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben 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;c&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;/c&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;
=== Schreiben 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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und die Anwendung in einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;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;/c&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;c&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;/c&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;C&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;/C&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;C&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;/C&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;C&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;/C&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;c&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;/c&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;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&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 - 1 ) {&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;/c&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://homepage.hispeed.ch/peterfleury/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;
&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 geringe Ü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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Aquarium-Steuerung&amp;diff=60814</id>
		<title>Aquarium-Steuerung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Aquarium-Steuerung&amp;diff=60814"/>
		<updated>2011-09-29T12:54:48Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Beschreibung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hallo zusammen,&lt;br /&gt;
&lt;br /&gt;
momentan versuche ich mich an einer Aquarium Steuerung. Als Controller fungiert ein PIC18F47J53. Dieser besitzt unter anderem 128kB Flash, 12 Bit A/D Wandler, USB. Zum einstellen der Parameter wird dieser per USB mit einem PC verbunden.&lt;br /&gt;
&lt;br /&gt;
Momentaner Stand:&lt;br /&gt;
&lt;br /&gt;
* LCD Display Ausgabe&lt;br /&gt;
* Temperaturmessung mittels DS18B20&lt;br /&gt;
* Berechnung von Sonnenauf/untergang&lt;br /&gt;
* Berechnen von Mondphase&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* PIC18F47J53 Datenblatt [http://ww1.microchip.com/downloads/en/DeviceDoc/39964B.pdf]&lt;br /&gt;
* MCP79410 Datenblatt [http://ww1.microchip.com/downloads/en/DeviceDoc/22266C.pdf]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=59004</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=59004"/>
		<updated>2011-07-27T10:02:40Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Empfangen (RX) */&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 -3V ... -12V (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;
== Die UART-Register ==&lt;br /&gt;
&lt;br /&gt;
Der 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;
| &#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;
&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 eine 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;c&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&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 Rundund. 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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&amp;gt;&lt;br /&gt;
// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.&amp;amp;nbsp;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;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben 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;c&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;/c&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;
=== Schreiben 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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und die Anwendung in einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;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;/c&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;c&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;/c&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;C&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;/C&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;C&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;/C&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;C&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;/C&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;c&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;/c&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;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&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 - 1 ) {&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;/c&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://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library] von Peter Fleury (UART (interrupt driven), Byte Buffering (circular))&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;
&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. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;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 es, dass dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Galvanische Trennung==&lt;br /&gt;
Für eine geringe Ü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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-GCC-Tutorial/Der_UART&amp;diff=59003</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=59003"/>
		<updated>2011-07-27T10:00:41Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* 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 -3V ... -12V (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;
== Die UART-Register ==&lt;br /&gt;
&lt;br /&gt;
Der 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;
| &#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;
&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 eine 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;c&amp;gt;&lt;br /&gt;
  UCSRB |= (1&amp;lt;&amp;lt;TXEN);&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&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 Rundund. 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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&amp;gt;&lt;br /&gt;
// bei neueren AVRs steht der Status in UCSRA/UCSR0A/UCSR1A, hier z.&amp;amp;nbsp;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;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Schreiben 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;c&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;/c&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;
=== Schreiben 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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und die Anwendung in einem Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;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;/c&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;c&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;/c&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;C&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;/C&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;C&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;/C&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;C&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;/C&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;c&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;
  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;
  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;
  UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
  UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&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;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&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 - 1 ) {&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;/c&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://homepage.hispeed.ch/peterfleury/avr-software.html UART-Library] von Peter Fleury (UART (interrupt driven), Byte Buffering (circular))&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;
&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. Statt dessen werden besondere ASCII-Zeichen benutzt, die der Gegenstelle signalisieren, daß Senden einzustellen bzw. wieder aufzunehmen.&amp;lt;br /&amp;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 es, dass dadurch keine direkte binäre Datenübertragung mehr möglich ist. Von den möglichen 256 Bytewerten werden ja 2 (nämlich &#039;&#039;&#039;XON&#039;&#039;&#039; und &#039;&#039;&#039;XOFF&#039;&#039;&#039;) für besondere Zwecke benutzt und fallen daher aus.&lt;br /&gt;
&lt;br /&gt;
==Galvanische Trennung==&lt;br /&gt;
Für eine geringe Ü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>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:Sms_command&amp;diff=58649</id>
		<title>Diskussion:Sms command</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:Sms_command&amp;diff=58649"/>
		<updated>2011-07-15T12:10:59Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: Die Seite wurde neu angelegt: „Das gibt es schon als Artikel hier im Forum, siehe: http://www.mikrocontroller.net/articles/Versenden_von_SMS_mittels_Mobiltelefon  Da wird auch das Steuern von G…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das gibt es schon als Artikel hier im Forum, siehe:&lt;br /&gt;
http://www.mikrocontroller.net/articles/Versenden_von_SMS_mittels_Mobiltelefon&lt;br /&gt;
&lt;br /&gt;
Da wird auch das Steuern von Geräten und das Auswerten von Nutzereingaben via SMS behandelt&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:GMSK-Modulation&amp;diff=58514</id>
		<title>Diskussion:GMSK-Modulation</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:GMSK-Modulation&amp;diff=58514"/>
		<updated>2011-07-13T07:59:04Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: Die Seite wurde neu angelegt: „Hast du zu dem Gauss Filter auch einen Frequenzgang?“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hast du zu dem Gauss Filter auch einen Frequenzgang?&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Minila_Version_MockUp&amp;diff=53745</id>
		<title>Minila Version MockUp</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Minila_Version_MockUp&amp;diff=53745"/>
		<updated>2010-12-22T12:49:16Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* FTDI */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== &amp;lt;u&amp;gt;Bilder der Platine&amp;lt;/u&amp;gt; ==&lt;br /&gt;
=== &amp;lt;u&amp;gt;Gerenderte Platine&amp;lt;/u&amp;gt; ===&lt;br /&gt;
[[Bild:MockUpMiniLALayoutTop.jpg|400px]]&lt;br /&gt;
[[Bild:MockUpMiniLALayoutBottom.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Platine der Sammelbestellung bestückt&amp;lt;/u&amp;gt; ===&lt;br /&gt;
[[Bild:MockUpMiniLALayoutTopPrototyp.jpg|400px]]&lt;br /&gt;
[[Bild:MockUpMiniLALayoutBottomPrototyp.jpg|400px]]&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;u&amp;gt;Informationen über den MiniLA&amp;lt;/u&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;CPLD&amp;lt;/u&amp;gt; ===&lt;br /&gt;
* wird der XC95288XL bleiben, da dies nur eine Veränderung und keine Neuentwicklung wird&lt;br /&gt;
* einige Signale werden an anderen Pins angeschlossen und verteilt&lt;br /&gt;
* TQFP144&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;RAM-Speicher&amp;lt;/u&amp;gt; ===&lt;br /&gt;
&amp;lt;u&amp;gt;512K*32:&amp;lt;/u&amp;gt;&lt;br /&gt;
* 512K*32 Kanäle&lt;br /&gt;
* TQFP100 0,65mm Pinabstand&lt;br /&gt;
* GS816036 &lt;br /&gt;
* [ http://de.farnell.com/gsi-technology/gs816032bgt-200/18m-synch-burst-sram-512kx32-smd/dp/1447526 Optional GS816032]&lt;br /&gt;
* [ http://www.cypress.com/?mpn=CY7C1381D-133AXC  Optional CY7C1381D-133AX]&lt;br /&gt;
* wird in der Sammelbestellung verwendet&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;1M*32:&amp;lt;/u&amp;gt;&lt;br /&gt;
* 1M*32 Kanäle&lt;br /&gt;
* TQFP100 0,65mm Pinabstand&lt;br /&gt;
* CY7C1441AV33-133AXC/CY7C1441AV33-133AXI&lt;br /&gt;
* 60eur/stk&lt;br /&gt;
* dazu muss eine Brücke zwischen TP1 am CPLD und TP2 am SRAM eingelötet werden&lt;br /&gt;
[[Bild:MockUpMiniLALayout1MBitRAM.png|200px]]&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;USB&amp;lt;/u&amp;gt; ===&lt;br /&gt;
Der FTDI FT2232D wird immer im Bus Powerd Modus betrieben und über USB versorgt.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Spannungsversorgung&amp;lt;/u&amp;gt; ===&lt;br /&gt;
* 3,3V, Versorgung über externe 5V bis 16V oder über den DCDC USB powered (Regler wird über den FTDI An/Aus geschaltet)&lt;br /&gt;
* 5V über DCDC (galvanisch getrennt) mit Spannungsregler dahinter oder DCDC überbrückt direkt über USB&lt;br /&gt;
&lt;br /&gt;
Dadurch kann der MiniLA mit einem Y-Kabel USB versorgt werden oder über ein externes Netzteil.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Galvanische Trennung&amp;lt;/u&amp;gt; ===&lt;br /&gt;
Als Zusatzoption über den ADUM4160, einem DCDC-Wandler und einem zusätzlichem Spannungsregler.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Steckverbinder&amp;lt;/u&amp;gt; ===&lt;br /&gt;
* Steckerbelegung bleibt beim alten&lt;br /&gt;
* X1-7 wird entfernt, da eh wenig Platz im CPLD ist&lt;br /&gt;
* X8 bleibt erhalten&lt;br /&gt;
* K8 wird dann auch komplett entfernt, da nicht mehr benötigt (alle Signale sind auch an K1 verfügbar)&lt;br /&gt;
* Serienwiderstände in den Datenleitungen wie gehabt&lt;br /&gt;
* Tastköpfe extern wie gehabt (siehe auch: Platinen)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;JTAG&amp;lt;/u&amp;gt; ===&lt;br /&gt;
* JTAG-Signale liegen an einem MicroMatch Steckverbinder an.&lt;br /&gt;
* Programmierung über FTDI und &amp;quot;xc3sprog&amp;quot; http://www.mikrocontroller.net/articles/MiniLA#via_USB&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Parallel Port&amp;lt;/u&amp;gt; ===&lt;br /&gt;
* nicht vorgesehen&lt;br /&gt;
* Signale liegen an einem MicroMatch Steckverbinder an&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;LEDs&amp;lt;/u&amp;gt; ===&lt;br /&gt;
Je nach Wunsch in SMD 0805 oder als 5mm LED&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Gehäuse&amp;lt;/u&amp;gt; ===&lt;br /&gt;
Passende Gehäusen:&lt;br /&gt;
* Hammond 1455L1602 (Mouser, RS, Conrad usw.)&lt;br /&gt;
* Fischer AKG 105 30 160 (RS, DK, Conrad usw.)&lt;br /&gt;
* Fischer AKG 105 34 160 (RS, DK, Conrad usw.)&lt;br /&gt;
* RFS RF-1599 (rfsupplier.com)&lt;br /&gt;
&lt;br /&gt;
Auch 3,5&amp;quot; USB/eSATA Festplatten-Geh​äuse sind bestens geeignet.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;u&amp;gt;Schaltplan und Layout&amp;lt;/u&amp;gt; ==&lt;br /&gt;
=== &amp;lt;u&amp;gt;Schaltplan&amp;lt;/u&amp;gt; ===&lt;br /&gt;
&#039;&#039;&#039;Seite 1 &amp;amp; 2&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet1.png|350px]]&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet2.png|350px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Seite 3 &amp;amp; 4&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet3.png|350px]]&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet4.png|350px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Seite 5 &amp;amp; 6&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet5.png|350px]]&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet6.png|350px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Seite 7&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:MockUpMiniLASchaltplanSheet7.png|350px]]&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Layout&amp;lt;/u&amp;gt; ===&lt;br /&gt;
&#039;&#039;&#039; Platine Mini-LA &#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
[[Bild:MockUpMiniLALayoutEagleTop.png.png|350px]]&lt;br /&gt;
[[Bild:MockUpMiniLALayoutEagleBottom.png|350px]]&lt;br /&gt;
* 2-lagig&lt;br /&gt;
* über den 5mm LEDs sind die SMD0805 LEDs angeordnet&lt;br /&gt;
* [[Datei:MiniLAVersionMockUpV1_05a.zip]]&lt;br /&gt;
* [[Datei:MiniLAVersionMockUpTeileliste.zip]]&lt;br /&gt;
&#039;&#039;&#039; &amp;lt;u&amp;gt;Platine Tastköpfe&amp;lt;/u&amp;gt; &#039;&#039;&#039;&lt;br /&gt;
* Sind über Wigbert zu beziehen. Er hat noch Tastköpfe von der ersten Sammelbestellung.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Steckverbinder auf der Platine&amp;lt;/u&amp;gt; ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Steckverbinder&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! || nicht gesteckt || gesteckt 1-2 || gesteckt 2-3&lt;br /&gt;
|-&lt;br /&gt;
| JP2  || - || Normal Modus || JTAG-Programmierung über FTDI&lt;br /&gt;
|-&lt;br /&gt;
| JP3  || - || Normal Modus || JTAG-Programmierung über FTDI&lt;br /&gt;
|-&lt;br /&gt;
| JP4  || - || Normal Modus || JTAG-Programmierung über FTDI&lt;br /&gt;
|-&lt;br /&gt;
| JP5  || - || Normal Modus || JTAG-Programmierung über FTDI&lt;br /&gt;
|-&lt;br /&gt;
| JP6  || - || - || -&lt;br /&gt;
|-&lt;br /&gt;
| JP7  || Platine &amp;quot;AUS&amp;quot; || Platine &amp;quot;AN&amp;quot; || -&lt;br /&gt;
|-&lt;br /&gt;
| JP9  || - || externe Versorgung || USB Versorgung&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
 JP6 - Spannungsversorgung für einen Externen Programmieradapter (&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;&#039;&#039;&#039;nicht Brücken!&#039;&#039;&#039;&amp;lt;/span&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;u&amp;gt;Programmierung&amp;lt;/u&amp;gt; ==&lt;br /&gt;
[[Image:MiniLAVersionMockUpFTPROG1.png|thumb|150px|right|Programmierung des FTDIs, Bild1]][[Image:MiniLAVersionMockUpFTPROG2.png|thumb|150px|right|Programmierung des FTDIs, Bild2]]&lt;br /&gt;
=== &amp;lt;u&amp;gt;FTDI&amp;lt;/u&amp;gt; ===&lt;br /&gt;
Zum Programmieren werden die [http://www.ftdichip.com/Drivers/VCP.htm FTDI-Treiber] und das Programm [http://www.ftdichip.com/Support/Utilities.htm FT_PROG] benötigt.&lt;br /&gt;
&lt;br /&gt;
*1. MiniLA über ein USB Kabel an den PC anschließen und die heruntergeladenen Treiber Installieren&lt;br /&gt;
*2. Das Programm FT_PROG starten&lt;br /&gt;
*3. Im Programm auf &amp;quot;Scan and Parse&amp;quot; drücken (1)(Bild1)&lt;br /&gt;
*4. rechte Maustaste auf den erkannten FTDI, &amp;quot;Aply Template&amp;quot; und &amp;quot;From File&amp;quot; [http://www.mikrocontroller.net/attachment/95887/MiniLA.xml diese Datei] auswählen (2)(Bild1)&lt;br /&gt;
*5. Im Programm auf &amp;quot;Program Devices&amp;quot; drücken (3)(Bild1), ein neues Fenster öffnet sich (Bild2)&lt;br /&gt;
*6. Hacken setzen (4)(Bild2)&lt;br /&gt;
*7. Auf &amp;quot;Program&amp;quot; drucken (5)(Bild2)&lt;br /&gt;
*8. MiniLA abstecken und wieder anklemmen&lt;br /&gt;
*9. Fertig&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;LibUSB&amp;lt;/u&amp;gt; ===&lt;br /&gt;
[[Image:MiniLAVersionMockUplibUSB1.png|thumb|150px|right|LibUSB einrichten, Bild1]][[Image:MiniLAVersionMockUplibUSB2.png|thumb|150px|right|LibUSB einrichten, Bild2]]&lt;br /&gt;
Zur Programmierung des CPLDs über xc3prog wird LibUSB benötigt. Dies bekommt man [http://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/ hier]. Die letzte Version anklicken und dann &amp;quot;libusb-win32-bin-x.x.x.x.zip&amp;quot; herunterladen und entpacken.&lt;br /&gt;
*1. In den Ordner &amp;quot;bin&amp;quot; und &amp;quot;x86&amp;quot; für Windows XP 32Bit wechseln&lt;br /&gt;
*2. &amp;quot;install-filter-win.exe&amp;quot; starten&lt;br /&gt;
*3. &amp;quot;Install a device filter&amp;quot; und &amp;quot;Next&amp;quot; drücken&lt;br /&gt;
*4. In der Liste auf &amp;quot;USB Serial Converter A&amp;quot; drücken (1) und danach auf Install (2) (Bild1)&lt;br /&gt;
*5. In der Liste auf &amp;quot;USB Serial Converter B&amp;quot; drücken (3) und danach noch einmal auf Install (4) (Bild2)&lt;br /&gt;
*6. Mit &amp;quot;Cancel&amp;quot; (5) Abbrechen (Bild2)&lt;br /&gt;
*7. Fertig&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;u&amp;gt;Firmware&amp;lt;/u&amp;gt; ===&lt;br /&gt;
[[Image:MiniLAVersionMockUpxc3prog.png|thumb|150px|right|Programmierung des CPLDs]]&lt;br /&gt;
Zum Programmieren wird [http://sourceforge.net/projects/xc3sprog/ xc3sprog] benötigt. [http://www.mikrocontroller.net/attachment/95662/xc3sprog.exe Hier eine angepasste Version (Mit der anderen gab es Probleme beim EPROM löschen).] &lt;br /&gt;
&lt;br /&gt;
*1. Auf dem MiniLA die Jumper JP2-JP5 in die Stellung [http://www.mikrocontroller.net/articles/Minila_Version_MockUp#Steckverbinder_auf_der_Platine 2-3] umstecken und den MiniLA anschließen. &lt;br /&gt;
*2. Kommandozeile öffnen&lt;br /&gt;
*3. In den Ordner wechseln, in der xc3prog und die jed-Datei liegt. (Die jed-Datei aus dem Archiv extrahieren. Sie ist im Ordner xilinx zu finden.)&lt;br /&gt;
*4. &amp;quot;xc3prog -c ftdi -v miniLA.jed&amp;quot; eingeben und Enter drücken.&lt;br /&gt;
*5. Der FTDI sollte programmiert werden, ähnlich dem nebenstehendem Bild.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;u&amp;gt;Hilfreiche Befehle&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
* &amp;quot;xc3prog -c ftdi -v -j&amp;quot; - Erkennung des CPLDs&lt;br /&gt;
* &amp;quot;xc3prog -c ftdi -v -e&amp;quot; - Löschen des CPLDs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;Die firmware ist nur lauffähig mit der Version des MockUp MiniLAs!&amp;lt;/span&amp;gt;&lt;br /&gt;
* [http://www.mikrocontroller.net/wikifiles/f/f5/Fw_timeanalysis_v1.8_for_MockUp_miniLA.zip Timeanalysis v1.8]&lt;br /&gt;
* [http://www.mikrocontroller.net/wikifiles/2/27/Fw_stateanalysis_v2.3_for_MockUp_miniLA.zip Stateanalysis v2.3]&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;u&amp;gt;Software&amp;lt;/u&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
* soon ...&lt;br /&gt;
&lt;br /&gt;
== Sammelbestellung ==&lt;br /&gt;
Ich nehme zurzeit die Anfragen für den MiniLA entgegen (MockUp). Wir planen mit 50 Bausätzen, davon sind bis jetzt:&lt;br /&gt;
* 50 verbindlich vergeben&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt; Es sind somit keine mehr Verfügbar!&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Die angebotenen Sachen:&amp;lt;/u&amp;gt; &lt;br /&gt;
*Z0 - Bausatz ohne galvanische Trennung (inkl. Platine) - 87EUR&lt;br /&gt;
*Z1 - Restliche Bauteile für die Galvanische Trennung mit 2W DCDC - 18EUR&lt;br /&gt;
*Z2 - Restliche Bauteile für die Galvanische Trennung mit 3W DCDC - 27EUR&lt;br /&gt;
*Z3 - Gehäuse - Hammond 1455L1602 Alu von Mouser - 13EUR&lt;br /&gt;
*Z4 - Platine(es sind nur 25 extra vorhanden zu den 50) - 10EUR&lt;br /&gt;
*Z5 - SMD Bestückung - 9EUR&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/174860#new Hauptthread auf Mikrocontroller.net]&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/MiniLA Original]&lt;br /&gt;
* [http://minila.sourceforge.net/hw/other/bg/minila_bg_sch.pdf Schaltplan der alten Version]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Projekte]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Avr_Webserver_mit_Wiznet_WIZ810MJ&amp;diff=30559</id>
		<title>Avr Webserver mit Wiznet WIZ810MJ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Avr_Webserver_mit_Wiznet_WIZ810MJ&amp;diff=30559"/>
		<updated>2008-08-27T09:26:10Z</updated>

		<summary type="html">&lt;p&gt;145.225.60.4: /* Hardware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Hardware==&lt;br /&gt;
* Atmega128&lt;br /&gt;
* 32kB SRAM&lt;br /&gt;
* WIZ810MJ Ethernetmodul&lt;br /&gt;
* FT245R&lt;br /&gt;
* CAN galvanisch getrennt&lt;br /&gt;
* SD Karte&lt;br /&gt;
* RS232&lt;br /&gt;
* Software SPI&lt;br /&gt;
&lt;br /&gt;
Alle unbelegten Pins sind auf Stiftleisten geführt und können für freie Anwendungen verwendet werden, z.B. für ein LC Display.&lt;br /&gt;
&lt;br /&gt;
==Platine==&lt;br /&gt;
[[Bild:webserver.jpg|framed]]&lt;br /&gt;
Die Platinen wurden bei Leiton gefertigt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eagle Files:&#039;&#039;&#039; [[Media:Webserver-PCB.zip|WiznetWebserverPCB]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schaltplan im PDF Format:&#039;&#039;&#039; [[Media:Webserver-Schematic.pdf|Schaltplan als PDF]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestückungsplan:&#039;&#039;&#039; [[Media:WebserverBestueckung.pdf|Bestückungsplan als PDF]]&lt;br /&gt;
&lt;br /&gt;
Hier ein Bild eines fertig aufgebauten Webservers:&lt;br /&gt;
&lt;br /&gt;
==Programmiermöglichkeiten==&lt;br /&gt;
Auf der Platine ist eine 6-polige Stiftleiste zum Programmieren vorhanden. Weiters wurde ein Bootloader geschreiben, durch den es möglich ist, den Avr mittels RS232 oder  über USB zu programmieren. Der Bootloader ist STK500 kompatibel, wodurch man einfach mit dem AVR Studio oder mit Avrdude programmieren kann.&lt;br /&gt;
&lt;br /&gt;
==Aufbau==&lt;br /&gt;
Beachtung gilt der DC Buchse. Es gibt mehrere Versionen dieser Buchse. Die Buchse besitzt 3 Anschlüsse. Besitzt der seitliche Anschluss kontakt zum hinteren Anschluss, muss dieser abgschnitten werden. Ansonsten kommt es zu einem Kurzschluss der Eingangsspannung. &lt;br /&gt;
Da sich unter dem Quarz für den Atmega128 mehrere Durchkontaktierungen befinden, muss der Quartz isoliert werden. Es gibt dafür spezielle Quarzisolierscheibchen. Ansonsten tut es auch ein Stück Kunststofffolie (zB ein Heftumschlag). &lt;br /&gt;
&lt;br /&gt;
==Software==&lt;br /&gt;
===Speichertestprogramm===&lt;br /&gt;
Um den externen SRAM testen zu können wurde ein Speichertestprogramm geschrieben. Es beschreibt den kompletten RAM in einer Schleife. Danach wird er wieder ausgelesen und die Differenz zum geschriebenen Wert berechnet. Ist das Ergebnis 0, wurde die Speicherzelle richtig beschrieben und ausglesen. Ist das Ergbnis ungleich Null, trat während des Vorganges ein Fehler auf, wird die Led1 (PG4) wird eingeschalten. Nach beenden der Speicherprüfung wird die Led2 (PG3) geschalten. Leuchtet nur eine der beiden Leds, ist das Ergebnis des Speichertests OK. Leuchten beide Leds, ergab sich während des Auslesens ein Fehler. In diesem Fall sollte man die Lötstellen des SRAMs, Latches und AVR kontrollieren. Zusätzlich werden die Informationen auch über die serielle Schnittstelle ausgegeben. Baudrate ist 115200 bps eingestellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Memtest Programm:&#039;&#039;&#039; [[Media:Memtest.zip|Memtest]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Bootloader===&lt;br /&gt;
Der Bootloader ist STK500 kompatibel, wodurch der ATmega128 direkt aus dem AVR Studio geflasht werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bootloaderroutinen mit Applikationsbeispiel zum Testen:&#039;&#039;&#039; [[Media:Webserver.zip|Bootloaderroutinen mit Applikationsbeispiel]]&lt;br /&gt;
&lt;br /&gt;
Mit dem Define &#039;COMM_MODE&#039; in der Header-Datei communication.h kann festgelegt werden, ob der Bootloader für RS232 oder USB ist. Diese Header befindet sich im Ordner Webserver_STK500_Bootloader.&lt;br /&gt;
&lt;br /&gt;
====Voreinstellungen====&lt;br /&gt;
Damit der Bootlaoder funktioniert, muss zuerst die hex Datein Webserver_STK500_Bootloader.hex im Ordner Webserver_STK500_Bootloader\defaultmit einem externen Programmiergerät über die 6-polige Stiftleiste geflasht werden. Weiters müssen die Fusebits richtig eingestellt werden. Folgendes muss eingestellt werden:&lt;br /&gt;
* ATmega103 Kompatiblitätsmodus deaktivieren&lt;br /&gt;
* JTAG und On-Chip-Debugging deaktivieren&lt;br /&gt;
* Externen Quarz aktivieren&lt;br /&gt;
* Bootresetvektor aktivieren&lt;br /&gt;
* Bootsize = 1024 words&lt;br /&gt;
&lt;br /&gt;
Die Werte für die Fuses, die die obrigen Einstellungen beinhalten, sind 0xFF (low Fuse) und 0xDC (high Fuse).&lt;br /&gt;
&lt;br /&gt;
Will man den Bootloader für USB verwenden, muss man einerseits das Define &#039;COMM_MODE&#039; in der Header-Datei communication.h für USB einstllen (Standardmäßig ist USB gewählt), andererseits muss der COM Port Treiber für den FT245 installiert werden. Diesen findet man hier http://www.ftdichip.com/Drivers/VCP.htm. Genauere Informationen zur Installation können auf der FTDI Homepage nachgelsen werden.&lt;br /&gt;
&lt;br /&gt;
====Informationen zur Nutzung====&lt;br /&gt;
Der Bootloader wurde mit dem AVR-Studio sowohl über USB als auch RS232 getestet. Probleme können auftreten, wenn der Bootlaoder für USB eingestellt ist und man USB - während man im AVR-Studio vebunden ist - absteckt. Sollte man dies doch tun, muss man zuerst den Connect-Dialog schließen, anschließend USB neu anstecken und erneut verbinden. In einer anderen Reihenfolge kann sein, dass man keine neue Verbindung aufbauen kann. Über RS232 treten diese Probleme nicht auf. Die Probleme über USB liegen möglicherweise am COM-Port Treiber von FTDI, da die Software am µC für die Auswertung der STK500 Kommandos für RS232 und USB gleich ist.&lt;br /&gt;
&lt;br /&gt;
====Applikationen flashen====&lt;br /&gt;
Im Ordner vom Bootlaoder ist auch ein Applikationsbeispiel. Dieses Projekt kann verwendet werden, um hier seine eigenen Applikationen zu schreiben. Um eine Hex-Datei zu flashen, kann einfach über RS232 oder den USB-COM-Port im AVR-Studio eine Verbindung zum Webserver aufgebaut werden und die hex geflasht werden. Wichtig ist noch, dass in einer selbst erstellten Applikation die USB-Routinen/RS232-Routinen mitkopiliert und sowohl USB als auch RS232 wie im mitgelieferten Beispiel initialisiert werden, damit der Bootlaoder funktioniert und jederzeit eine neue Applikation geflasht werden kann. Ansonsten muss der Bootloader immer wieder neu geflasht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Interface==&lt;br /&gt;
Alle Routinen für die vorhanden Interfaces des Webservers sind bereits im Applikationsbeispiel enthalten, siehe [[Avr_Webserver_mit_Wiznet_WIZ810MJ#Bootloader|Applikationsbeispiel mit Bootloaderroutinen]]).&lt;br /&gt;
&lt;br /&gt;
===External Memory Interface===&lt;br /&gt;
Da sich am Webserver mehrere Devices am XMEM-Interface befinden, musste eine Logik entwickelt werden, mit der je nach selektierter Adresse immer nur das jeweilig angesprochene Device aktiv ist.&lt;br /&gt;
Am XMEM Inteface befinden sich folgende Devices:&lt;br /&gt;
* 32kB SRAM: Adresse 0x0000-0x7FFF&lt;br /&gt;
* Wiznet Modul WIZ810MJ: Adresse 0x8000-0xFFFF&lt;br /&gt;
* USB FT245: beliebige Adresse zwischen 0xA000-0xAFFF (nicht genutzer Bereich des Wiznet Moduls)&lt;br /&gt;
&lt;br /&gt;
Die gesamte Logikschaltung mit ausführlicher Dokumentierung kann hier heruntergeladen werden: [[Media:Adresslogik_für_Webserver.pdf|Adresslogik für Webserver.pdf]]&lt;br /&gt;
&lt;br /&gt;
===USB===&lt;br /&gt;
Der Webserver hat eine USB Schnittstelle und verwendet den FT245 von FTDI Chip als USB Chip. Der FT245 ist am XMEM Interface des Webservers angeschlossen und kann in einem fix vorgegebenem Adressbereich liegen (siehe External Memory Interface).&lt;br /&gt;
&lt;br /&gt;
Für den Webserver wird der COM-Port Treiber von FTDI verwendet, der zuvor installiert werden muss (siehe auch [[Avr_Webserver_mit_Wiznet_WIZ810MJ#Voreinstellungen|Bootloader Voreinstellungen]]).&lt;br /&gt;
&lt;br /&gt;
====Übertragungsprotokoll====&lt;br /&gt;
Das Übertragungsprotokoll war nötig und an den STK500 Bootloader gebunden, damit es auch möglich ist, von der Applikation in den Bootlaoder zu springen. Die Übertragung ist folgendermaßen aufgebaut:&lt;br /&gt;
* START&lt;br /&gt;
** 0x1B reserviert für Daten aus AVR Studio&lt;br /&gt;
**anderes 8 bit Startbyte, das ungleich 0x1B ist, kann für USER Daten verwendet werden&lt;br /&gt;
* Sequence Number&lt;br /&gt;
** 8 bit breit&lt;br /&gt;
* Message Size 1&lt;br /&gt;
** MSB der Message Size (Anazhl der Datenbytes)&lt;br /&gt;
* Message Size 2&lt;br /&gt;
** LSB der Message Size (Anzahl der Datenbytes)&lt;br /&gt;
* Datenblock&lt;br /&gt;
** Messagesize (16bit) Datenbytes zu je 8bit&lt;br /&gt;
* Checksumme&lt;br /&gt;
** XOR aus allen Bytes einer Message (START, SeqNum, MsgSize1, MsgSize2, DATA)&lt;br /&gt;
&lt;br /&gt;
Wird als START Byte ein 0x1B erkannt, dann wird die Funktion collectCompleteMessage() aus der Header Datei BootlaoderApplication.h aufgerufen, die nichts anderes macht, als zu warten, bis eine vollständige Message empfangen wurde, diese anschließend in der .noinit section speichert und daraufhin einen Reset mit dem Watchdog generiert, damit der Webserver in den Bootlaoder springt. Dort wird dann erkannt, dass ein Watchdog Reset ausgelöst wurde und neue Daten in der .noinit section für den Bootloader sind. Diese werden daraufhin ausgelesen und verarbeitet.&lt;br /&gt;
&lt;br /&gt;
====Probleme an FT245 Pins TXE und RXF====&lt;br /&gt;
Beim Testen der Platine und des FT245 hat sich herausgestellt, dass nach dem Anstecken der Stromversorgung oder nach dem Anstecken von USB am PC einige high-low Pulse an TXE und RXF auftreten. Diese waren insofern ein Problem als die Datenauswertung in der Hauptapplikation (nicht im Bootloader) interruptgesteuert erfolgt und ein Übertragungsprotokoll (für STK500 Bootloader nötig) verwendet wird. Diese Pulse wurden nun per Software kompensiert, dafür war jedoch der Timer0 notwendig. Der Timer0 kann somit nur mehr bedingt für eigene Anwendungen verwendet werden, empfohlen wird jedoch, diesen Timer nicht zu verwenden.&lt;br /&gt;
&lt;br /&gt;
===UART RS232===&lt;br /&gt;
Auf der Webserver Platine befindet sich ein MAX3232 für die Pegelwandlung. Für RS232 wurden die Routinen aus der AVRlib verwendet (sowohl uart.h/uart.c als auch rprintf.h/rprintf.c). Damit man den Bootloader für RS232 verwenden kann, war es jedoch notwendig, die Receive Interruptroutine in der uart.c für den Webserver anzupassen. Für RS232 wird nun dasselbe Übertragungsprotokoll wie für USB verwendet. Siehe [[Avr_Webserver_mit_Wiznet_WIZ810MJ#.C3.9Cbertragungsprotokoll|USB Übertragungsprotokoll]]&lt;br /&gt;
&lt;br /&gt;
===CAN===&lt;br /&gt;
* Hardware SPI&lt;br /&gt;
* MCP2515&lt;br /&gt;
* CAN Transceiver&lt;br /&gt;
* Galvanische Trennung mit DC-DC Wandler und Optokoppler&lt;br /&gt;
* Latch, für SD nötig&lt;br /&gt;
&lt;br /&gt;
===SD-Karte===&lt;br /&gt;
[[Bild:SdKarteFehler.png|top|framed|Diese beiden Leiterbahnen müssen aufgetrennt werden]]&lt;br /&gt;
* Hardware-SPI&lt;br /&gt;
* Latch, damit CS auf low bleibt&lt;br /&gt;
&lt;br /&gt;
Leider hat sich ein Fehler im Schaltplan eingeschlichen. Zwei Netze haben den gleichen Namen und haben somit eine Verbindung untereinander. Dieser Fehler wurde aber beim Routen nicht erkannt und somit gibt es hier eine Verbindung. Nach neun Stunden Debugging wurde der Fehler gefunden. Damit die Ansteuerung der SD-Karte aber funktioniert, müssen zwei Leiterbahnen aufgetrennt werden. Im Bild links sind die notwendigen Schnitte orange und gelb eingezeichnet. Für das Verhalten der Schaltung hat diese Leitung (Datenausgang der SD-Karte) keinen Einfluss. Da die SD-Karte intern Pullup-Widerstände besitzt, hat SD-Karte auch bei offenem CS-Signal einen definierten Pegel.&lt;br /&gt;
&lt;br /&gt;
Für die FAT-Implementation wird eine vorgefertigte Bibliothek von [http://www.holger-klabunde.de/ Holger Klabunde] verwendet. Es wurde ein einfacher Schreib- und Lesetest für den Webserver implementiert. Das Programm liest eine Datei ein und gibt sie über die serielle Schnittstelle aus. Weiters schreibt das Programm im Root-Verzeichnis der SD-Karte eine Datei. Danach wird ein neues Verzeichnis angelegt und darin eine weitere Datei geschrieben. Die Debug-Ausgaben erfolgen über die serielle Schnittstelle mit einer Bitrate von 115000bps.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SD-Karten-Beispiel:&#039;&#039;&#039; [[Media:WebserverSDBsp.zip|Schreib- und Lesetest]]&lt;br /&gt;
&lt;br /&gt;
===Software SPI===&lt;br /&gt;
Da Hardware SPI des ATmega128 bereits für CAN und SD Karte verwendet wurden, wurde am Webserver auch ein softwaremäßiges SPI Interface eingebunden. Für die Software SPI Pins ist eine eigene 6-polige Stiftleiste auf der Platine vorgesehen.&lt;br /&gt;
Die Routinen sind in Assembler geschrieben und erzeugen einen SCK Takt von F_CPU/32. Die Assemblerfunktionen steuern nur SSMISO, SSMOSI und SSCK. Die Chipselect Leitung muss vom User vor Funktionsaufruf gesteuert bzw. initialisert werden.&lt;br /&gt;
&lt;br /&gt;
===Ethernet===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Projekte|A]]&lt;br /&gt;
[[Category:AVR|A]]&lt;br /&gt;
[[Category:Ethernet|A]]&lt;/div&gt;</summary>
		<author><name>145.225.60.4</name></author>
	</entry>
</feed>