<?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=84.58.239.213</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=84.58.239.213"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/84.58.239.213"/>
	<updated>2026-04-10T21:37:02Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Vorlage_f%C3%BCr_Projektbeschreibung&amp;diff=39380</id>
		<title>Vorlage für Projektbeschreibung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Vorlage_f%C3%BCr_Projektbeschreibung&amp;diff=39380"/>
		<updated>2009-09-25T15:15:06Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* Siehe auch */ Tipp zu den Kategorien&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von NAME&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Hier steht eine Beschreibung des Projekts. Eine Beschreibung besteht aus dem Titel und einer Liste von Features. &lt;br /&gt;
&lt;br /&gt;
Eine Liste von Features:&lt;br /&gt;
&lt;br /&gt;
* Feature 1&lt;br /&gt;
* Feature 2&lt;br /&gt;
* Feature 3&lt;br /&gt;
&lt;br /&gt;
Es ist sinnvoll, dem Projekt einen vernünftigen Namen zu geben. Viele der Projekte und Artikel haben das &#039;AVR&#039; als Anfang im Namen stehen. Das erscheint sinnvoll, ist es aber nicht, denn in der alphabetischen Sortierung stehen diese alle unter &#039;A&#039;. Besser wäre es, einen Namen zu vergeben, der mit dem Zweck des Projektes übereinstimmt. Also:&lt;br /&gt;
&lt;br /&gt;
Nicht &#039;AVR-Drehgeberdecoder&#039;      sondern&lt;br /&gt;
&#039;Gray-Code decoder für Drehgeber&#039;&lt;br /&gt;
&lt;br /&gt;
Bei Software, die nur auf dem AVR läuft, kann das &#039;AVR&#039; als Suchtag verwendet werden. Falls die Software so geschrieben ist, dass sie auf mehreren verschiedenen CPUs läuft, soll keine CPU im Titel genannt werden.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Beschreibung der Software&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Sourcecode: http://www.mikrocontroller.net/attachment.php/123/Sourcecode.zip&lt;br /&gt;
* Schaltplan: http://www.mikrocontroller.net/attachment.php/1234/Schaltplan.pdf&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* Diskussion zu diesem Projekt: http://www.mikrocontroller.net/forum/read-4-123.html&lt;br /&gt;
* [[Audio-Projekt|Link zu anderem Projekt]]&lt;br /&gt;
&lt;br /&gt;
Am eine des Artikels erfolgt eine Einsortierung in Artikel-Schublade(n), auch Kategorie(n) genannt. Damit ist Dein Artikel leichter auffindbar. &lt;br /&gt;
&lt;br /&gt;
Such aus den vorhandenen [[Spezial:Kategorien|Kategorien]] diejenige(n) aus, die am besten auf den Artikel passen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- &lt;br /&gt;
Bitte nicht blind die folgenden Kategorien kopieren ;-)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte| ]]&lt;br /&gt;
[[Category:Projekte| ]]&lt;br /&gt;
[[Kategorie:Tipps für Autoren]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Uhr&amp;diff=39379</id>
		<title>Uhr</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Uhr&amp;diff=39379"/>
		<updated>2009-09-25T15:07:29Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: --&amp;gt; Kategorie:AVR-Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Dieser kleine Code zeigt die beispielhafte Implementierung einer Siebensegment Uhr.&lt;br /&gt;
&lt;br /&gt;
==Version 1.0==&lt;br /&gt;
===Eckdaten===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Eckdaten&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft || Wert&lt;br /&gt;
|-&lt;br /&gt;
| Controller  || AVR ATTiny2313&lt;br /&gt;
|-&lt;br /&gt;
| Spannung || 5V&lt;br /&gt;
|-&lt;br /&gt;
| Quarz || erforderlich; 8MHz&lt;br /&gt;
|-&lt;br /&gt;
| Flashspeicherbelegung || 746Bytes (36,4%)&lt;br /&gt;
|-&lt;br /&gt;
| SRAM-Belegung || 18Bytes (14,1%)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Code===&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
Belegungen:&lt;br /&gt;
PB0	B		PD0	1er, 	PNP-Transistor&lt;br /&gt;
PB1	C		PD1	10er, 	PNP-Transistor&lt;br /&gt;
PB2	D		PD2	100er, 	PNP-Transistor&lt;br /&gt;
PB3	E		PD3	1000er, PNP-Transistor&lt;br /&gt;
PB4	F		PD4	-&lt;br /&gt;
PB5	G		PD5	Schalter, interne Pullups sind an&lt;br /&gt;
PB6	DP		PD6	-&lt;br /&gt;
PB7	A&lt;br /&gt;
 &lt;br /&gt;
Takt:&lt;br /&gt;
8MHZ Quarz&lt;br /&gt;
 &lt;br /&gt;
Siebensegmentanzeige:&lt;br /&gt;
Gemeinsame Anode&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#include 	&amp;lt;avr/io.h&amp;gt;			//Grundfunktionen&lt;br /&gt;
#ifndef 	F_CPU				//Vordefinieren für delay.h&lt;br /&gt;
#define 	F_CPU 8000000UL		        //Definition von F_CPU in Herz&lt;br /&gt;
#endif			&lt;br /&gt;
#include 	&amp;lt;util/delay.h&amp;gt;		        //Warteschleifen&lt;br /&gt;
#include        &amp;lt;avr/interrupt.h&amp;gt;		//Interruptheader&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
//Konstanten&lt;br /&gt;
//Hier kann man den Code an seine Siebensegment Anzeige anpassen&lt;br /&gt;
const unsigned char Zahl[10] = {0b01100000, 0b11111100, 0b01010010, 0b01011000, 0b11001100, 0b01001001, 0b01000001, 0b01111100, 0b01000000, 0b01001000};&lt;br /&gt;
//Globale Variablen&lt;br /&gt;
volatile unsigned long int Sekunden=0;	        //32Bit Variable; 24Bit würdens auch tun&lt;br /&gt;
unsigned char Tausender;&lt;br /&gt;
unsigned char Hunderter; &lt;br /&gt;
unsigned char Zehner;&lt;br /&gt;
unsigned char Einer;&lt;br /&gt;
 &lt;br /&gt;
ISR(TIMER1_COMPA_vect)			       //Bei Compare-Match mit OCR1A (31250) wird Sekunden um eins erhöht.&lt;br /&gt;
{&lt;br /&gt;
Sekunden++;&lt;br /&gt;
if (Sekunden &amp;gt;= 86400)			       //Wenn 24h vorbei sind, wird wieder auf 0 gesetzt&lt;br /&gt;
	Sekunden = 0;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
void Berechnung (void)			       //Sekunden zu BCD&lt;br /&gt;
{&lt;br /&gt;
Tausender = (Sekunden / 3600) / 10;&lt;br /&gt;
Hunderter = (Sekunden / 3600) - Tausender*10;&lt;br /&gt;
Zehner    = ((Sekunden % 3600) / 60) / 10;&lt;br /&gt;
Einer     = ((Sekunden % 3600) / 60) - Zehner*10;&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void Einstellen (void)			      //Dient dem Einstellen der Uhr&lt;br /&gt;
{&lt;br /&gt;
if ( !(PIND &amp;amp; (1&amp;lt;&amp;lt;PIND5)) )		&lt;br /&gt;
	Sekunden = Sekunden + 60;	&lt;br /&gt;
}		&lt;br /&gt;
 &lt;br /&gt;
void Anzeige (void)			     //Zeigt die BCD Werte an, Eine 4fach Siebensegmentanzeige wird gemultiplexed&lt;br /&gt;
{&lt;br /&gt;
unsigned int i;				     //Variable für die Schleife&lt;br /&gt;
for (i=10; i&amp;gt;0; i--)			     //10*12ms=120ms Schleifenlaufzeit&lt;br /&gt;
	{&lt;br /&gt;
	PORTD=0b01110111;		     //PD3 an --- Tausenderstelle&lt;br /&gt;
	PORTB=Zahl[Tausender];&lt;br /&gt;
	_delay_ms(3);&lt;br /&gt;
 &lt;br /&gt;
	PORTD=0b01111011;		     //PD2 an --- Hunderterstelle&lt;br /&gt;
	PORTB=Zahl[Hunderter];&lt;br /&gt;
	PORTB &amp;amp;= ~((1 &amp;lt;&amp;lt; PB6)); 	     //löscht Bit 6 in PORTB, dadurch wird der Punkt zwischen den Stunden und Minuten angezeigt&lt;br /&gt;
	_delay_ms(3);&lt;br /&gt;
 &lt;br /&gt;
	PORTD=0b01111101;		     //PD1 an --- Zehnerstelle&lt;br /&gt;
	PORTB=Zahl[Zehner];&lt;br /&gt;
	_delay_ms(3);&lt;br /&gt;
 &lt;br /&gt;
	PORTD=0b01111110;		     //PD0 an --- Einerstelle&lt;br /&gt;
	PORTB=Zahl[Einer];&lt;br /&gt;
	_delay_ms(3);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
int main (void)&lt;br /&gt;
{&lt;br /&gt;
//Registerinitialisierung&lt;br /&gt;
TCCR1A = 0b00000000;	                    //Timer im CTC-Mode; Prescaler 256; CPU-Clock&lt;br /&gt;
TCCR1B = 0b00001100;	                    //Timer im CTC-Mode; Prescaler 256; CPU-Clock&lt;br /&gt;
TCCR1C = 0b00000000;	                    //Timer im CTC-Mode; Prescaler 256; CPU-Clock&lt;br /&gt;
TIMSK  = 0b01000000;	                    //Interrupt bei Compare Match&lt;br /&gt;
OCR1A  = 31249;  	                    //genau eine Sekunde; Comparematch-Register&lt;br /&gt;
DDRB   = 0b11111111;	                    //Alles Ausgang; Siebensegmentanzeige&lt;br /&gt;
DDRD   = 0b11011111;	                    //PD5 ist Eingang&lt;br /&gt;
sei();			                    //Interrupts an&lt;br /&gt;
//Hauptschleife&lt;br /&gt;
while(1) &lt;br /&gt;
{&lt;br /&gt;
Berechnung();&lt;br /&gt;
Anzeige();&lt;br /&gt;
Einstellen();&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Category:Timer und Uhren]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Projekt_Netzteiltester&amp;diff=39376</id>
		<title>Projekt Netzteiltester</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Projekt_Netzteiltester&amp;diff=39376"/>
		<updated>2009-09-25T15:04:43Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* Weblinks */ --&amp;gt; Kategorie:AVR-Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Pstester_img_1.jpg|thumb|Der Netzteiltester in Aktion]]&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* Über RS232 vom PC aus frei einstellbare Pulsfolgen, die mit 1kHz Wiederholrate ausgegeben werden&lt;br /&gt;
* Auf Wunsch Speicherung der Folgen-Definition im [[EEPROM]] des Controllers&lt;br /&gt;
* Erzeugung eines Trigger-Impulses für das Oszilloskop (externe Triggerung)&lt;br /&gt;
* Verwendung eines leistungsfähigen HEXFETs von International Rectifier, deshalb sollten Ströme bis 20A kein Problem sein (habe es noch nicht selbst ausprobiert, bei 3A war jedenfalls keine Erwärmung des FETs spürbar)&lt;br /&gt;
* 0.1 Ohm Keramik-Shuntwiderstand zur Strommessung&lt;br /&gt;
* Platine kleiner als halbe Eurokarte (d.h. kleiner als 80x100mm), trotzdem keine SMD Parts&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
* [[AVR|ATMega8]] mit 16MHz Quarz&lt;br /&gt;
* [[RS-232]] Schnittstelle für die Kommunikation mit PC&lt;br /&gt;
* IRF 1404 HEXFET als Leistungstransistor&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
bisher sind keine Probleme bekannt :-)&lt;br /&gt;
&lt;br /&gt;
== Kontakt und Diskussion ==&lt;br /&gt;
Natürlich freue ich mich auf jeden Kommentar und auf jede Anregung (Flames wandern trotzdem in den Mülleimer :)&lt;br /&gt;
&lt;br /&gt;
In Bälde mehr ...&lt;br /&gt;
&lt;br /&gt;
--[[Benutzer:Cl|Cl]] 15:36, 14. Mär 2005 (CET)&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronik-kompendium.de/public/schaerer/pstst1.htm Netzteiltester von Thomas Schaerer] inclusive ausführlicher Erklärung&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Kategorie:Spannungsversorgung und Energiequellen]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Ambilight_in_Hardware&amp;diff=39375</id>
		<title>Ambilight in Hardware</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Ambilight_in_Hardware&amp;diff=39375"/>
		<updated>2009-09-25T15:03:42Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* Downloads */ --&amp;gt; Kategorie:AVR-Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:1. Wettbewerb]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;von Martin Antoni&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Dieses Projekt ist ein Nachbau des bekannten AmbiLights von Philips. &lt;br /&gt;
Ambilight steht für Umgebungslicht und soll den Bildschirm optisch vergrößern, indem es die Bildfarben an die Wand projeziert.&lt;br /&gt;
Mit Hilfe eines kleinen Mikrocontrollers habe ich die Bildinhalte ausgewertet und per [[PWM]] an RGB-[[LED]]s geschickt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Features&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* günstiger MC ATMega48&lt;br /&gt;
* kein Computer notwendig&lt;br /&gt;
* 8*8 Matrix Bildauswertung&lt;br /&gt;
* 128 Farbstufen (noch erweiterbar)&lt;br /&gt;
&lt;br /&gt;
== Hintergrund ==&lt;br /&gt;
&lt;br /&gt;
Im Internet habe ich schon einige Varianten gefunden, welche z.B. mit einer Software den Bildinhalt auswerten und dann an einen Mikrocontroller mit RGB-LED schicken. &lt;br /&gt;
Dabei ist das Problem der Verzögerung und man braucht einen Computer zum Auswerten.&lt;br /&gt;
Bei diesem Projekt kann direkt ein S-Video-Signal oder ein FBAS-Signal (siehe Ausblick) angeschlossen werden.&lt;br /&gt;
&lt;br /&gt;
== Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FBAS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das FBAS-Signal setzt sich aus dem Schwarz-Weiß-Signal (BAS) und dem Farbsignal zusammen. Das Farbsignal ist auf dem Schwarz-Weiß-Signal aufmoduliert.&lt;br /&gt;
&lt;br /&gt;
Ein Bild ist in 625 Zeilen aufgeteilt, jede Zeile ist 64us lang. Die Übertragung geschieht in Halbbildern. Zuerst werden die ungeraden Zeilen(1, 3, 5, 7, ...) übertragen, im Anschluss kommen die geraden Zeilen (2, 4, 6, ...). &lt;br /&gt;
Zusätzlich zu den Bildzeilen sind noch Synchronsignal im BAS-Signal moduliert. Eine genaue Beschreibung lässt sich auf folgenden Seiten finden:&lt;br /&gt;
&lt;br /&gt;
[http://de.wikipedia.org/wiki/Fernsehsignal Wikipedia Fernsehsignal]&lt;br /&gt;
&lt;br /&gt;
[http://www.elektroniktutor.de/techno/pal_ffs.html Fernsehtechnik]&lt;br /&gt;
&lt;br /&gt;
Das Farbsignal kann man unterteilen in Farbart und Farbsättigung. Diese beiden Informationen sind Quadraturamplitudenmoduliert. In der Phase steckt die Farbart und in der Amplitude die Farbsättigung.&lt;br /&gt;
Dieses modulierte Signal ist bei einer Frequenz von 4,433MHz auf das BAS-Signal moduliert. Mit einem [[Filter|Tiefpassfilter]] lässt sich das BAS-Signal herausfiltern, mit einem Hoch- oder Bandpass lässt sich das Farbsignal herausfiltern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;YUV&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
YUV ist ein Farbraum, mit einem Helligkeitssignal (Y) und zwei Farbsignalen (U + V).&lt;br /&gt;
Dieser wird bei FBAS verwendet, das Y-Signal wird als BAS-Signal übertragen und die U- und V-Signale werden QAM auf das BAS-Signal moduliert als FBAS.&lt;br /&gt;
&lt;br /&gt;
[http://de.wikipedia.org/wiki/YUV YUV auf Wikipedia]&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Die Hardware kann in logische Blöcke aufgeteilt werden.&lt;br /&gt;
&lt;br /&gt;
* Integrator&lt;br /&gt;
* Demodulator&lt;br /&gt;
* Auswertung&lt;br /&gt;
* Farbhilfsträger&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Integrator&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Ich habe das Bild in 64 Felder unterteilt, sozusagen eine 8*8-Matrix, für jedes Feld steht der RGB-Wert zur Verfügung. Damit können Lichter an verschiedenen Positionen hinter dem Fernseher angesteuert werden. Es müssen nur die richtigen Felder ausgewertet werden. Z.B. Feld 0 - 7 für die obere Bildkante.&lt;br /&gt;
&lt;br /&gt;
Jede Zeile ist in 8 Felder unterteilt.&lt;br /&gt;
Ich integriere jeweils über 1/8 der Zeile, der Endwert wird dann mit einer Sample-&amp;amp;-Hold-Schaltung so lange gehalten, bis der AD-Wandler fertig ist. In der nächsten Zeile wird dann das nächste Achtel integriert. Nach 8 Zeilen habe ich somit eine komplette Zeile analysiert. &lt;br /&gt;
&lt;br /&gt;
[[Bild:Integrator.png]]&lt;br /&gt;
&lt;br /&gt;
Insgesamt werden 512 Zeilen pro Bild analysiert, 8 Zeilen braucht man um eine komplette Zeile zu analysieren, das macht dann 64 wirkliche Bildzeilen. Da eine 8*8-Matrix verwendet wird, addiere ich immer 8 Zeilen. Jedes Feld besteht aus 8 Bildzeilenabschnitten aufaddiert.&lt;br /&gt;
&lt;br /&gt;
Für die Integratorschaltung verwende ich eine abgewandelte Negativ-Impedanz-Schaltung. Diese, mit einem RC-Glied voran, bildet einen nichtinvertierdenden Integrator.&lt;br /&gt;
&lt;br /&gt;
Ich verwende 3 Integratoren, für das Helligkeitssignal Y, das U- und das V-Signal.&lt;br /&gt;
&lt;br /&gt;
Der Eingang jedes Integrators ist mit einem Mosfet (BS170) gegen Masse versehen. Wenn dieser Mosfet einschaltet, ist der Eingang auf Masse gelegt und somit wird eine Spannung von 0V integriert. 0 Integriert ergibt 0, somit ist das eine Sample-&amp;amp;-Hold-Schaltung.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Demodulator&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Um aus dem Quadraturamplitudenmodulierten Signal wieder das U- und das V-Signal zuerhalten, verwende ich einen Produkt-Detektor. Das Hauptelement ist ein Balanced Modulator (MC1496). Im Datenblatt ist auf Seite 10 ist die Schaltung zu sehen. &lt;br /&gt;
Ich habe die Schaltung übernommen und hab die Werte der Bauelement angepasst. An den Ausgang habe ich einen Differenzverstärker mit einem Verstärkungsfaktor 2 und einer Offsetspanung von 2,5V gebaut. &lt;br /&gt;
&lt;br /&gt;
Für das U-Signal brauche ich 0° Phasenverschiebung vom Referenzoszillator und für das V-Signal brauche ich 90° Phasenverschiebung. Beide Sinussignale kommen vom Referenzoszillator (siehe Farbhilfsträger).&lt;br /&gt;
&lt;br /&gt;
Die Ausgänge der Modulatoren gehen an die Eingänge der Integratoren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auswertung&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Herz der Schaltung ist der ATMega48. An PORTC hängen die ADC-Ports.&lt;br /&gt;
&lt;br /&gt;
* PC0 - U-Integrierer&lt;br /&gt;
* PC1 - V-Integrierer&lt;br /&gt;
* PC2 - Y-Integrierer&lt;br /&gt;
&lt;br /&gt;
* PC3 - Sample-&amp;amp;-Hold U&lt;br /&gt;
* PC4 - Sample-&amp;amp;-Hold V&lt;br /&gt;
* PC5 - Sample-&amp;amp;-Hold Y&lt;br /&gt;
&lt;br /&gt;
Im Kapitel Software ist beschrieben, wie die Ports angesteuert werden müssen, um die Signale zu integrieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Farbhilfsträger&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für eine korrekte Demodulation des Quadratursignales muss ein Referenzsignal in korrekter Phase und Frequenz zur Verfügung stehen.&lt;br /&gt;
&lt;br /&gt;
Der Farbhilfsträger wird von einem Quarzoszillator erzeugt, welcher über eine PLL an die Farbfrequenz des Fernsehsignales angepasst wird.&lt;br /&gt;
&lt;br /&gt;
Der [[Oszillator]] ist ein Colpitts-Quarzoszillator. Um den Oszillator abzustimmen, wird normalerweise eine Kapazitätsdiode verwendet. In meiner Schaltung verwende ich jedoch eine 1N4001. Diese hat eine geringere Kapazitätsänderung, reicht aber völlig aus. Je nach Sperrspannung verändert sich die Diffusionskapazität. Damit wird der Quarz gezogen.&lt;br /&gt;
&lt;br /&gt;
Der Abgleich findet mit dem Burst statt. Der Burst ist ein Paket von Sinusschwingungen am Anfang (fast) jeder Zeile. Die Phase des Burst wird bei jeder Zeile um 90° hin- und hergeschaltet. Somit verwende ich nur jeden zweiten Burst, um den Referenzoszillator abzustimmen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgangsspannung des Colpitts-Oszillators ist mit Oberwellen übersäht. Um eine reine Sinusschwingung zu erzeugen, habe ich ein LC-Tiefpassfilter mit eingebaut.&lt;br /&gt;
&lt;br /&gt;
Wie schon im Kapitel Demodulator erwähnt, braucht man zwei um 90° phasenverschobene Signale. Ich nutze den Fakt, dass der Burst immer um 45° phasenverschoben ist.&lt;br /&gt;
Ein zweites Tiefpassfilter erzeugt nochmal 45° Phasenverschiebung, somit habe ich 90°.&lt;br /&gt;
Für die 0° Phasenverschiebung habe ich ein Hochpassfilter eingebaut. Dieses Filter erzeugt -45° Phasenverschiebung. Damit werden die 45° des Bursts kompensiert.&lt;br /&gt;
Am Ausgang kommt jeweils ein Emitterfolger, um das Tief- bzw. Hochpassfilter nicht zu belasten.&lt;br /&gt;
&lt;br /&gt;
Dieses Bild zeigt die um 45° verschobenden Sinen. &lt;br /&gt;
&lt;br /&gt;
[[Bild:Oszillator.png]]&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
Die Software ist komplett in Assembler geschrieben. Nur so habe ich die komplette Kontrolle über das Timing.&lt;br /&gt;
&lt;br /&gt;
Die Hauptaufgabe wird in Interrupts abgearbeitet, welche durch Timer ausglöst werden.&lt;br /&gt;
Die Berechnung der RGB-Signale erfolgt in der Main Loop.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;H-Sync und V-Sync&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Bild:frame_odd.png]]&lt;br /&gt;
[[Bild:frame_even.png]]&lt;br /&gt;
&lt;br /&gt;
Die beiden Bilder zeigen die Sync-Signale des BAS-Signales bei Zeile 1 und Zeile 313. Bei Zeile 1 fängt ein Even Frame an, bei Zeile 313 fängt ein Odd Frame an.&lt;br /&gt;
&lt;br /&gt;
Um die Sync-Signale auszuwerten ist am Pin PD.2 der Komparator angeschlossen, welcher ein High-Signal erzeugt, sobald das BAS-Signal unter 0,1V geht. Am Pin PD.2 ist außerdem der Pullup eingebaute Pullup eingeschaltet, um sich einen externen Widerstand zu sparen.&lt;br /&gt;
&lt;br /&gt;
Der externe Interrupt ist auf steigende Flanke eingestellt. Dann wird der Timer2 im CTC-Modus gestartet. Überlaufzeit beträgt 24us. In den Bildern erkennt man die Überläufe des Timers 2.&lt;br /&gt;
Bei jedem Überlauf wird geschaut, ob ein V-Sync Signal vorliegt. Wenn beim ersten Überlauf der Syncwert gleich dem Syncwert des zweiten Überlaufes ist, beginnt ein Odd Frame (Bild links). Bei unterschiedlichen Syncwerten beginnt ein Even Frame (Bild rechts).&lt;br /&gt;
Die Aufgabe des Timers 2 ist nur das Sync-Signal zu extrahieren und die aktuelle Zeilennummer zu bestimmen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Messung der Helligkeits- und Farbsignale&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Timer1 ist für den Burst-Gate-Impuls und für die Integration über die Zeile zuständig. Wenn ein H-Sync-Impuls (ext. Int0) kommt, wird der Timer1 gestartet. Beim ersten Überlauf wird der Burst-Gate-Impuls generiert. Beim zweiten mal Überlaufen wird die Integration gestartet und beim dritten Überlaufen wird die Integration gestoppt und die Sample-&amp;amp;-Hold-Schaltungen alle gleichzeitig gestartet.&lt;br /&gt;
Anschließend wird der AD-Wandler gestartet, welcher nacheinnander alle 3 Signale wandelt. Die Wandlung von allen 3 Signalen dauert ca. 33us.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Integrate.png]]&lt;br /&gt;
&lt;br /&gt;
Dieses Bild zeigt 4 Linien mit dem BAS-Signal (blau). Das pinke Signal ist die Integration mit Sample &amp;amp; Hold. Man erkennt, dass bei hellerem Bild (höhere blaue Linie) der Endwert der Integration höher ist.&lt;br /&gt;
Außerdem erkennt man gut, dass bei der nächsten Linie ein Achtel weiter hinten integriert wird. Die Länge der S-&amp;amp;-H-Phase ist die AD-Wandlung, ca. 11us pro Wandlung.&lt;br /&gt;
&lt;br /&gt;
Um eine Messung zu starten, setze ich das DDRC-Register auf Ausgang, damit wird der Integrator-Kondensator entladen und die Schaltung auf Null gesetzt. Dann wird für 1/8 der Zeit integriert, indem das DDRC-Register auf Eingang und die Sample-&amp;amp;-Hold-Schaltung auf aus gesetzt wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Berechnung der RGB-Werte&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In Zeile 10 des Bildes werden die Y-, U- und V-Matrix auf Null gesetzt. Bis zur Zeile 620 werden alle Integrationswerte gespeichert und aufaddiert. Bei Zeile 620 beginnt nun die Berechnung. &lt;br /&gt;
&lt;br /&gt;
Der Y-Wert wird auf 127 normiert, somit hat er einen Wertebereich von 0-127. &lt;br /&gt;
Das geschieht mit der Multiplikation vom Matrixwert mit 0,1875. Der Wert wurde durch ausprobieren herausgefunden. Die Multiplikation wird mit fractional numbers durchgeführt.&lt;br /&gt;
Die Normierung ist nötig, da mit Vorzeichen gerechnet wird und höhere Zahlen dann negativ wären.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird der U-Wert normiert und mit 0,25 multipliziert. Dieser Wert wurde auch durch ausprobieren herausgefunden. Nun wird der U-Wert und der Y-Wert addiert und das Blausignal ist fertig.&lt;br /&gt;
&lt;br /&gt;
Das Rotsignal wird ähnlich berechnet, nur dass die V-Matrix verwendet wird und der Multiplikant ist 0,22. Auch hier wird der V-Wert und der Y-Wert addiert und man erhält das Rotsignal.&lt;br /&gt;
&lt;br /&gt;
Das Grünsignal muss mit Y-, U- und V-Signal berechnet werden. Die U- und V-Signale werden wieder normiert und diesmal von Helligkeitssignal Y abgezogen.&lt;br /&gt;
Damit erhält man das Grünsignal. Der Multiplikant für U ist 0,047 und für V ist er 0,078.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
&lt;br /&gt;
Die Schaltung funkioniert gut. Es gibt aber noch einige Verbesserungsmöglichkeiten.&lt;br /&gt;
&lt;br /&gt;
Die Kondensatoren bei den Integratoren sind zu klein, damit wird das Signal verfälscht, sobald der Mega48 seine interne S-&amp;amp;-H-Schaltung aktiviert. Je nachdem, ob der interne Kondensator voller oder leerer als der vom Integrator, gibt es Ausgleichvorgänge, welche nicht unerheblich sind. Am besten wäre eine 10-50 höhere Kapazität beim Integrator. Dann müssten die Widerstände noch angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Die PLL-Schaltung schwingt etwas, das Tiefpassfilter müsste noch optimiert werden. &lt;br /&gt;
&lt;br /&gt;
Zur Zeit verwende ich [[Soft-PWM]], da ich nur noch einen Timer übrig habe, das heißt nur 2 PWM-Kanäle. Besser wäre es, einen größeren Mega zu nehmen oder externe PWM-Bausteine zu verwenden.&lt;br /&gt;
&lt;br /&gt;
Die Schaltung lässt sich nur mit einem S-Video mit getrennten Farb- und Helligkeitsignal betreiben. Wenn ein FBAS-Signal angeschlossen werden soll, müssen noch ein Tiefpass und ein Hochpass eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
Der Burst-Detektor arbeitet noch nicht zuverlässig, da ich nur den internen Pullup-Widerstand verwendet habe. In der nächsten Version muss ein externer Widerstand hin und das Poti kann durch ein Festwiderstand ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
Zur Zeit gebe ich nur ein Feld an die LEDs aus. Man müsste noch über mehrere Felder mitteln, dann würde die LEDs nicht so stark reagieren auf Bildänderungen.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Sourcecode: [[Bild:Ambilight1 1.zip]]&lt;br /&gt;
* Schaltplan: [[Bild:Ambi bilder.zip]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Category:Displays und Anzeigen]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=MMC/SD_Bootloader_f%C3%BCr_AT_Mega&amp;diff=39373</id>
		<title>MMC/SD Bootloader für AT Mega</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=MMC/SD_Bootloader_f%C3%BCr_AT_Mega&amp;diff=39373"/>
		<updated>2009-09-25T15:01:19Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: -= Kategorie:Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Beschreibung =&lt;br /&gt;
Dieser [[Bootloader]] wird einmalig, z.B. per PonyProg, STK500, etc., in einen AVR Atmega geschrieben. Ab nun kann das Hauptprogramm mit Hilfe einer SD oder MMC Speicherkarte in den µC geschrieben werden.&lt;br /&gt;
Um sicherzustellen dass das Gerät mit dem richtigen Update beschrieben wird, wird im Hauptprogramm mit Hilfe einer Section am Ende des Hauptprogramms eine eindeutige Hardware-ID, eine Software-Versionsnummer und eine Prüfsumme hinterlegt.&lt;br /&gt;
Zum Aktualisieren des Hauptprogrammes wird im selbigen die Versionnummer erhöht, kompiliert, und mit einem kleinen Kommandozeilen-Tool die CRC berechnet und in die Datei geschrieben. Diese Datei wird dann auf eine Speicherkarte kopiert, an den µC angeschlossen, und der µC resettet.&lt;br /&gt;
&lt;br /&gt;
= Ausstattung =&lt;br /&gt;
* Passt in 2 kB Bootsection, also z.B. auch für Atmega8 geeignet&lt;br /&gt;
* Liest von MMC und SD Karten mit oder ohne MBR, die mit FAT16 oder FAT12 formatiert sind&lt;br /&gt;
* Anhand einer 4 Byte langen Kennung, die im Bootloader und im Hauptprogramm hinterlegt ist, wird die passende Datei auf der Karte geladen. So ist es möglich  Updates für verschiedene Geräte gleichzeitig auf der Karte zu haben&lt;br /&gt;
* CRC Überprüfung der eingespielten Software&lt;br /&gt;
* Ohne Änderung an Geräten, die nur einen ISP Stecker haben verwendbar. Der CS Pin der Karte wird in diesem Fall auf GND gelegt.&lt;br /&gt;
* Optional: Status-LED. Diese wird beim Start des Bootloaders aktiviert, flackert während eines Aktualisierungsvorgangs und verlischt schließlich, wenn die CRC-Prüfung erfolgreich war und das Hauptprogramm gestartet wird.&lt;br /&gt;
&lt;br /&gt;
= Erstellen und Anpassen =&lt;br /&gt;
== Bootloader ==&lt;br /&gt;
Der Bootloader muss zunächst an den verwendeten µC angepasst werden. Dazu öffnet man zunächst das makefile des Bootloaders und passt diesen Ausschnitt an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
DEVID = 0x12345678&lt;br /&gt;
TARGET = bootloader&lt;br /&gt;
BOOTLOADERSTARTADR = 0x3800&lt;br /&gt;
BOOTLDRSIZE = 0x800&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der DEVID handelt es sich um eine eindeutige Hardware-Kennung, die für jedes Gerät eindeutig vergeben werden muss.&lt;br /&gt;
BOOTLOADERSTARTADR ist die Adresse, auf der der Bootloader liegt, im obigen Beispiel für einen ATMega 16. Dieser hat 16384 Bytes Flash, davon wird die Größe des Bootloaders (2048 Bytes) abgezogen und man erhält 14336 = 0x3800.&lt;br /&gt;
&lt;br /&gt;
Nun muss noch in mmc_lib.h angegeben werden, wo die Speicherkarte angeschlossen ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define MMC_PORT PORTB&lt;br /&gt;
#define MMC_DDR DDRB&lt;br /&gt;
&lt;br /&gt;
#define SPI_MISO	PB3		//DataOut of MMC &lt;br /&gt;
#define SPI_MOSI	PB6		//DataIn of  MMC&lt;br /&gt;
#define SPI_CLK  	PB7		//Clock of MMC&lt;br /&gt;
#define MMC_CS		PB4		//ChipSelect of MMC&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für CS der Speicherkarte sollte der SS Pin des µC verwendet werden. Der SS Pin muss sowieso auf Ausgang und Low gesetzt werden, weil sonst der SPI Master nicht funktioniert.&lt;br /&gt;
&lt;br /&gt;
== Hauptprogramm ==&lt;br /&gt;
Im Hauptprogramm müssen folgende Angaben an entsprechender Stelle im makefile gemacht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
FORMAT = binary&lt;br /&gt;
DEVID = 0x12345678&lt;br /&gt;
SWVERSIONMAJOR = 1&lt;br /&gt;
SWVERSIONMINOR = 0&lt;br /&gt;
BOOTLDRSIZE = 0x800&lt;br /&gt;
#FLASHSIZE - BOOTLDRSIZE - 8&lt;br /&gt;
BOOTLDRINFOSTART = 0x37F8&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
CDEFS += -DDEVID=$(DEVID)&lt;br /&gt;
CDEFS += -DSWVERSIONMAJOR=$(SWVERSIONMAJOR)&lt;br /&gt;
CDEFS += -DSWVERSIONMINOR=$(SWVERSIONMINOR)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
LDFLAGS	+= -Wl,--section-start=.bootldrinfo=$(BOOTLDRINFOSTART)&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Format muss zwingend auf binary gesetzt werden. Die DEVID muss mit der des Bootloaders übereinstimmen. Mit SWVERSIONMAJOR und SWVERSIONMINOR wird die Softwareversionsnummer eingestellt. Der Bootloader schreibt das Hauptprogramm nur dann, wenn die Versionnummer auf der Karte größer als im Flash ist.&lt;br /&gt;
BOOTLDRINFOSTART ist die Adresse, auf der der Informationsblock (DEVID, Version, CRC) landet. Das Beispiel bezieht sich ebenfalls auf den ATMega16: 16384 Flash - 2048 Bootloader - 8 Bytes Info = 14328 = 0x37F8.&lt;br /&gt;
&lt;br /&gt;
In main.c wird der Infoblock deklariert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
	unsigned long dev_id;&lt;br /&gt;
	unsigned short app_version;&lt;br /&gt;
	unsigned short crc;&lt;br /&gt;
} bootldrinfo_t;&lt;br /&gt;
&lt;br /&gt;
const bootldrinfo_t bootlodrinfo __attribute__ ((section (&amp;quot;.bootldrinfo&amp;quot;))) = {DEVID, SWVERSIONMAJOR &amp;lt;&amp;lt; 8 | SWVERSIONMINOR, 0x0000};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann das Hauptprogramm compiliert werden. Die entstehende .hex Datei (binary Format!) wird anschließend noch mit dem Kommandozeilentool crcgen.exe behandelt (z.B. crcgen myflashfile.hex). Der Aufruf dieses Programmes kann auch vom makefile aus geschehen.&lt;br /&gt;
&lt;br /&gt;
Das Update benennt man am besten etwa in der Form MeinApparat-v1.0-bin, darf aber beliebig (lang) sein .&lt;br /&gt;
&lt;br /&gt;
= Einschränkungen =&lt;br /&gt;
* Der Bootloader unterstützt nur FAT12 (Partitionstyp 1) und FAT16 (Partitionstyp 6). Große Karten die mit FAT32 formatiert wurden sind nicht geeignet, und müssen zunächst mit FAT16 formatiert werden.&lt;br /&gt;
* Das Update muss im Hauptverzeichnis der Speicherkarte liegen.&lt;br /&gt;
&lt;br /&gt;
= Download und Diskussion =&lt;br /&gt;
Der Sourcecode findet sich in der Codesammlung unter&lt;br /&gt;
http://www.mikrocontroller.net/topic/67047#new&lt;br /&gt;
Immer schön runterscrollen um die brandneueste Version zu erwischen!&lt;br /&gt;
&lt;br /&gt;
= Danke =&lt;br /&gt;
an&lt;br /&gt;
* Jörg&lt;br /&gt;
* holger&lt;br /&gt;
* Werner B.&lt;br /&gt;
* Hagen Re&lt;br /&gt;
für die Codeoptimierung. So konnte die ursprüngliche Fassung, in der nur FAT16 implementiert war, um FAT12 erweitert werden.&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR-Programmer und -Bootloader]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Layoutsammlung&amp;diff=39348</id>
		<title>Layoutsammlung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Layoutsammlung&amp;diff=39348"/>
		<updated>2009-09-25T14:59:43Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* Siehe auch */ --&amp;gt;Kategorie:AVR-Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Einige interessante Layouts aus der [http://www.mikrocontroller.net/forum/codesammlung Codesammlung]&lt;br /&gt;
----&lt;br /&gt;
Symbole zur Kennzeichnung des verwendeten CAD-Systems&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|(E)&lt;br /&gt;
|EAGLE Projekt, (E5) = EAGLE 5&lt;br /&gt;
|-&lt;br /&gt;
|(K)&lt;br /&gt;
|[[KiCAD]] Projekt&lt;br /&gt;
|-&lt;br /&gt;
|(P)&lt;br /&gt;
|Protel Projekt&lt;br /&gt;
|-&lt;br /&gt;
|(T)&lt;br /&gt;
|Target Projekt&lt;br /&gt;
|-&lt;br /&gt;
|(X)&lt;br /&gt;
|Rohdaten der Leiterplatte (pdf, gerber, usw.)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Audio ==&lt;br /&gt;
&lt;br /&gt;
* (E) [http://www.mikrocontroller.net/topic/114262 SD-Karten-Wave-Recorder] (ATXMega128A1 + 2x ATTiny2313)&lt;br /&gt;
&lt;br /&gt;
== Entwicklungsboard ==&lt;br /&gt;
* (X) [[Zilog Encore Experimentierplatine]]&lt;br /&gt;
&lt;br /&gt;
== Programmieradapter ==&lt;br /&gt;
&lt;br /&gt;
* (E) [[AVR HV-Programmer|Platinenlayout für ElmChans AVR HV-Programmer]]&lt;br /&gt;
&lt;br /&gt;
== RF, Funk ==&lt;br /&gt;
&lt;br /&gt;
* (E) [http://www.mikrocontroller.net/topic/90021#768374 Layout RFM12 Funkmodul]&lt;br /&gt;
* (E) [http://www.mikrocontroller.net/topic/68442#552128 USB RFID Tag Leser] für 64 Bit 125 kHz Tags&lt;br /&gt;
* (X) [[RFID Türmodul]]&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
&lt;br /&gt;
* (E) [http://www.mikrocontroller.net/topic/120816 Giess-o-mat mit AVR] - Blumengiessanlage mit Atmega8&lt;br /&gt;
* (X) [[STK500USB-Adapter]] - USB zu RS232 Adapter mit Schaltregler zur Versorgung des [[STK500]] per USB&lt;br /&gt;
&lt;br /&gt;
== E-Tools ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/120124 Kleiner Funktionsgenerator mit Tiny2313]&lt;br /&gt;
* (E) [http://www.mikrocontroller.net/topic/103600 I2C (TWI) Sniffer mit AVR] (ATtiny85 mit 14,7456MHz Quarz)&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Schaltplaneditoren]]&lt;br /&gt;
* [[AVR Softwarepool]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Category:Platinen]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=CAN_Bibiliothek_f%C3%BCr_AT90CAN_Prozessoren&amp;diff=39347</id>
		<title>CAN Bibiliothek für AT90CAN Prozessoren</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=CAN_Bibiliothek_f%C3%BCr_AT90CAN_Prozessoren&amp;diff=39347"/>
		<updated>2009-09-25T14:57:41Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* Dateien mit dem Quelltext der Bibliotheksprogramme */ --&amp;gt; Kategorie:AVR-Projekte&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Autor: [[Benutzer:Harms|Jürgen Harms]] 16:30, 21. Jul. 2008&lt;br /&gt;
==Einleitung==&lt;br /&gt;
&lt;br /&gt;
Dieser Artikel beschreibt eine einfache C-Programmbibliothek für die Programmierung des Zugriffs auf einen CAN Bus mittels eines AVR &#039;&#039;&#039;AT90CAN&#039;&#039;&#039; Prozessors (AT90CAN128, AT90CAN64 oder AT90CAN32). Zusätzlich beschreibt er eine Demo-Anwendung zum Testen dieser Bibliothek; diese Demo-Anwendung kann aber auch als Anregung für die Gestaltung eines einfachen Echtzeitrahmens für kleine Anwendungen dienen. &lt;br /&gt;
&lt;br /&gt;
===Anwendungsgebiet, Eigenschaften===&lt;br /&gt;
&lt;br /&gt;
Die Bibliothek wurde als Teil eines in C geschriebenen Hausbus Systems entwickelt - daraus ergeben sich die Spezifikationen:&lt;br /&gt;
&lt;br /&gt;
* Die Bibliothek macht die wichtigsten Fähigkeiten der CAN Kontrolleinheit auf der Ebene des Aufrufs von &#039;&#039;&#039;C-Unterprogrammen&#039;&#039;&#039; verfügbar.&lt;br /&gt;
&lt;br /&gt;
* Die Anwendung muss &#039;&#039;&#039;interruptgetrieben&#039;&#039;&#039; sein.&lt;br /&gt;
&lt;br /&gt;
* Die Bibliothek verwendet einen einfachen &#039;&#039;&#039;Prioritätsmechanismus&#039;&#039;&#039; bei der Behandlung von Nachrichten: falls mehrere Nachrichten gleichzeitig auf das Senden oder Empfangen warten, bestimmt dieser Mechanismus welche Nachricht zuerst von der Kontrolleinheit des AT90CAN bedient wird.&lt;br /&gt;
&lt;br /&gt;
* Sie erlaubt sparsamen Umgang mit dynamischem Speicherplatz.&lt;br /&gt;
&lt;br /&gt;
* Bei der Implementierung wurde auf Einfachheit und leichte Lesbarkeit geachtet.&lt;br /&gt;
&lt;br /&gt;
* In der jetzigen Version sind die &#039;&#039;&#039;Taktraten fest kodiert&#039;&#039;&#039; und setzen einen 16 MHz Quarz voraus (Bus Takt = 100Kbps, interner Takt der CAN Kontrolleinheit = 10Kbps) - diese Parameter sind aber leicht änderbar.&lt;br /&gt;
&lt;br /&gt;
* Mit einer Ausnahme bietet die Bibliothek derzeit keine Vorkehrungen zur &#039;&#039;&#039;Behandlung von Fehlern&#039;&#039;&#039;: die CAN Kontrolleinheit des AT90CAN besorgt weitgehend - und unter normalen Bedingungen ausreichend - die Behandlung von Übermittlungsfehlern. Die Ausnahme betrifft das Erkennen von gesendeten Nachrichten, die während längerer Zeit nicht vom Bus quittiert werden und damit das entsprechende MOb blockieren (der Begriff MOb ist im folgenden Abschnitt definiert).&lt;br /&gt;
&lt;br /&gt;
===Begriffe===&lt;br /&gt;
&lt;br /&gt;
Ein paar Begriffe werden im folgenden häufig verwendet und haben, im Kontext von CAN, eine sehr spezifische Bedeutung:&lt;br /&gt;
&lt;br /&gt;
: Der Begriff &#039;&#039;&#039;MOb&#039;&#039;&#039; - (&#039;&#039;Message Object&#039;&#039;) - bezeichnet die Infrastruktur in der Kontrolleinheit, welche zur Darstellung und Behandlung einer Nachricht vorgesehen ist.&lt;br /&gt;
&lt;br /&gt;
: Eine &#039;&#039;&#039;Adresse&#039;&#039;&#039; (&#039;&#039;Identifier Tag&#039;&#039;) auf einem CAN Bus bezeichnet eine - für normal Gruppe von - Adressaten. Knoten am Bus beobachten das Adressfeld von Nachrichten und vergleichen es mit Schablonen, die bei der Initialisierung des Knotens definiert wurden. CAN unterscheidet zwischen &#039;&#039;&#039;normalen&#039;&#039;&#039; und &#039;&#039;&#039;erweiterten&#039;&#039;&#039; Adressen (dargestellt in 11, bzw. 29 Bits).&lt;br /&gt;
&lt;br /&gt;
: Ein &#039;&#039;&#039;Filter&#039;&#039;&#039; enthält solch eine Schablone; zusätzlich zu den Bits zur Adressierung von Knoten bestimmt das Filter auch Bits, welche bestimmte Eigenschaften beschreiben. Ein Filter wird getriggert - die entsprechende Nachricht wird vom Knoten empfangen - wenn alle in der Schablone festgelegten Bedingungen erfüllt sind.&lt;br /&gt;
&lt;br /&gt;
===Implementierung, Demo-Anwendung===&lt;br /&gt;
&lt;br /&gt;
Die Bibliothek wurde in einer Unix Programmierumgebung (AVR-Gcc, Make, AVR-Libc) entwickelt. Diese Umgebung gibt es auch in einer ausgezeichneten und gut gewarteten Variante für Windows. Die Bibliothek läuft bei mir ohne bekannte Probleme, allerdings wurde sie bisher (zur Zeit der Veröffentlichung dieses Artikels) nur im Rahmen der Anwendung eingesetzt für welche die Bibliothek geschrieben wurde.&lt;br /&gt;
&lt;br /&gt;
Zur Illustration ist die Bibliothek durch eine Demo-Anwendung und eine entsprechenden Makefile Datei ergänzt.&lt;br /&gt;
&lt;br /&gt;
Die Anzeige von Test- und Fehlermeldungen beruht auf der Ausgabe an stdout und verwendet die Programme in der Datei Uart.c (Teil der Demoanwendungen der AVR-Libc) - mit zwei kleinen Anpassungen:&lt;br /&gt;
(1) es wird das Bestehen des zweiten seriellen Kanals im AT90CAN berücksichtigt,&lt;br /&gt;
und (2) die Warteschleife im Unterprogramm UartPutchar ist durch den Aufruf der &amp;quot;Idle-Loop&amp;quot; der Demo-Anwendung ersetzt. Damit ist die Verwendung auch bei aktivem Interruptsystem möglich - allerdings muss man Sorge tragen, dass zu jeder Zeit höchstens eine Ausgabe an stdout erfolgt.&lt;br /&gt;
&lt;br /&gt;
==Elemente der CAN Bibliothek==&lt;br /&gt;
&lt;br /&gt;
===Unterprogramme und globale Variable===&lt;br /&gt;
&lt;br /&gt;
Die Bibliothek verwendet eine globale Variable - &#039;&#039;can_status&#039;&#039;, welche in der Datei Can.c definiert ist. Sie dient vor allem der Synchronisation zwischen Interrupt Behandlung und Anwendungsprogramm. &lt;br /&gt;
&lt;br /&gt;
Die wesentlichen Aufgaben werden von vier Unterprogrammen besorgt:&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;CanInit&#039;&#039;&#039; initialisiert die Kontrolleinheit des AVR Prozessors und die Variablen der Programmbibliothek,&lt;br /&gt;
: &#039;&#039;&#039;CANIT_vect&#039;&#039;&#039; bedient CAN-relevante Interrupts,&lt;br /&gt;
: &#039;&#039;&#039;CanGetFrame&#039;&#039;&#039; behandelt den Empfang von Nachrichten,&lt;br /&gt;
: &#039;&#039;&#039;CanSendFrame&#039;&#039;&#039; besorgt das Senden von Nachrichten.&lt;br /&gt;
&lt;br /&gt;
Weitere Unterprogramme sind:&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;CanCheckWait&#039;&#039;&#039; hilft zu erkennen, wann das Senden einer Nachricht exzessiv lange dauert (z.B. weil kein Busteilnehmer antwortet),&lt;br /&gt;
: &#039;&#039;&#039;CanKillFrame&#039;&#039;&#039; erlaubt solche Nachrichten zu annullieren,&lt;br /&gt;
: &#039;&#039;&#039;CanNormalID&#039;&#039;&#039; und &#039;&#039;&#039;CanExtendedID&#039;&#039;&#039; dienen der Behandlung der Adressen und von Filtern; diese beiden Programme sind nur innerhalb von CanInit aufrufbar.&lt;br /&gt;
&lt;br /&gt;
Alle diese Unterprogramme sind im Modul Can.c implementiert. Modul Can.h definiert die entsprechenden Datenstrukturen und Konstanten.&lt;br /&gt;
&lt;br /&gt;
===Datenstrukturen===&lt;br /&gt;
&lt;br /&gt;
Die folgenden Strukturen sind für die Programmierung der Anwendung von besonderer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Die Struktur can_header&#039;&#039;&#039; dient zur Beschreibung einer zu sendenden oder gerade empfangenen Nachricht:&lt;br /&gt;
* &#039;&#039;&#039;mob_header&#039;&#039;&#039; = die Adresse der Nachricht - als 32-Bit binärer Wert dargestellt,&lt;br /&gt;
* &#039;&#039;&#039;mob_stamp&#039;&#039;&#039; = die Zeit (10 msec Einheiten) bei Eintreffen des die Nachrichten betreffenden Interrupts (Nachricht vollkommen empfangen, resp. Senden der Nachricht abgeschlossen),&lt;br /&gt;
* &#039;&#039;&#039;mob_length&#039;&#039;&#039; = die Zahl Bytes im Datenbereich der Nachricht,&lt;br /&gt;
* &#039;&#039;&#039;mob_flags&#039;&#039;&#039; = eine Liste von Bits zur Beschreibung der Eigenschaften des MOb:&lt;br /&gt;
** CAN_EXTD = wenn dieses Bit gesetzt ist, werden erweiterte, andernfalls normale Adressen verwendet.&lt;br /&gt;
** RTRTAG, RB0TAG, RB1TAG ... siehe AT90CAN Datenblatt,&lt;br /&gt;
* &#039;&#039;&#039;mob_page&#039;&#039;&#039;= der Index (0 bis 14) des die Nachricht enthaltenden MOb.&lt;br /&gt;
: Die Speicherung der Daten erfolgt unabhängig von dieser Struktur: die Speicherzone für die Daten wird beim Aufruf von &#039;&#039;CanSendFrame&#039;&#039; und &#039;&#039;CanGetFrame&#039;&#039; als eigenes Argument angegeben; dadurch können für die Beschreibung des MOb und der Daten Variablen mit unterschiedlicher Lebensdauer verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Die Struktur can_descr&#039;&#039;&#039; beschreibt den Zustand der CAN Kontrolleinheit:&lt;br /&gt;
* &#039;&#039;&#039;cs_stamp&#039;&#039;&#039; = eine Tabelle (15 Einträge - einer pro MOb) mit dem Zeitpunkt, bei dem das Senden eines MOb ausgelöst wurde (= Wert des oberen Bytes von CANTIMH),&lt;br /&gt;
* &#039;&#039;&#039;cs_rcvOK&#039;&#039;&#039; = eine 16-Bit Liste mit Bits zur Markierung von MObs, für welche die Anwendung den Empfang einer neuen Nachricht behandeln muss (0 wenn keine),&lt;br /&gt;
* &#039;&#039;&#039;cs_xmtOK&#039;&#039;&#039; = eine 16-Bit Liste mit Bits zur Markierung von MObs, für welche die Anwendung das Ende des Sendens behandeln muss (0 wenn keine),&lt;br /&gt;
* &#039;&#039;&#039;cs_mask&#039;&#039;&#039; = eine 16-Bit Liste mit Bits zur Markierung von MObs, die für das Senden reserviert sind,&lt;br /&gt;
* &#039;&#039;&#039;cs_xmit&#039;&#039;&#039; = der MOb Index des ersten MOb welches für das Senden reserviert ist (redundant zu cs_mask),&lt;br /&gt;
* &#039;&#039;&#039;cs_error&#039;&#039;&#039; = der Fehlerkode beim letzten Interrupt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Die Struktur can_filter&#039;&#039;&#039; dient zur Definition der Filterdaten bei der Initialisierung eines MOb. Dies erlaubt bei der Initialisierung der Kontrolleinheit die für den Empfang bestimmten MObs auf einfache Weise zu definieren; die vier Elemente der Struktur sind:&lt;br /&gt;
* &#039;&#039;&#039;cf_idt&#039;&#039;&#039; = bestimmt die Adressen, die dem MOb zugeordnet sind, dargestellt als 32-Bit binärer Wert,&lt;br /&gt;
* &#039;&#039;&#039;cf_mask&#039;&#039;&#039; = die Liste der bei dieser Definition signifikanten Bits,&lt;br /&gt;
* &#039;&#039;&#039;cf_iflags&#039;&#039;&#039; = die Liste von Werten der Bits zur Beschreibung der Eigenschaften des MOb, die beim Filtern beachtet werden,&lt;br /&gt;
* &#039;&#039;&#039;cf_mflags&#039;&#039;&#039;  = die Liste der bei dieser Definition signifikanten Bits.&lt;br /&gt;
&lt;br /&gt;
: Die mit cf_iflags und cf_mflags zu definierenden Bits sind:&lt;br /&gt;
&lt;br /&gt;
:: - &#039;&#039;&#039;CAN_EXTD&#039;&#039;&#039; = erweiterte oder normale Adressen verwenden, je nachdem ob dieses Bit gesetzt ist oder nicht,&lt;br /&gt;
:: - &#039;&#039;&#039;RTRTAG, RB0TAG, RB1TAG&#039;&#039;&#039; ... siehe AT90CAN Datenblatt,&lt;br /&gt;
:: - &#039;&#039;&#039;RTRMSK, RB0MSK, RB1MSK&#039;&#039;&#039; ... siehe AT90CAN Datenblatt.&lt;br /&gt;
&lt;br /&gt;
: Wenn das Unterprogramm &#039;&#039;CanInit&#039;&#039; ein Filter einrichtet, werden die binären Werte aus cf_idt und cf_mask in Felder zerlegt und in den Registern CANIDT1 ... CANDIT4, bzw. CANIDM1 ... CANDIM4 gespeichert. Die Bits aus cf_iflags und cf_mflags werden dabei in die dem Namen der Symbole entsprechenden Positionen von CANIDT4, bzw. CANIDM4 gesetzt.&lt;br /&gt;
&lt;br /&gt;
: Das Bit &#039;&#039;&#039;CAN_EXTD&#039;&#039;&#039; spielt eine etwas eigene Rolle:&lt;br /&gt;
:: - sein Wert wird zwar nur in cf_mflags angegeben, es steuert aber die Behandlung der meisten von &#039;&#039;CanInit&#039;&#039; behandelten Werte:&lt;br /&gt;
:: - es bestimmt das Format, nachdem die Werte aus cf_idt und cf_idm in Felder zerlegt werden;&lt;br /&gt;
:: - ausserdem bestimmt es, ob das IDE Bit im Register CANCDMOB gesetzt wird oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Konstanten und Parameter===&lt;br /&gt;
&lt;br /&gt;
Die Bibliothek verwendet die Symbole für die Bezeichnung von Registern und Bits der Kontrolleinheit wie sie im ATMEL Datenblatt definiert sind - sie werden automatisch von AVR-Libc eingeführt; Can.h definiert zwei zusätzliche Symbole und deren Werte:&lt;br /&gt;
* &#039;&#039;&#039;CAN_EXTD&#039;&#039;&#039; bestimmt, ob ein Filter, resp. eine zu sendende Nachricht den normalen Adressbereich (V2.0 part A) oder den erweiterten Adressbereich (V2.0 part B) verwendet,&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;MG_PRIORITY&#039;&#039;&#039; bestimmt ein Bit (binäre Maske) zur Kontrolle der Priorität beim Senden: Nachrichten, in deren Adresse dieses Bit gesetzt ist, haben geringere Priorität als solche, bei denen das Bit den Wert 0 hat (der Wert von MG_PRIORITY ist ein binärer Wert, nicht der Wert der dann in den Registern der Kontrolleinheit abgesetzt wird - siehe die Umwandlung in den Unterprogramme &#039;&#039;CanNormaID&#039;&#039; und &#039;&#039;CanExtendedID&#039;&#039;).&lt;br /&gt;
: &#039;&#039;MG_PRIORITY&#039;&#039; steuert sowohl die Priorität bei der Wahl des zu sendenden MOb - wenn mehr als eines in der Kontrolleinheit wartet (Auswahl durch das Programm &#039;&#039;CanSendFrame&#039;&#039;), als auch die Priorität beim Behandeln von Kollisionen am Bus (CAN arbitriert hierbei nach Massgabe der numerischen Werte der Adressfelder).&lt;br /&gt;
&lt;br /&gt;
Die in die Register CANBT1, CANBT2, CANBT3 programmierten Werte müssen der Taktrate am Bus entsprechen (100Kbps im Original der Bibliothek); der Inhalt des Registers CANTCON bestimmt den internen Takt der Kontrolleinheit - er wird nur für den Mechanismus der Erkennung von hängengebliebenen Nachrichten beim Senden verwendet.&lt;br /&gt;
&lt;br /&gt;
Falls der Wert von CANTCON oder die Echtzeit-Uhr anders definiert werden als im Original der Bibliothek, muss auch die Schwelle zum Erkennen von beim Senden zu lange wartenden MObs entsprechend geändert werden - siehe Kommentar im Unterprogramm &#039;&#039;CanCheckWait&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Unterprogramme der CAN Bibliothek==&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Unterprogramme sind reichlich kommentiert, der folgende Text beschränkt sich daher auf ergänzende Kommentare.&lt;br /&gt;
&lt;br /&gt;
===CanInit===&lt;br /&gt;
&lt;br /&gt;
Der erste Abschnitt (Initialisierung des MObs) definiert die Filter für den Empfang von Nachrichten. Die Angaben hierfür werden als Argumente beim Aufruf von &#039;&#039;CanInit&#039;&#039; festgelegt: das erste Argument ist eine Tabelle mit Definitionen für die Filter (im Flash-Speicher programmiert!) und das zweite die Zahl der zu definierenden Filter (wenn diese Zahl 0 ist, definiert &#039;&#039;CanInit&#039;&#039; automatisch ein einziges Filter, das für den Empfang aller Nachrichten dient). Der Index in der Tabelle entspricht dem Index des zu definierenden MOb in der Kontrolleinheit. MObs mit Indizes jenseits der so festgelegten Werte werden für das Senden von Nachrichten verwendet.&lt;br /&gt;
&lt;br /&gt;
Bei der Definition von Filtern ist zu beachten, dass immer ein letztes Filter zum Empfang aller bis dahin nicht erkannten Nachrichten vorgesehen werden sollte (siehe Datenblatt des AT90CAN, Abschnitt 33.2 - &amp;quot;Errata&amp;quot;, Paragraph 6).&lt;br /&gt;
&lt;br /&gt;
===ISR CANIT_vect===&lt;br /&gt;
&lt;br /&gt;
Dies ist das Programm zum Abarbeiten der von der CAN Kontrolleinheit erzeugten Interrupts. Folgende Interrupts werden behandelt:&lt;br /&gt;
&lt;br /&gt;
: Interrupt nach Vollendung des &#039;&#039;&#039;Sendens einer Nachricht&#039;&#039;&#039;:&lt;br /&gt;
:: Das Interrupt wird gelöscht, das MOb wird für neue Verwendung freigegeben. Das Bit mit dem Index des betroffenen MOb wird in der Kontrollvariablen &#039;&#039;can_status.cs_xmtOK&#039;&#039; gesetzt - damit wird das Ereignis der &amp;quot;Idle-Loop&amp;quot; des Anwendungsprogrammes gemeldet.&lt;br /&gt;
&lt;br /&gt;
: Interrupt wenn einer &#039;&#039;&#039;empfangene Nachricht&#039;&#039;&#039; verfügbar ist:&lt;br /&gt;
:: Das Interrupt wird gelöscht, das MOb mit der empfangenen Nachricht wird bis zur Behandlung durch die Anwendung (Abholen der Empfangenen Daten) blockiert. Das Bit mit dem Index des betroffenen MOb wird in der Kontrollvariablen &#039;&#039;can_status.cs_rcvOK gesetzt&#039;&#039; - damit wird das Ereignis der &amp;quot;Idle-Loop&amp;quot; des Anwendungsprogrammes gemeldet.&lt;br /&gt;
&lt;br /&gt;
: Interrupt zufolge eines &#039;&#039;&#039;Fehlers&#039;&#039;&#039;:&lt;br /&gt;
:: Derzeit im Programm noch nicht unterstützt, nur das Interrupt wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
===CanSendFrame===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;CanSendFrame&#039;&#039; löst das Senden einer Nachricht durch die Kontrolleinheit aus (das Ende des Sendevorganges wird aber nicht abgewartet). Falls - z.B. wenn Nachrichten hintereinander und in einer garantierten Reihenfolge gesendet werden sollen - das Ende abgewartet werden muss, ist dies Aufgabe der Anwendung (dazu dient z.B. das Unterprogramms &#039;&#039;XmitWait&#039;&#039;, siehe Demo-Anwendung). &lt;br /&gt;
&lt;br /&gt;
Wichtige Abschnitte des Programms:&lt;br /&gt;
* Wahl eines verfügbaren MOb&lt;br /&gt;
: Die Kontrolleinheit des AT90CAN entscheidet bei Vorliegen mehrerer zu sendender Nachrichten welche Nachricht mit Priorität zu behandeln ist: die Nachricht in dem MOb mit dem jeweils niedrigsten Index wird als erste gesendet.&lt;br /&gt;
&lt;br /&gt;
: Der Mechanismus zur Wahl des zu verwendenden MOb baut auf dieses Verhalten: das Abtasten zum Finden des ersten verfügbaren MObs beginnt bei dem niedrigsten MOb wenn der Wert des Bits in der Position MG_PRIORITY 0 ist, sonst bei dem MOb mit dem höchsten Index (Index 14).&lt;br /&gt;
&lt;br /&gt;
* Setzen der IDT Register&lt;br /&gt;
: Der binäre Wert in &#039;&#039;p_header.mob_ID&#039;&#039; wird in die entsprechenden Werte für die IDT Register umgesetzt, wobei die unterschiedlichen Formate für die beiden Varianten der Adressierung berücksichtigt werden; der Vorgang hierbei ist analog zu dem, was in der Beschreibung der Datenstruktur &#039;&#039;can_filter&#039;&#039; erklärt ist.&lt;br /&gt;
&lt;br /&gt;
* Auslösen des Sendevorgangs&lt;br /&gt;
: Die Daten werden vom Anwendungsbereich in die Register der Kontrolleinheit kopiert - danach sind die als Argumente für den Aufruf von &#039;&#039;CanSendFrame&#039;&#039; verwendeten Speicherzonen wieder verfügbar. Der Zeitpunkt des Sendens wird in &#039;&#039;can_status.cs_stamp&#039;&#039; (indiziert mit dem Index des MOb) gespeichert - dies ist der zu diesem Zeitpunk in CANTIMH befindliche Wert; er dient als Grundlage für das Programm &#039;&#039;CanCheckWait&#039;&#039; um MObs zu finden, die &amp;quot;hängen&amp;quot; geblieben sind (der häufigste Grund dafür ist das Fehlen eines Partners am Bus, der Nachrichten mit der entsprechenden Adresse quittiert).&lt;br /&gt;
&lt;br /&gt;
===CanGetFrame===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;CanGetFrame&#039;&#039; geht davon aus, dass die Ankunft einer Nachricht schon bei der Behandlung der Interrupts erkannt und behandelt wurde. &#039;&#039;CanGetFrame&#039;&#039; wird daraufhin aufgerufen um die Daten abzuholen (bei der Behandlung des Interrupts wurde das MOb gegen eine versehentliche neue Verwendung geschützt).&lt;br /&gt;
&lt;br /&gt;
Das Programm kopiert danach (a) die wesentlichen Daten aus dem Nachrichten Kopf und (b) die Nachricht selber in die von der Anwendung bestimmten Speicherzonen. Danach wird das MOb wieder freigegeben.&lt;br /&gt;
&lt;br /&gt;
=== CanCheckWait und CanKillFrame===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;CanCheckWait&#039;&#039; ist ein Unterprogramm, das von der Anwendung periodisch aufgerufen werden sollte, wenn nichts anderes zu tun ist. Bei jedem Aufruf wird zyklisch ein MOb untersucht und darauf geprüft, ob und wie lange es mit dem Senden einer Nachricht beschäftigt ist (Vergleich des Werts in &#039;&#039;can_status.cs_stamp&#039;&#039; und mit dem jeweiligen Wert von CANTIMH). Im Kode von &#039;&#039;CanCheckWait&#039;&#039; ist eine Schwelle definiert, deren Überschreiten als Fehler behandelt wird.&lt;br /&gt;
&lt;br /&gt;
Die statische Variable &#039;&#039;next_mob&#039;&#039; in &#039;&#039;CanCheckWait&#039;&#039; bestimmt, welches MOb als nächstes zu Prüfen ist - sie wird nach jedem Aufruf neu definiert.&lt;br /&gt;
&lt;br /&gt;
Das Programm &#039;&#039;CanKillFrame&#039;&#039; dient dazu, den Sendevorgang eines dermassen gefundenen MOb abzubrechen.&lt;br /&gt;
&lt;br /&gt;
Die globale Variable &#039;&#039;killed_mobs&#039;&#039; (sie gibt es erst ab Version V1.0.1) informiert über MObs, deren Senden mithilfe von CanKillFrame abgebrochen wurde. Sie ist anfänglich auf 0 gesetzt; wenn CanKillFrame eine MOb re-aktiviert, wird das entsprechende Bit gesetzt. Eine Anwendung kann einzelne / alle Bits wieder löschen nachdem sie den Sachverhalt zur Kenntnis genommen hat.&lt;br /&gt;
&lt;br /&gt;
Solch ein Sendeabbruch muss nicht unbedingt als Hinweis auf einen zu korrigierenden Fehler verstanden werden: beim Hochfahren einzelner / aller Netzknoten kann es vorkommen, dass Nachrichten versendet werden, bevor ein adressierter Partnerknoten aktiviert ist.&lt;br /&gt;
&lt;br /&gt;
==Demo-Anwendung==&lt;br /&gt;
&lt;br /&gt;
Die Demo-Anwendung erlaubt die Verwendung der CAN Bibliothek unter einfachen und erprobten Bedingungen.&lt;br /&gt;
&lt;br /&gt;
===Echtzeitrahmen===&lt;br /&gt;
&lt;br /&gt;
Das dabei eingesetzte Prinzip ist einfach:&lt;br /&gt;
* der Prozessor durchläuft eine Endlos-Warteschleife - die &amp;quot;Idle-Loop&amp;quot;; nach jedem Durchgang wird der Prozessor in den &amp;quot;Sleep&amp;quot; Zustand gesetzt;&lt;br /&gt;
* die Behandlung von Interrupts definiert &amp;quot;Ereignisse&amp;quot; - deren Eintreffen markiert eine zur Synchronisation vorgesehene Variable; als Folge des Interrupts wird die Warteschleife neu durchlaufen;&lt;br /&gt;
* jeder Durchgang durch die Endlos-Warteschleife prüft die zur Synchronisation bestimmten Variablen und erkennt, wenn eine Variable gesetzt ist: in der Folge wird alles unternommen, was für das Abarbeiten des Ereignisses nötig ist, und die Variable wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
Dieses Prinzip kann auch zur Gestaltung eines minimalen Echtzeitrahmens für einfache Anwendungen verwendet werden, was den Einsatz eines Echtzeit-Betriebsystems erübrigt. Ich verwende, zum Beispiel, dieses Konzept bei meiner Hausbus Anwendung.&lt;br /&gt;
&lt;br /&gt;
Damit dieses Vorgehen klaglos funktioniert sind zwei wichtige Punkte zu beachten:&lt;br /&gt;
# In der gesamten Anwendung dürfen keine &amp;quot;busy-wait&amp;quot; Warteschleifen (= von Interrupts nicht unterbrechbare Schleifen) programmiert sein - wie sie z.B. in den in &amp;amp;lt;util.delay.h&amp;amp;gt; und &amp;amp;lt;util/delay_basic.h&amp;amp;gt; definierten Werkzeugen der AVR-Libc Bibliothek vorkommen; dies ist auch der Grund, warum im Uart.c Programm der AVR-Libc Bibliothek eine kleine Änderung notwendig ist.&lt;br /&gt;
# Iterative Aufrufe der Programme der &amp;quot;Idle-Loop&amp;quot; sind sorgfältig zu programmieren: solche Aufrufe erzeugen zusätzliche Niveaus der Warteschleife und reservieren dabei jeweils alle dafür nötigen lokalen Variablen; ein typisches Beispiel dafür ist, wenn beim Abarbeiten des Empfangs einer Nachricht eine neue Nachricht mit &#039;&#039;CanSendFrame&#039;&#039; gesendet wird und dann das Unterprogramm &#039;&#039;XmitWait&#039;&#039; aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
: Die Variable &#039;&#039;idle_level&#039;&#039; der Demo-Anwendung gibt jederzeit die Zahl aktiver &amp;quot;Idle-Loops&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
Diese &amp;quot;&#039;&#039;&#039;Verschachtelung der Aufrufe&#039;&#039;&#039;&amp;quot; von Warteschleifen ist das &#039;&#039;&#039;kritische Element&#039;&#039;&#039;, das bei der Entscheidung zwischen dem Einsatz der hier beschriebenen Methode und dem Einsatz eines Echtzeit Betriebsystems zu bewerten ist:&lt;br /&gt;
&lt;br /&gt;
* Bei einer Demo-Anwendung zum Erproben der Bibliothek, oder bei einer einfachen Anwendung wie meinem Hausbus (ich habe nie mehr als 2 Niveaus von Warteschleifen) ist diese Methode hervorragend geeignet.&lt;br /&gt;
&lt;br /&gt;
* Bei einer komplexen Anwendung, wo die Zahl der Niveaus hoch oder unvorhersehbar ist, kann der Einsatz dieses Konzepts problematisch werden.&lt;br /&gt;
&lt;br /&gt;
Diese kritische Bemerkung betrifft natürlich nur die Demo-Anwendung, die CAN Programmbibliothek kann ohne Einschränkung sowohl mit einem Echtzeit Betriebssytem als auch mit der einfachen Warteschleifen Methode eingesetzt werden. Bei Einsatz eines Echtzeit Betriebssystems wird es aber wahrscheinlich nötig sein, im Unterprogramm CANIT_vect den Mechanismus für die Kommunikation mit dem Betriebssystem den Anforderungen des gewählten Systems anzupassen.&lt;br /&gt;
&lt;br /&gt;
===Elemente der Demo-Anwendung===&lt;br /&gt;
&lt;br /&gt;
Die wesentlichen Bestandteile der Demo-Anwendung sind:&lt;br /&gt;
* eine &#039;&#039;&#039;Warteschleife&#039;&#039;&#039; (&amp;quot;Idle-Loop&amp;quot;) - nach Ende jeder Iteration geht der Prozessor in den &amp;quot;Sleep&amp;quot; Zustand,&lt;br /&gt;
&lt;br /&gt;
* ein interruptgetriebener &#039;&#039;&#039;Taktgeber&#039;&#039;&#039; (&#039;&#039;Timer&#039;&#039;) mit 10 msec Intervallen, der die Warteschleife periodisch aktiviert und jeder Aktivierung das auslösende Ereignis beiordnet; vom Taktgeber generierte Ereignisse sind: (a) 10-msec Interrupts, (b) 1-sec Echtzeit Zähler-Schritte, und (c) der Ablauf einer vorgewählten Warte-Periode (Unterprogramm &#039;&#039;TimedWait&#039;&#039;),&lt;br /&gt;
&lt;br /&gt;
* Erweiterungen der &amp;quot;Idle-Loop&amp;quot; zum &#039;&#039;&#039;Abarbeiten&#039;&#039;&#039; der bei der Interrupt Behandlung &#039;&#039;&#039;markierten Ereignisse&#039;&#039;&#039; (zusätzlich zu den vom Taktgeber erzeugten Ereignissen natürlich auch zum Abarbeiten der weiter oben beschriebenen Ereignisse der CAN Kontrolleinheit),&lt;br /&gt;
&lt;br /&gt;
* zwei &#039;&#039;&#039;Hilfsprozeduren&#039;&#039;&#039;: &#039;&#039;XmitWait&#039;&#039; zum Warten auf die Vollendung des Sendens einer Nachricht (mit aktivierten Interrupts), und &#039;&#039;TimedWait&#039;&#039; zum Warten einer festgelegten Zeit (mit aktivierten Interrupts); &#039;&#039;TimedWait&#039;&#039; ist hier nur zur Illustration erwähnt, das Unterprogramm wird im Demobeispiel nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
Diese Unterprogramme sind im Modul Demo.c enthalten, in Demo.h werden alle global verwendbaren Definitionen vorgenommen.&lt;br /&gt;
&lt;br /&gt;
===Kommentare zum Hauptprogramm (main)===&lt;br /&gt;
&lt;br /&gt;
Das Programm erklärt sich im wesentlichen selbst: nach der Initialisierung startet das Hauptprogramm den &amp;quot;Watchdog Timer&amp;quot; (die &amp;quot;Idle-Loop&amp;quot; unternimmt alle Sekunden die nötigen periodischen Watchdog Aufrufe); danach wird eine Initialisierungszeile gedruckt und eine Nachricht auf den CAN Bus gelegt.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf von &#039;&#039;CanInit&#039;&#039; erfolgt im hier gezeigten Beispiel in einer vereinfachten Form: der Wert 0 des zweiten Arguments bedeutet, dass die Anwendung keine Filter definiert: das Programm &#039;&#039;CanInit&#039;&#039; erzeugt automatisch ein Default Filter, das die Kontrolleinheit alle Nachrichten in einem einzigen MOb empfangen lässt.&lt;br /&gt;
&lt;br /&gt;
Schliesslich durchläuft das Programm die &amp;quot;Idle-Loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Kommentare zum Unterprogramm IdleActions===&lt;br /&gt;
&lt;br /&gt;
Die Kontrollvariablen werden der Reihe nach auf das Vorliegen der verschiedenen Ereignisse abgefragt: wenn eine Ereignis-Variable (hier ein Bit in den Variablen &#039;&#039;can_status.cs_xmtOK&#039;&#039;, &#039;&#039;can_status.cs_rcvOK&#039;&#039; oder &#039;&#039;interrupt_flags&#039;&#039;) bei der  Behandlung eines Interrupts markiert worden ist, wird das entsprechende Ereignis abgearbeitet, die Markierung wird gelöscht.&lt;br /&gt;
&lt;br /&gt;
Die spezifischen Aktionen bei den einzelnen Ereignissen sind im folgenden kurz beschrieben.&lt;br /&gt;
: Ablauf der vorgewählten Warte-Periode:&lt;br /&gt;
:: Falls &#039;&#039;IdleActions&#039;&#039; vom &#039;&#039;TimedWait&#039;&#039; Unterprogramm aufgerufen wurde, Rückkehr zu diesem Unterprogramm - sonst keine Aktion.&lt;br /&gt;
&lt;br /&gt;
: Echtzeit-Zähler Sekundenschritt:&lt;br /&gt;
:: Sekundenzähler nachführen, &amp;quot;Watchdog Timer&amp;quot; aufrufen.&lt;br /&gt;
&lt;br /&gt;
: Empfang einer Nachricht vom CAN Bus:&lt;br /&gt;
:: Die Kontrollvariable &#039;&#039;can_status.cs_rcvOK&#039;&#039; enthält die Liste aller MObs (Liste von Bits), für die eine empfangene Nachricht wartet. Das Bit mit der höchsten Priorität in die Nummer (Nummern 0 ... 14) des entsprechenden MOb umwandeln, &#039;&#039;CanGetFrame&#039;&#039; für dieses MOb aufrufen, eine entsprechende Zeile drucken.&lt;br /&gt;
&lt;br /&gt;
: Abschluss des Sendens einer vom CAN Bus gesendeten Nachricht:&lt;br /&gt;
:: Falls &#039;&#039;IdleActions&#039;&#039; vom &#039;&#039;XmitWait&#039;&#039; Unterprogramm aufgerufen wurde, Rückkehr zu diesem Unterprogramm - sonst keine Aktion.&lt;br /&gt;
&lt;br /&gt;
: Andere Ereignisse (10 msec Taktgeber Ereignis):&lt;br /&gt;
:: Prüfen ob eine &amp;quot;hängengebliebene&amp;quot; Nachricht existiert und gegebenenfalls Löschen dieser Nachricht. Danach den Prozessor in den &amp;quot;Sleep&amp;quot; Zustand setzen.&lt;br /&gt;
&lt;br /&gt;
Die Befehlsfolge am Ende des Unterprogramms bezweckt, dass der Prozessor sicher und ohne Auslösen eines Interrupt sofort nach dem &amp;quot;sei&amp;quot; Befehl in den &amp;quot;Sleep&amp;quot; Zustand gesetzt wird - wie in der Dokumentation zu avr/sleep.h (AVR-Libc) beschrieben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
	cli () ;&lt;br /&gt;
	sleep_enable () ;&lt;br /&gt;
	sei () ;&lt;br /&gt;
	sleep_cpu () ;&lt;br /&gt;
	sleep_disable () ;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass ein neuer Durchlauf durch die Warteschleife erfolgt, auch wenn ein Interrupt genau am Ende der Warteschleife erfolgt.&lt;br /&gt;
&lt;br /&gt;
===Kommentare zu den Unterprogrammen XmitWait und TimedWait===&lt;br /&gt;
&lt;br /&gt;
Mithilfe dieser Unterprogramme kann die Anwendung das Eintreten eines Ereignisses abwarten, ohne dass dafür eine &amp;quot;busy-wait&amp;quot; Schleife programmiert werden muss.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;XmitWait&#039;&#039; startet eine neue Warteschleife und bleibt darin, bis das Senden der als Argument angegebenen MOb beendet ist. &#039;&#039;TimedWait&#039;&#039; startet eine neue Warteschleife und bleibt darin, bis die als Argument von &#039;&#039;TimedWait&#039;&#039; vorgegebene Zahl von 10-msec Intervallen abgelaufen ist.&lt;br /&gt;
&lt;br /&gt;
Alle auftretenden Ereignisse werden von der jeweils ausgeführten Warteschleife abgearbeitet (das ist die Warteschleife, die als letzte geöffnet wurde).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Adressierung und Filter==&lt;br /&gt;
&lt;br /&gt;
===Verwendung von CanInit zum Definieren von Filtern===&lt;br /&gt;
&lt;br /&gt;
Zum leichteren Verständnis verzichtet die oben beschriebene Demo-Anwendung auf den Einsatz von Filtern.&lt;br /&gt;
&lt;br /&gt;
Die folgenden Bemerkungen illustrieren, wie Filter verwendet werden können und sollen Anregungen sein, wie das Broadcast Konzept von CAN für die Bestimmung der Partner beim Austausch von Nachrichten sinnvoll eingesetzt werden könnte.&lt;br /&gt;
&lt;br /&gt;
Die beiden Argumente von &#039;&#039;CanInit&#039;&#039; erlauben, eine Tabelle mit Filtern zu definieren, welche dann von &#039;&#039;CanInit&#039;&#039; installiert werden. Wenn diese Tabelle in der Variablen &#039;&#039;filter_table&#039;&#039; definiert ist (sie muss im Flash Speicher liegen), sieht der Aufruf von CanInit so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    CanInit ( filter_table, sizeof (filter_table) / sizeof (can_filter) ) ;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Konzept für die Gestaltung des Adressraums, Beispiel===&lt;br /&gt;
&lt;br /&gt;
Bevor eine Filtertabelle erstellt wird, muss ein Konzept für die Gestaltung des Adressraums definiert werden. Im hier erläuterten Beispiel wurde das folgende Konzept gewählt:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Normale Adressen&#039;&#039;&#039; (11-Bit Werte) werden in zwei Zonen zerlegt:&lt;br /&gt;
* die oberen 3 Bits bestimmen eine Adress-Familie,&lt;br /&gt;
* die unteren 8 Bits unterscheiden spezifische Adressen innerhalb einer Familie,&lt;br /&gt;
* das oberste der 3 Bits in der ersten Zone teilt die Familien in 2 Gruppen - eine Gruppe mit hoher Priorität beim Senden, eine mit niedriger Priorität; die unteren 2 Bits definieren damit 4 Gruppen, die mit und ohne Priorität verwendet werden können (siehe auch die Beschreibung des &#039;&#039;&#039;MG_PRIORITY&#039;&#039;&#039; Bits im Abschnitt Datenstrukturen).&lt;br /&gt;
* im Modul Can.h werden Symbole mit Werten für die Familien definiert, z.B.:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
#define	MG_MASK		0x0700L		// message group mask&lt;br /&gt;
#define MG_PRIORITY	0x0400L		// priority bit (set if low priority)&lt;br /&gt;
&lt;br /&gt;
#define MG_BROADCAST	0x0400L		// low-priority broadcast&lt;br /&gt;
#define MG_ACTION	0x0500L		// low-priority action trigger&lt;br /&gt;
#define MG_MODIFY	0x0600L		// low-priority modify state request&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
* auch für die unteren 8 Bits ist es zweckmässig, Symbole zu definieren - hier sind ein paar Beispiele für Typen von Broadcast Nachrichten die ich verwende:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
enum {&lt;br /&gt;
	MB_TIME = 1,			// sending time and date&lt;br /&gt;
	MB_POLL,			// CPU status poll&lt;br /&gt;
	MB_ECHO,			// CPU status echo&lt;br /&gt;
	MB_CONFIG,			// Solicit configuration messages&lt;br /&gt;
	  ...&lt;br /&gt;
} ;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch &#039;&#039;&#039;erweiterte Adressen&#039;&#039;&#039; können in mehrere Zonen zerlegt werden - damit lässt sich Kontrollinformation im Nachrichtenkopf &amp;quot;verstecken&amp;quot; ohne die wertvollen 8 Datenbytes zu verwenden:&lt;br /&gt;
* Sequenznummer der Nachricht,&lt;br /&gt;
* Typ der Nachricht,&lt;br /&gt;
* Zieladresse.&lt;br /&gt;
&lt;br /&gt;
Ich habe vorgesehen (es aber bisher nicht gebraucht und daher noch nicht implementiert) dies für verbindungs-orientierte Kommunikation zu verwenden. Zurzeit setze ich erweiterte Adressen ein, um eine Nachricht an eine spezifische CPU zu senden und dabei zwischen verschiedenen Arten von Nachrichten zu unterscheiden.&lt;br /&gt;
&lt;br /&gt;
Die für eine derartige Verwendung des Adressraums nötige Programmierung ist Aufgabe der Anwendung: sie muss das &#039;&#039;mob_header&#039;&#039; Feld zum Senden entsprechend vorbereiten, bzw. beim Empfang entsprechend interpretieren - die CAN Bibliothek hat damit nichts zu tun, sie arbeitet auf einer tieferen &amp;quot;Schicht&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===Definition eines Satzes von Filtern, Beispiel===&lt;br /&gt;
&lt;br /&gt;
Eine Definition der Filtertabelle kann dann so aussehen:&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
// Can receive filter definition table&lt;br /&gt;
// -----------------------------------&lt;br /&gt;
//	Filter table item descriptors for channel 0 and on&lt;br /&gt;
//	Channels beyond those defined in this table are used for&lt;br /&gt;
//	transmission&lt;br /&gt;
&lt;br /&gt;
static can_filter	filter_table[] PROGMEM = {&lt;br /&gt;
//	  tag			    mask			tag_flags	mask_flags&lt;br /&gt;
&lt;br /&gt;
// high-priority broadcasts&lt;br /&gt;
    { MG_BROADCAST &amp;amp; ~MG_PRIORITY,  MG_MASK,			0,		_BV(RTRMSK) | _BV(IDEMSK) },&lt;br /&gt;
&lt;br /&gt;
// low- &amp;amp; high-priority broadcasts&lt;br /&gt;
    { MG_BROADCAST &amp;amp; ~MG_PRIORITY,  MG_MASK &amp;amp; ~MG_PRIORITY,	0,		_BV(RTRMSK) | _BV(IDEMSK) },&lt;br /&gt;
&lt;br /&gt;
// high-priority modify state requests&lt;br /&gt;
    { MG_MODIFY &amp;amp; ~MG_PRIORITY,	    MG_MASK,			0,		_BV(RTRMSK) | _BV(IDEMSK) },&lt;br /&gt;
&lt;br /&gt;
// low- &amp;amp; high-priority modify state req.&lt;br /&gt;
    { MG_MODIFY &amp;amp; ~MG_PRIORITY,	    MG_MASK &amp;amp; ~MG_PRIORITY,	0,		_BV(RTRMSK) | _BV(IDEMSK) },&lt;br /&gt;
&lt;br /&gt;
// extended messages (sequence numbers)&lt;br /&gt;
    { CPU_ID,			    0x000000FFL,	       	0,		_BV(RTRMSK) | _BV(IDEMSK) | CAN_EXTD },&lt;br /&gt;
&lt;br /&gt;
// all other messages&lt;br /&gt;
    { 0x00000000L,		    0x00000000L,		0,		_BV(RTRMSK)		  }  } ;&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ersten 4 Filter sind Paare von Filtern (je ein Filter mit hoher und mit niedriger Priorität) für die Gruppen mit Adressen der Familie MG_BROADCAST und MG_MODIFY, alle verwenden normale Adressen. Das fünfte Filter verwendet erweiterte Adressen und dient dem Empfang von Nachrichten nur für den betroffenen Prozessor (CPU_ID muss in Demo.h definiert sein). Das letzte Filter wird für den Empfang aller Nachrichten verwendet, die von den ersten 5 Filtern nicht angenommen worden sind.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
&lt;br /&gt;
===Referenzdokumente===&lt;br /&gt;
Hier sind zwei Artikel: (a) spezifischen Fragen zum CAN Zugriff mittels AT90CAN Prozessoren, und (b) ein Tutorial zur praktischen Implementierung des Zugriffs auf einen CAN Bus von einem AVR Prozessor - auch wenn er den AT90CAN &amp;quot;umgeht&amp;quot; ist er eine hervorragende Einführung zu Fragen der praktischen Implementierung des Zugriffs von einem AVR Prozessor auf einen CAN Bus:&lt;br /&gt;
* [http://www.avrfreaks.net/modules/FreaksArticles/files/23/CAN%20on%20the%20AVR.pdf CAN on the AVR (Mike Blendermann)]&lt;br /&gt;
* [http://www.kreatives-chaos.com/artikel/ansteuerung-eines-mcp2515 Ansteuerung des MCP2515 (Tutorial, Fabian Greif)]&lt;br /&gt;
&lt;br /&gt;
Daneben gibt es Tonnen von Artikeln zu CAN, die aufzuzählen hier fehl am Platze wäre.&lt;br /&gt;
&lt;br /&gt;
===Dateien mit dem Quelltext der Bibliotheksprogramme===&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Can.c Can.c - V1.0.1, korrigiert 30.07.2009]&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Can.h Can.h]&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Demo.c Demo.c]&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Demo.h Demo.h]&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Uart.c Uart.c]&lt;br /&gt;
* [http://cui.unige.ch/~harms/canlib/Makefile Makefile]&lt;br /&gt;
&lt;br /&gt;
:: &#039;&#039;&#039;[[CAN_Bibiliothek_f%C3%BCr_AT90CAN_Prozessoren#top|Zurück zum Inhaltsverzeichnis]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:AVR-Projekte]]&lt;br /&gt;
[[Category:CAN]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pollin_Funk-AVR-Evaluationsboard&amp;diff=39346</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=39346"/>
		<updated>2009-09-25T14:55:34Z</updated>

		<summary type="html">&lt;p&gt;84.58.239.213: /* 8-Bit Zähler mit RS232-Anschluss */ Layout&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.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;
== 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;c&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;/c&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;c&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;/c&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;c&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;/c&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.B. HyperTerm gemacht, so dass hier keine Programmierung notwendig ist. Lediglich die Einstellung der RS232-Schnittstelle (z.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.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.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;c&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.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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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;c&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.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;/c&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;c&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;/c&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;c&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;/c&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;c&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.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;/c&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;c&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;/c&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;c&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;/c&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;c&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;/c&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.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;
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 Ohm [[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 Ohm 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;c&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;/c&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;c&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;/c&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;c&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;/c&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.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;c&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.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;/c&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;c&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;/c&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;c&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;/c&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;c&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 Ohm =&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;/c&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;c&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;/c&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;
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;c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
// #include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
* Anpassen des Eingangspins für das Signal vom TSOP1736. &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define xRC5 PD2 // IR input low active (statt PD7)&lt;br /&gt;
&amp;lt;/c&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!&lt;br /&gt;
&amp;lt;c&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;/c&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;c&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;/c&amp;gt;&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;c&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;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Änderungen in rc5.c&#039;&#039;&#039;:&lt;br /&gt;
* Einbau der DEBUG-LED. Dazu wird folgendes Codestück zwischen #if 1 und #endif ergänzt. &lt;br /&gt;
&amp;lt;c&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;/c&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, 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;
== 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] und [http://www.mikrocontroller.net/topic/122551#1118124]&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;
== 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:Funk]]&lt;/div&gt;</summary>
		<author><name>84.58.239.213</name></author>
	</entry>
</feed>