<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PicNick</id>
		<title>RN-Wissen.de - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PicNick"/>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Spezial:Beitr%C3%A4ge/PicNick"/>
		<updated>2026-04-11T20:44:51Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.25.1</generator>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18592</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18592"/>
				<updated>2011-08-27T17:54:07Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Daten-Bäume */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleich große Elemente hintereinander im Speicher angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei C (z.B. GCC) beginnt der Index bei „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmäßigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen).&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines bereits in der Liste verketteten Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
&amp;quot;PREV&amp;quot; ist der Vorgänger des Elements, &amp;quot;NEXT&amp;quot; dessen Nachfolger&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich bin auch darauf hingewiesen worden, dass die Verwendung von verketteten Listen durchaus nicht in allen Fällen die beste Methode ist, irgendein Problem zu lösen. Gerade bei &amp;quot;Queues&amp;quot; u.ä sind Ringpuffer wohl eher µC-Style. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
===&amp;quot;sliding window&amp;quot;===&lt;br /&gt;
Wenn fortlaufend Messages kommen und ich aber nur ein &amp;quot;Beobachtungs-fenster&amp;quot; von ein paar Messages habe, dann bewegt sich dieses Fenster bildlich über die Messages (oder andere Ereignisse) hinweg. Das ist dann eben ein &amp;quot;gleitendes Fenster&amp;quot; oder eben &amp;quot;Sliding window&amp;quot;. Ist wohl am ehesten bekannt bei Kommunikations-Protokollen. Damit nicht jede Message einzeln bestätigt werden muss, werden Messages zwar fortlaufend gesendet, aber von der letzten bestätigten Paket-Nummer an gespeichert. Eine ev. angeforderte Sendewiederholung kann sich nur auf eine Stelle in diesem &amp;quot;Message-Fenster&amp;quot; beziehen und dann ist diese Message eben noch greifbar. Kommt aber eine Bestätigung für eine Message-Nummer innerhalb des Fensters, werden diese Message und alle davor endgültig verworfen, das Fenster wird wieder kleiner. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitendes Mittel“===&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weniger detailliert, aber besser für den Überblick kann das dann so aussehen&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tree1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Tree1.JPG&amp;diff=18591</id>
		<title>Datei:Tree1.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Tree1.JPG&amp;diff=18591"/>
				<updated>2011-08-27T17:52:15Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18590</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18590"/>
				<updated>2011-08-27T17:51:13Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Daten-Bäume */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleich große Elemente hintereinander im Speicher angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei C (z.B. GCC) beginnt der Index bei „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmäßigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen).&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines bereits in der Liste verketteten Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
&amp;quot;PREV&amp;quot; ist der Vorgänger des Elements, &amp;quot;NEXT&amp;quot; dessen Nachfolger&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich bin auch darauf hingewiesen worden, dass die Verwendung von verketteten Listen durchaus nicht in allen Fällen die beste Methode ist, irgendein Problem zu lösen. Gerade bei &amp;quot;Queues&amp;quot; u.ä sind Ringpuffer wohl eher µC-Style. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
===&amp;quot;sliding window&amp;quot;===&lt;br /&gt;
Wenn fortlaufend Messages kommen und ich aber nur ein &amp;quot;Beobachtungs-fenster&amp;quot; von ein paar Messages habe, dann bewegt sich dieses Fenster bildlich über die Messages (oder andere Ereignisse) hinweg. Das ist dann eben ein &amp;quot;gleitendes Fenster&amp;quot; oder eben &amp;quot;Sliding window&amp;quot;. Ist wohl am ehesten bekannt bei Kommunikations-Protokollen. Damit nicht jede Message einzeln bestätigt werden muss, werden Messages zwar fortlaufend gesendet, aber von der letzten bestätigten Paket-Nummer an gespeichert. Eine ev. angeforderte Sendewiederholung kann sich nur auf eine Stelle in diesem &amp;quot;Message-Fenster&amp;quot; beziehen und dann ist diese Message eben noch greifbar. Kommt aber eine Bestätigung für eine Message-Nummer innerhalb des Fensters, werden diese Message und alle davor endgültig verworfen, das Fenster wird wieder kleiner. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitendes Mittel“===&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18589</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18589"/>
				<updated>2011-08-27T17:49:02Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Anwendungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleich große Elemente hintereinander im Speicher angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei C (z.B. GCC) beginnt der Index bei „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmäßigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen).&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines bereits in der Liste verketteten Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
&amp;quot;PREV&amp;quot; ist der Vorgänger des Elements, &amp;quot;NEXT&amp;quot; dessen Nachfolger&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich bin auch darauf hingewiesen worden, dass die Verwendung von verketteten Listen durchaus nicht in allen Fällen die beste Methode ist, irgendein Problem zu lösen. Gerade bei &amp;quot;Queues&amp;quot; u.ä sind Ringpuffer wohl eher µC-Style. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
===&amp;quot;sliding window&amp;quot;===&lt;br /&gt;
Wenn fortlaufend Messages kommen und ich aber nur ein &amp;quot;Beobachtungs-fenster&amp;quot; von ein paar Messages habe, dann bewegt sich dieses Fenster bildlich über die Messages (oder andere Ereignisse) hinweg. Das ist dann eben ein &amp;quot;gleitendes Fenster&amp;quot; oder eben &amp;quot;Sliding window&amp;quot;. Ist wohl am ehesten bekannt bei Kommunikations-Protokollen. Damit nicht jede Message einzeln bestätigt werden muss, werden Messages zwar fortlaufend gesendet, aber von der letzten bestätigten Paket-Nummer an gespeichert. Eine ev. angeforderte Sendewiederholung kann sich nur auf eine Stelle in diesem &amp;quot;Message-Fenster&amp;quot; beziehen und dann ist diese Message eben noch greifbar. Kommt aber eine Bestätigung für eine Message-Nummer innerhalb des Fensters, werden diese Message und alle davor endgültig verworfen, das Fenster wird wieder kleiner. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitendes Mittel“===&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18587</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18587"/>
				<updated>2011-08-27T17:22:11Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines bereits in der Liste verketteten Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
&amp;quot;PREV&amp;quot; ist der Vorgänger des Elements, &amp;quot;NEXT&amp;quot; dessen Nachfolger&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18586</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18586"/>
				<updated>2011-08-27T17:17:41Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines bereits in der Liste verketteten Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18585</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18585"/>
				<updated>2011-08-27T17:16:31Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Liste mit vier Elementen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines (List- oder Element-) Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18584</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18584"/>
				<updated>2011-08-27T17:15:35Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines (List- oder Element-) Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwende ich als &amp;quot;POSITION&amp;quot; die Adresse des LIst-Headers, entspricht:&amp;lt;br/&amp;gt;&lt;br /&gt;
'''Vor''' dem Header ==&amp;gt; eintragen als letztes Element &lt;br /&gt;
'''Nach''' dem Header ==&amp;gt; eintragen als erstes Element &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18583</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18583"/>
				<updated>2011-08-27T17:11:41Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen. Zur Erläuterung:&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;quot;POSITION&amp;quot; ist die Adresse eines (List- oder Element-) Headers. &lt;br /&gt;
&amp;quot;NEW&amp;quot; ist die Adresse eines neuen Elements bzw. dessen Headers&lt;br /&gt;
&amp;quot;-.FWD&amp;quot; ist der &amp;quot;vorwärts&amp;quot;-Verweis eines dieser Header&lt;br /&gt;
&amp;quot;-.BCK&amp;quot; ist der &amp;quot;rückwärts&amp;quot;-Verweis&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''vor''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**Einfügen &amp;quot;NEW&amp;quot; '''nach''' einem anderen Element (&amp;quot;POSITION&amp;quot;) &amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit vier Elementen&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''vor dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18582</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18582"/>
				<updated>2011-08-27T16:57:20Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Doppelt verkettete Listen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jeder Element-Header zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls.&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18581</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18581"/>
				<updated>2011-08-27T16:55:45Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Erzeugen einer leeren Liste. List-Header werden die Verweise für erstes und letztes Element auf NULL gesetzt &lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen in die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Einfügen an erster Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header aber auch als letztes eingetragen werden.&lt;br /&gt;
** Einfügen an letzter Stelle, gegeben sind Head und das neue Element. Ist das neue Element gleichzeitig das erste der Liste, muss es im Header dann auch als erstes eingetragen werden.&lt;br /&gt;
** Einfügen eines neuen Elements als Nachfolger eine anderen. Wird das neue Element aber dadurch das neue letzte Element, muss man das im List-Header korrigieren &lt;br /&gt;
* Abfragen nach Element-Adressen&lt;br /&gt;
** Abfrage im List-Header nach dem ersten Element&lt;br /&gt;
** Abfrage im List-Header nach dem letzten Element&lt;br /&gt;
** Abfrage im Element-Header nach dem nächsten Element&lt;br /&gt;
* Entfernen (ausketten) aus der Liste (die Nutzdaten bleiben dabei aber erhalten)&lt;br /&gt;
** Entfernen des ersten Elements. Man fragt den List-Header nach dem ersten Element. Dessen Nachfolger wird dann im List-Header das neue erste Element. Gibt's diesen Nachfolger aber garnicht, dann ist die Liste nun leer, also muss man den List-Header initialisieren (s.o)&lt;br /&gt;
** Entfernen des letzten Elements. Das geht nur, wenn ich auch das bisher vorletze kenne, um es im List-Header nun als neue letztes zu verzeichnen.&lt;br /&gt;
** Entfernen eines beliebigen Elements. Auch da muss ich den Vorgänger kennen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18580</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18580"/>
				<updated>2011-08-27T16:21:46Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Einfach verkettete Listen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält im Header nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen ind die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Insert Head (HEAD, NEW) Einfügen an erster Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header auch als letztes eingetragen werden (verwirrt?).&lt;br /&gt;
** Insert Tail (HEAD, NEW) Einfügen an letzter Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste der Liste, muss es im Header auch als erstes eingetragen werden (noch mehr verwirrt?).&lt;br /&gt;
** Insert Behind (POSITION, NEW) Einfügen als Nachfolger, gegeben sind Vorgänger und das neue Element. Da es offenbar einen Vorgänger gibt, ist die Liste nicht leer. Das neue Element kann aber auch das neue letzte Element sein, das man in den Header dann als solches eintragen muss. &lt;br /&gt;
* FIRST = Read First(HEAD) Lesen des ersten Elements, angegeben wird List-Head.&lt;br /&gt;
* NEXT = Read Next(POSITION) Lesen des nächsten Elements, angegeben wird das aktuelle Element.&lt;br /&gt;
* Entfernen&lt;br /&gt;
** Remove First(HEAD) Entfernen des ersten Elements, Angabe List-Head. Auch hier ist zu prüfen, ob dieses Element das letzte ist in der Liste ist. Man muss dan den List-Header entsprechend korrigieren. &lt;br /&gt;
** Remove(POSITION, PREV) Entfernen eines beliebigen Elements. Das geht nur, wenn der Vorgänger gegeben ist. Gibt es diesen, kann das entfernte Element nicht das erste sein. Es kann aber das letzte sein, dann muss man den Vorgänger entsprechend im Header eintragen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18579</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18579"/>
				<updated>2011-08-27T16:19:13Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Liste / Tabelle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen.&amp;lt;br/&amp;gt;&lt;br /&gt;
In einer verketteten Liste kann das Auffinden eines bestimmten Elements nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte Element gefunden hat.&amp;lt;br/&amp;gt;&lt;br /&gt;
Wenn die Elemente umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden. Daher gibt es: &lt;br /&gt;
* einen &amp;quot;ListHeader&amp;quot;, der einen festen Platz in der Gesamtdatenstruktur hat und auf das das Anfangselement verweist. &lt;br /&gt;
* Jeder Datenelement bekommt zusätzlich zu seinen eigentlichen Nutzdaten einen &amp;quot;Element-Header&amp;quot;.&lt;br /&gt;
Dieser Element-Header befindet sich zweckmässigerweise ganz vorne in seiner Struktur. Dadurch ist die Adresse des Elements gleichzeitig auch die Adresse dieses Headers. Die Listen-Manipulations-Routinen brauchen ja für ihre Funktionen nur den jeweiligen Header und kümmern sich nicht um die übrigen Daten. Aus diesem Grunde können die Listen-Elemente auch völlig unterschiedlich lang sein und völlig unterschiedliche Strukturen haben.&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
In diesem Artikel sind nur die Header und deren Inhalte das Thema, die &amp;quot;Nutzdaten&amp;quot; sind aus dieser Sicht nur Anhängsel und werden in den Bildern wohl etwas vernachlässigt (oder gar tw. weggelassen)&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen ind die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Insert Head (HEAD, NEW) Einfügen an erster Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header auch als letztes eingetragen werden (verwirrt?).&lt;br /&gt;
** Insert Tail (HEAD, NEW) Einfügen an letzter Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste der Liste, muss es im Header auch als erstes eingetragen werden (noch mehr verwirrt?).&lt;br /&gt;
** Insert Behind (POSITION, NEW) Einfügen als Nachfolger, gegeben sind Vorgänger und das neue Element. Da es offenbar einen Vorgänger gibt, ist die Liste nicht leer. Das neue Element kann aber auch das neue letzte Element sein, das man in den Header dann als solches eintragen muss. &lt;br /&gt;
* FIRST = Read First(HEAD) Lesen des ersten Elements, angegeben wird List-Head.&lt;br /&gt;
* NEXT = Read Next(POSITION) Lesen des nächsten Elements, angegeben wird das aktuelle Element.&lt;br /&gt;
* Entfernen&lt;br /&gt;
** Remove First(HEAD) Entfernen des ersten Elements, Angabe List-Head. Auch hier ist zu prüfen, ob dieses Element das letzte ist in der Liste ist. Man muss dan den List-Header entsprechend korrigieren. &lt;br /&gt;
** Remove(POSITION, PREV) Entfernen eines beliebigen Elements. Das geht nur, wenn der Vorgänger gegeben ist. Gibt es diesen, kann das entfernte Element nicht das erste sein. Es kann aber das letzte sein, dann muss man den Vorgänger entsprechend im Header eintragen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18578</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18578"/>
				<updated>2011-08-27T15:55:48Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Grundsätzliche Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden,  daher gibt es üblicherweise einen „Header“, der zumindest auf das das Anfangselement verweist. &lt;br /&gt;
Listen-Elemente können auch unterschiedlich lang sein, dann muss oder sollte jedes Element auch eine Längenangabe enthalten (Bei Strings reicht natürlich ev. auch der „NUL“ Terminator).  Das Auffinden eines bestimmten Elements kann hier aber nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte gefunden hat.&lt;br /&gt;
Wenn die Element umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen ind die ob. Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Insert Head (HEAD, NEW) Einfügen an erster Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header auch als letztes eingetragen werden (verwirrt?).&lt;br /&gt;
** Insert Tail (HEAD, NEW) Einfügen an letzter Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste der Liste, muss es im Header auch als erstes eingetragen werden (noch mehr verwirrt?).&lt;br /&gt;
** Insert Behind (POSITION, NEW) Einfügen als Nachfolger, gegeben sind Vorgänger und das neue Element. Da es offenbar einen Vorgänger gibt, ist die Liste nicht leer. Das neue Element kann aber auch das neue letzte Element sein, das man in den Header dann als solches eintragen muss. &lt;br /&gt;
* FIRST = Read First(HEAD) Lesen des ersten Elements, angegeben wird List-Head.&lt;br /&gt;
* NEXT = Read Next(POSITION) Lesen des nächsten Elements, angegeben wird das aktuelle Element.&lt;br /&gt;
* Entfernen&lt;br /&gt;
** Remove First(HEAD) Entfernen des ersten Elements, Angabe List-Head. Auch hier ist zu prüfen, ob dieses Element das letzte ist in der Liste ist. Man muss dan den List-Header entsprechend korrigieren. &lt;br /&gt;
** Remove(POSITION, PREV) Entfernen eines beliebigen Elements. Das geht nur, wenn der Vorgänger gegeben ist. Gibt es diesen, kann das entfernte Element nicht das erste sein. Es kann aber das letzte sein, dann muss man den Vorgänger entsprechend im Header eintragen.&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18575</id>
		<title>Menüs</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18575"/>
				<updated>2011-08-26T18:10:27Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das ist offensichtlich noch eine Baustelle !&lt;br /&gt;
&lt;br /&gt;
=Menüs=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Handelt es sich um ein einfaches Menü, d.h. eine Reihe von Parametern ohne Sub-Menus, kann man durchaus mit einer für die benötigten Werte angepassten „Select Case“ bzw. „switch“ Konstruktion sein Auslangen finden.  &lt;br /&gt;
&lt;br /&gt;
Die folgenden Überlegungen betreffen aber sozusagen den „worst case“, das ist einen regelrechter Menübaum mit unterschiedlichen und verschiedenartigen Parametern, Parameter-Werten und Submenüs in unterschiedlicher Tiefe. &lt;br /&gt;
Wenn man da nicht halbwegs strukturiert vorgeht, schreibt man, bevor man sich’s versieht, seitenweise Code, und das bei jeder neuen Anwendung wieder neu, da ja doch dieses oder jenes verschieden ist.&lt;br /&gt;
&lt;br /&gt;
Worauf ich mich '''nicht''' einlassen werde, ist, wie und nach welchen Gesichtspunkten man Parameter zu Sub-Menüs zusammenfasst, um eine &amp;quot;benutzerfreundliche&amp;quot; Bedienung des Ganzen möglich zu machen. Ich hab zu Hause hoch-professionelle Geräte, wo ich erst nach einigem Suchen die Einstellwerte finde, die ich grade brauche. Dieses Design ist also auch nicht so einfach.  &lt;br /&gt;
&lt;br /&gt;
==Menubaum==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Menu0.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier ist ein Auschnitt aus so ein Baum dargestellt.  Ganz oben ist der Einstiegspunkt, in der ersten Ebene darunter findet man gemischt „Param“, da sind einzelne EinstellWerte, und „Submenu“, da geht’s eine Ebene weiter runter, wo sich wieder, in anderer Folge, andere Parameter und andere Submenus befinden. Das wiederholt sich immer weiter.&amp;lt;br/&amp;gt;&lt;br /&gt;
Dass ich in der Zeichnung &amp;quot;Param&amp;quot; und dazugehörigen &amp;quot;Value&amp;quot; (Einstellwert) getrennt habe, hat strukturelle Gründe. &lt;br /&gt;
&lt;br /&gt;
Wenn man das &amp;quot;Top&amp;quot;-Menu und die erste Ebene darunter betrachtet, erkennt man eine typische Anwendung für &amp;quot;verkettete Listen&amp;quot;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18574</id>
		<title>Menüs</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18574"/>
				<updated>2011-08-26T17:08:10Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Menüs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Menüs=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Handelt es sich um ein einfaches Menü, d.h. eine Reihe von Parametern ohne Sub-Menus, kann man durchaus mit einer für die benötigten Werte angepassten „Select Case“ bzw. „switch“ Konstruktion sein Auslangen finden.  &lt;br /&gt;
&lt;br /&gt;
Die folgenden Überlegungen betreffen aber sozusagen den „worst case“, das ist einen regelrechter Menübaum mit unterschiedlichen und verschiedenartigen Parametern, Parameter-Werten und Submenüs in unterschiedlicher Tiefe. &lt;br /&gt;
Wenn man da nicht halbwegs strukturiert vorgeht, schreibt man, bevor man sich’s versieht, seitenweise Code, und das bei jeder neuen Anwendung wieder neu, da ja doch dieses oder jenes verschieden ist.&lt;br /&gt;
&lt;br /&gt;
Worauf ich mich '''nicht''' einlassen werde, ist, wie und nach welchen Gesichtspunkten man Parameter zu Sub-Menüs zusammenfasst, um eine &amp;quot;benutzerfreundliche&amp;quot; Bedienung des Ganzen möglich zu machen. Ich hab zu Hause hoch-professionelle Geräte, wo ich erst nach einigem Suchen die Einstellwerte finde, die ich grade brauche. Dieses Design ist also auch nicht so einfach.  &lt;br /&gt;
&lt;br /&gt;
==Menubaum==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Menu0.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Hier ist ein Auschnitt aus so ein Baum dargestellt.  Ganz oben ist der Einstiegspunkt, in der ersten Ebene darunter findet man gemischt „Param“, da sind einzelne EinstellWerte, und „Submenu“, da geht’s eine Ebene weiter runter, wo sich wieder, in anderer Folge, andere Parameter und andere Submenus befinden. Das wiederholt sich immer weiter.&amp;lt;br/&amp;gt;&lt;br /&gt;
Dass ich in der Zeichnung &amp;quot;Param&amp;quot; und dazugehörigen &amp;quot;Value&amp;quot; (Einstellwert) getrennt habe, hat strukturelle Gründe. &lt;br /&gt;
&lt;br /&gt;
Wenn man das &amp;quot;Top&amp;quot;-Menu und die erste Ebene darunter betrachtet, erkennt man eine typische Anwendung für &amp;quot;verkettete Listen&amp;quot;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18573</id>
		<title>Menüs</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Men%C3%BCs&amp;diff=18573"/>
				<updated>2011-08-26T16:47:45Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Menüs=&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Diskussion:Rutscherle_-_selbstbalancierender_Elektroroller&amp;diff=18572</id>
		<title>Diskussion:Rutscherle - selbstbalancierender Elektroroller</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Diskussion:Rutscherle_-_selbstbalancierender_Elektroroller&amp;diff=18572"/>
				<updated>2011-08-26T16:41:44Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hallo Leute,&lt;br /&gt;
&lt;br /&gt;
Der Artikel ist im Moment in Bearbeitung. Ihr könnt mir hier aber eine ToDo-Liste hinterlassen.&lt;br /&gt;
Dann weis ich was ich zuerst bearbeiten soll.&lt;br /&gt;
&lt;br /&gt;
'''ToDo-Liste:'''&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;strike&amp;gt;Schaltbilder Hauptsteuerung&amp;lt;/strike&amp;gt;&lt;br /&gt;
# &amp;lt;strike&amp;gt;Software Hauptsteuerung&amp;lt;/strike&amp;gt;&lt;br /&gt;
# &amp;lt;strike&amp;gt;Tacho&amp;lt;/strike&amp;gt;&lt;br /&gt;
# Bilder vom Tacho&lt;br /&gt;
# &amp;lt;strike&amp;gt;Mechanischer Aufbau mit Detailfotos&amp;lt;/strike&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Du hast meine volle Bewunderung. Aber in den Programmen die fixen Angaben ($100 etc) bei den Speicheradressen gehen mir überhaupt nicht unter die Nase und sind nicht wartungsfreundlich. Gibt's einen bestimmten Grund dafür, sowas in Kauf zu nehmen ? Wenn um Missverständnisse geht, wie man Variablen gemeinschaftlich von Bascom und Assembler benutzt, könnt ich dir schon ein paar Tips geben (wenn gewünscht, denn dreinreden will ich dir nicht) --PicNick 18:41, 26. Aug 2011 (CEST)&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18571</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18571"/>
				<updated>2011-08-26T16:10:47Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden,  daher gibt es üblicherweise einen „Header“, der zumindest auf das das Anfangselement verweist. &lt;br /&gt;
Listen-Elemente können auch unterschiedlich lang sein, dann muss oder sollte jedes Element auch eine Längenangabe enthalten (Bei Strings reicht natürlich ev. auch der „NUL“ Terminator).  Das Auffinden eines bestimmten Elements kann hier aber nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte gefunden hat.&lt;br /&gt;
Wenn die Element umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
*Verweis = makeVerweis(Adresse)&lt;br /&gt;
*Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
===Liste mit zwei Elementen===&lt;br /&gt;
Der List-Header verweist auf das erste Element und dieses auf das nächste, das durch den NULL-Verweis zeigt, dass es auch das letzte Element der Liste ist. &lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Einfügen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
** Insert Head (HEAD, NEW) Einfügen an erster Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste in der Liste, muss es im Header auch als letztes eingetragen werden (verwirrt?).&lt;br /&gt;
** Insert Tail (HEAD, NEW) Einfügen an letzter Stelle, gegeben sind Head und das neue Element.Ist das neue Element gleichzeitig das erste der Liste, muss es im Header auch als erstes eingetragen werden (noch mehr verwirrt?).&lt;br /&gt;
** Insert Behind (POSITION, NEW) Einfügen als Nachfolger, gegeben sind Vorgänger und das neue Element. Da es offenbar einen Vorgänger gibt, ist die Liste nicht leer. Das neue Element kann aber auch das neue letzte Element sein, das man in den Header dann als solches eintragen muss. &lt;br /&gt;
* FIRST = Read First(HEAD) Lesen des ersten Elements, angegeben wird List-Head.&lt;br /&gt;
* NEXT = Read Next(POSITION) Lesen des nächsten Elements, angegeben wird das aktuelle Element.&lt;br /&gt;
* Entfernen&lt;br /&gt;
** Remove First(HEAD) Entfernen des ersten Elements, Angabe List-Head. Auch hier ist zu prüfen, ob dieses Element das letzte ist in der Liste ist. Man muss dan den List-Header entsprechend korrigieren. &lt;br /&gt;
** Remove(POSITION, PREV) Entfernen eines beliebigen Elements. Das geht nur, wenn der Vorgänger gegeben ist. Gibt es diesen, kann das entfernte Element nicht das erste sein. Es kann aber das letzte sein, dann muss man den Vorgänger entsprechend im Header eintragen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. Dafür ist natürlich der Aufwand grösser.&amp;lt;br/&amp;gt;&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, '''keine''' NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher (und sicherer), weil sie kein Wenn und Aber brauchen.&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die Vorwärts- und Rückwärts-Kett-Felder haben nun auch die Elemente einen wirklichen Header, der dafür aber völlig identisch mit dem List-Header ist. &lt;br /&gt;
* Header&lt;br /&gt;
** Einen Verweis auf  das nächste Element  (beim List-Header ist das das erste)&lt;br /&gt;
** einen Verweis auf das vorherige Element (beim List-Header ist das das letzte)&lt;br /&gt;
&lt;br /&gt;
===Liste mit vier Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Grundsätzliche Methoden==&lt;br /&gt;
* Init Head (HEAD)Erzeugen einer leeren Liste. Gegeben ist List-Head.&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst.&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
*Einfügen&lt;br /&gt;
Durch die Gleichsetzung von List- und Element-Header und den &amp;quot;Eigenverweis&amp;quot; im Header einer leeren Liste, gibt es nur zwei Einfügefunktionen:&lt;br /&gt;
**'''vor''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV		= makeAdresse(NEW.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als letztes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
**'''nach''' einem anderen Element. Gegeben das neue Element und ein anderes Element (oder der List-Header)&amp;lt;br/&amp;gt; &lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEW.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT		= makeAdresse(NEW.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK	= makeVerweis(NEW)&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Angabe der List-Headers als Einfügeposition entspricht das dem &amp;quot;Einfügen als erstes Element&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Hier das Beispiel &amp;quot;Einfügen irgendwo&amp;quot;, wobei das Gleiche rauskommt, wenn ich '''nach''' dem zweiten oder '''ovr dem dritten einfüge&lt;br /&gt;
**Vorher&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
**Nachher&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
* Entfernen gibt es nur in einer Version. Gegeben immer das Element, das gemeint ist (=POSITION).&amp;lt;br/&amp;gt; &lt;br /&gt;
PREV			= POSITION.BCK&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT			= POSITION.FWD&amp;lt;br/&amp;gt;&lt;br /&gt;
NEXT.BCK		= makeVerweis(PREV)&amp;lt;br/&amp;gt;&lt;br /&gt;
PREV.FWD		= makeVerweis(NEXT)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional, nur der Ordnung halber. dadurch ist das entfernte Element auch als &amp;quot;keiner Liste zugehörig&amp;quot; gekennzeichnet&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
POSITION.BCK		= makeVerweis(POSITION)&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Was man vermeiden sollte: Ein „Remove“ des Headers würde zwar technisch funktionieren, aber alle List-Elemente wären dann verwaist und nicht mehr zu finden, weil sie keinen Header mehr haben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Lesen Nächstes. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das erste Element werden soll&lt;br /&gt;
&lt;br /&gt;
NEXT		= makeAdresse(POSITION.FWD)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist NEXT gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
*Lesen Vorgänger. Gegeben der Listheader und &lt;br /&gt;
**das zuletzt gelesene Element oder&lt;br /&gt;
**nochmal der Listheader, wenn es das letzte Element werden soll&lt;br /&gt;
&lt;br /&gt;
PREV		= makeAdresse(POSITION.BCK)&amp;lt;br/&amp;gt;&lt;br /&gt;
ist PREV gleich HEAD, dann gibt es kein weiteres Element (end-of-list)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
==Anwendungen==&lt;br /&gt;
&lt;br /&gt;
Man kann nicht generell sagen, ob man für irgendeine Anwendung mit einer einfach verketteten Liste auskommt oder unbedingt doppelt verketten muss. Die fehlende Rückwärtsverkettung lässt sich mit etwas Befehlscode eigentlich auch immer ersetzen. Man muss einen Kompromiss zwischen Performance und Speicherverbrauch finden und ggf. entscheiden, was im konkreten Fall wichtiger ist. &lt;br /&gt;
&lt;br /&gt;
===Warteschlangen (queues)===&lt;br /&gt;
&lt;br /&gt;
Beim Aufbau von „Warteschlangen“ wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste entfernt (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===„Gleitende Statistik“===&lt;br /&gt;
(gibt's da nicht einen fach-chinesischen Ausdruck dafür ?)&lt;br /&gt;
Ganz gleich funktioniert es, wenn man z.B. immer nur die, sagen wir, 10 letzten Ergebnisse eines Sensors speichern und einen aktuellen Durchschnittswert errechnen will.  &lt;br /&gt;
Dann wird das neueste Ergebnis hinten eingefügt und der Messwert auf eine Gesamtsumme addiert.&lt;br /&gt;
Wenn es nun mehr als zehn Ereignisse sind, entfernt man das bisher erste Element und zieht diesen Wert von der Summe ab. Der aktuelle Durchschnittswert ist dann immer Summe / Elementanzahl. &lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
===Daten-Bäume===&lt;br /&gt;
Da ich ja in jedem List-Element auch wieder einen List-Header hineinschreiben und dort wiederum andere Elemente einfügen kann, habe ich so die Möglichkeit, einen „Datenbaum“ zu erstellen bzw. zu bearbeiten. Die Directory-struktur bei einer Hard-Disk ist zum Beispiel so ein Baum. &lt;br /&gt;
Auch ein „Menü“-Baum kann so dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
==Kombination Tabelle/Liste==&lt;br /&gt;
Man kann natürlich auch die Elemente einer Tabelle verketten. Damit entsteht neben der eigentlichen Tabellen-Index-Reihenfolge eine weitere „logische“ Reihenfolge. &lt;br /&gt;
Wenn ich also eine Tabelle umsortieren will, lasse ich die Elemente, wo sie sind, und ändere nur die Verkettung. &lt;br /&gt;
Als ListHeader brauch ich dann den Index der ersten und ev. letzen Elements. &lt;br /&gt;
Da ist auch gleich so ein Fall, wo ein „Verweis“ keine wirkliche Adresse ist, sondern nur ein Index für ein anderes Element. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18568</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18568"/>
				<updated>2011-08-26T13:56:12Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Liste / Tabelle==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle (Array) sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden,  daher gibt es üblicherweise einen „Header“, der zumindest auf das das Anfangselement verweist. &lt;br /&gt;
Listen-Elemente können auch unterschiedlich lang sein, dann muss oder sollte jedes Element auch eine Längenangabe enthalten (Bei Strings reicht natürlich ev. auch der „NUL“ Terminator).  Das Auffinden eines bestimmten Elements kann hier aber nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte gefunden hat.&lt;br /&gt;
Wenn die Element umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, bei einer Tabelle muss man die gesamten Daten verschieben. Ebenso, wenn man einzelne Element entfernen oder einfügen will.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Verweise/Adressen==&lt;br /&gt;
Ich werde in der Folge immer wieder unterscheiden zwischen „Verweis“ und „Adresse“ . Das wird zwar in vielen Fällen ein und dasselbe sein, aber eben nicht unbedingt. &lt;br /&gt;
Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist die Verwendung der Adresse als Verweis sicher eine praktikable Methode. &lt;br /&gt;
Auf einem µC reicht meistens eine 16-Bit (2Byte) Adresse, um eine 64k memory abzudecken. Aber wenn einem die 2 Byte für den Verweis je Element zu viel sind oder die 64k Adressraum zu wenig, muss man sich was überlegen.  &lt;br /&gt;
Das ist aber auch ein eigenes Thema. &lt;br /&gt;
Bei meinen diversen Pseudo-Codes werde ich daher der Ordnung halber zwei Hilfs-Funktionen verwenden, die aus einem Verweis eine Adresse und umgekehrt machen. Ich gehe aber nicht drauf ein, wie diese Funktionen genau beschaffen sind. &lt;br /&gt;
Verweis = makeVerweis(Adresse)&lt;br /&gt;
Adresse = makeAdresse(Verweis)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einfach verkettete Listen==&lt;br /&gt;
jedes Element enthält nur einen Verweis auf das folgende Element. Das letzte Element hat einfach (und meistens) den Wert NULL als Verweis.  Man muss aber berücksichtigen, „0“ ist eine zumindest theoretisch mögliche Adresse.  &lt;br /&gt;
Der List-Header bei einer solchen Liste muss einen Verweis auf das erste Element haben. Ein zusätzlicher Verweis auf das aktuelle letzte Element hat nur Sinn, wenn ich auch am Ende der Liste einfügen will. Wird ohnehin immer an einer beliebigen Stelle eingefügt (sortierte Liste) oder immer nur ganz vorne (FILO = First-In Last-out) kann ich mir einen solchen Verweis auch sparen. Im schlimmsten Falle muss ich mir hat das letzte Element suchen (das ist das mit dem „NULL“-Verweis). &lt;br /&gt;
&lt;br /&gt;
* Eine leere Liste&lt;br /&gt;
[[Bild:List1.JPG|center]]&lt;br /&gt;
* Liste mit zwei Elementen&lt;br /&gt;
[[Bild:List2.JPG|center]]&lt;br /&gt;
* Einfügen von Elementen&lt;br /&gt;
[[Bild:List3.JPG|center]]&lt;br /&gt;
==Grundsätzliche MEthoden==&lt;br /&gt;
* Init Head (HEAD)&lt;br /&gt;
HEAD.FIRST		= makeVerweis(NULL)&lt;br /&gt;
HEAD.LAST		= makeVerweis(NULL)&lt;br /&gt;
* Insert Head (HEAD, NEW)&lt;br /&gt;
FIRST			= makeAddresse(HEAD.FIRST)&lt;br /&gt;
? FIRST = NULL	HEAD.LAST	= makeVerweis(NEW)&lt;br /&gt;
HEAD.FIRST		= makeVerweis(NEW)&lt;br /&gt;
NEW.FWD		= makeVerweis(FIRST)&lt;br /&gt;
* Insert Tail (HEAD, NEW)&lt;br /&gt;
LAST			= makeAddresse(HEAD.LAST)&lt;br /&gt;
? LAST = NULL		HEAD.FIRST	= makeVerweis(NEW)&amp;lt;/br&amp;gt;&lt;br /&gt;
else			LAST.FWD	= makeVerweis(NEW)&amp;lt;/br&amp;gt;&lt;br /&gt;
HEAD.LAST		= makeVerweis(NEW)				optional&amp;lt;/br&amp;gt;&lt;br /&gt;
NEW.FWD 		= „NULL“&lt;br /&gt;
* Insert Behind (POSITION, NEW)&lt;br /&gt;
NEW.FWD		= POSITION.FWD&amp;lt;/br&amp;gt;&lt;br /&gt;
POSITION.FWD		= makeVerweis(NEW)&amp;lt;/br&amp;gt;&lt;br /&gt;
? NEW.FWD = NULL	HEAD.LAST	= makeVerweis(NEW)		optional&amp;lt;/br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Read First(HEAD)&lt;br /&gt;
NEXT			= makeAddresse(HEAD.FIRST)&amp;lt;/br&amp;gt;&lt;br /&gt;
? NEXT = NULL   end-of-list&lt;br /&gt;
* Read Next(POSITION)&lt;br /&gt;
NEXT			= makeAddresse(POSITION.FWD)&amp;lt;/br&amp;gt;&lt;br /&gt;
? NEXT = NULL   end-of-list&lt;br /&gt;
* Remove First(HEAD)&lt;br /&gt;
FIRST			= makeAddresse(HEAD.FIRST)&amp;lt;/br&amp;gt;&lt;br /&gt;
? FIRST = NULL	HEAD.LAST = NULL			optional    end-of-list		&amp;lt;/br&amp;gt;&lt;br /&gt;
else			HEAD.FIRST = FIRST.FWD&lt;br /&gt;
* Remove(POSITION, PREV)&lt;br /&gt;
Entfernen eines beliebigen Elements aus der Liste geht nur, wenn auch der Vorgänger gegeben ist.  &lt;br /&gt;
PREV.FWD		= POSITION.FWD&lt;br /&gt;
&lt;br /&gt;
==Doppelt verkettete Listen==&lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element zusaätzlich auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste direkt in beiden Richtungen navigiert werden. &lt;br /&gt;
&lt;br /&gt;
===NULL-Verweise===&lt;br /&gt;
Um sich nicht beim Einfügen und Entfernen immer wieder darum kümmern zu müssen, ob man sich am Anfang, mittendrin oder am Ende der Liste befindet und ob die Liste leer ist oder nicht, hat es sich bewährt, KEINE NULL-Verweise zu verwenden.  Die entsprechenden Methoden sind einfacher, weil sie kein Wenn und Aber brauchen.&lt;br /&gt;
&lt;br /&gt;
===List Header = Element Header===&lt;br /&gt;
* Einen Verweis auf  das nächste Element   (beim List-Header ist das das erste)&lt;br /&gt;
* einen Verweis auf das vorherige Element (beim List-Header ist das das letzte )&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei den Lese-Routinen spielt die Unterscheidung List / Element-Header eine Rolle (um Anfang und Ende der Liste erkennen zu können). &lt;br /&gt;
&lt;br /&gt;
* Leere Liste&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
Ein Header eine leeren Liste zeigt vorwärts und rückwärts auf sich selbst. &lt;br /&gt;
* Liste mit einem Element&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
* Liste mit mehreren Elementen&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
Das letzte Element verweist „vorwärts“ wieder auf den List-Header, das erste „rückwärts“ ebenfalls. &lt;br /&gt;
&lt;br /&gt;
==Element einfügen==&lt;br /&gt;
===Vorher===&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
===Nachher===&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==List-Tree==&lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Menu0.JPG&amp;diff=18567</id>
		<title>Datei:Menu0.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Menu0.JPG&amp;diff=18567"/>
				<updated>2011-08-26T13:28:34Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:List3.JPG&amp;diff=18566</id>
		<title>Datei:List3.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:List3.JPG&amp;diff=18566"/>
				<updated>2011-08-26T13:28:18Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:List2.JPG&amp;diff=18565</id>
		<title>Datei:List2.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:List2.JPG&amp;diff=18565"/>
				<updated>2011-08-26T13:28:00Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:List1.JPG&amp;diff=18564</id>
		<title>Datei:List1.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:List1.JPG&amp;diff=18564"/>
				<updated>2011-08-26T13:27:44Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18561</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18561"/>
				<updated>2011-08-23T12:55:58Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
&lt;br /&gt;
==Verkettete Listen==&lt;br /&gt;
Man muss verkettete Listen von Tabellen unterscheiden. Bei einer Tabelle sind gleichgrosse Elemente hintereinander in der Memory angeordnet und können durch einen „Index“ (= Element-Nummer) gezielt adressiert werden. &lt;br /&gt;
Bei Bascom hat übrigens das erste Tabellenelement den Index „1“, bei GCC und anderen gilt hier der Index „0“&lt;br /&gt;
Beides hat was, aber darauf wollen wir hier nicht eingehen&lt;br /&gt;
&lt;br /&gt;
Bei verketteten Listen kann sich jedes Element irgendwo im Speicher befinden, jedes Element enthält einen Verweis auf das folgende Element, meist in Form der Adresse. Die Element können auch unterschiedlich lang sein, dann muss oder sollte jedes Element auch eine Längenangabe enthalten (Bei Strings reicht natürlich ev. auch der „NUL“ Terminator).  Die Auffinden eine bestimmten Elements kann hier aber nur erfolgen, indem man der Verkettung vom ersten Element weg folgt, bis man das gesuchte gefunden hat.  &lt;br /&gt;
Bei eine „doppelt“ verketteten Liste enthält jedes Element auch einen Verweis auf seinen Vorgänger, dadurch kann dann in der Liste in beiden Richtungen navigiert werden. &lt;br /&gt;
&lt;br /&gt;
Klingt irgendwie aufwendig, wofür kann man das brauchen ?&lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
Nun, wenn zum Beispiel die Element umsortiert werden sollen, dann reicht es, die Verkettungs-Zeiger zu verändern, während man bei einer Tabelle die gesamten Daten verschieben muss. Ebenso, wenn man einzelne Element entfernen oder einfügen will.  &lt;br /&gt;
Beim Aufbau von „Warteschlangen“ (Queues)  wird z.B. beim „enqueuen“ immer „hinten“ ein Element eingefügt, beim „dequeuen“ immer vorn das erste (wie beim Aldi an der Kassa). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Aufbau==&lt;br /&gt;
===List-Header===&lt;br /&gt;
Ein solcher Header ist sozusagen die Kontroll-Struktur für eine Liste. Auf das notwendigste reduziert, hat er drei Attribute: &lt;br /&gt;
* Seine eigene Adresse&lt;br /&gt;
* Einen Verweis auf  das erste Element&lt;br /&gt;
* einen Verweis auf das letzte. &lt;br /&gt;
&lt;br /&gt;
===Element-Header===&lt;br /&gt;
Sieht im Grunde genauso aus&lt;br /&gt;
* Seine eigene Adresse&lt;br /&gt;
* Einen Verweis auf  das nächste Element&lt;br /&gt;
* einen Verweis auf das vorherige. &lt;br /&gt;
&lt;br /&gt;
[[Bild:Chain1.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
===Verweise/Adressen===&lt;br /&gt;
Als Verweis kann direkt die jeweilige Speicher-Adresse dienen. Bei einem Computer /PC mit 32-Bit Adressen (4 Byte) und keinen (kaum) Speicherproblemen ist das sicher eine praktikable Methode. Aber dann bestünde so ein Header aus 2x4 , also 8 Byte. Und für einen µC knabbert das doch ganzschön an den üblicherweise knappen Speicherressourcen. Verwendet man dort 16-Bit Adressen, reicht das, um eine 64k memory abzudecken. Da gehen zwar auch noch 4 Byte für jeden Header drauf, aber von nix kommt eben nix. &lt;br /&gt;
Es gibt noch mehr Möglichkeiten, solche Verweise kürzer zu bekommen, eine davon ist die, auf die Rückwärtsverkettung zu verzichten, in manchen /vielen Fällen ist sie ja nicht notwendig, dann halbiert sich der Speicherbedarf schlagartig. &lt;br /&gt;
Wie man das auch macht, in der Folge bleibe ich bei meinen doppelt verketteten Listen, und bleibe beim Begriff „Verweis“ und lasse offen, was das genau ist. Für das weitere Verständnis ist es auch unerheblich. &lt;br /&gt;
 &lt;br /&gt;
===Verweise/Sonderfälle===&lt;br /&gt;
Für das erste bzw. letzte Element der Liste ist die Frage, wie dann die Verweise auf den Nachfolger/Vorgänger aussehen sollen. Die gleiche Frage beim List-Header, wenn die Liste leer ist. &lt;br /&gt;
Man kann natürlich einfach „NULL“ reinschreiben bzw. darin stehen lassen. Für die Lese-Funktionen „nächstes-“/“vorheriges Element“ ist das praktisch, „NULL“ heisst, es ist kein Element mehr da.&lt;br /&gt;
===Leere Liste===&lt;br /&gt;
[[Bild:Chain2.JPG|center]]&lt;br /&gt;
===Liste mit einem Element===&lt;br /&gt;
[[Bild:Chain3.JPG|center]]&lt;br /&gt;
===Liste mit mehreren Elementen===&lt;br /&gt;
[[Bild:Chain4.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Element einfügen==&lt;br /&gt;
===Vorher===&lt;br /&gt;
[[Bild:Chain5.JPG|center]]&lt;br /&gt;
===Nachher===&lt;br /&gt;
[[Bild:Chain6.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==List-Tree==&lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain6.JPG&amp;diff=18560</id>
		<title>Datei:Chain6.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain6.JPG&amp;diff=18560"/>
				<updated>2011-08-23T12:46:22Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain5.JPG&amp;diff=18559</id>
		<title>Datei:Chain5.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain5.JPG&amp;diff=18559"/>
				<updated>2011-08-23T12:46:08Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain4.JPG&amp;diff=18558</id>
		<title>Datei:Chain4.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain4.JPG&amp;diff=18558"/>
				<updated>2011-08-23T12:45:53Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain3.JPG&amp;diff=18557</id>
		<title>Datei:Chain3.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain3.JPG&amp;diff=18557"/>
				<updated>2011-08-23T12:45:36Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain1.JPG&amp;diff=18556</id>
		<title>Datei:Chain1.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain1.JPG&amp;diff=18556"/>
				<updated>2011-08-23T12:45:22Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain2.JPG&amp;diff=18555</id>
		<title>Datei:Chain2.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain2.JPG&amp;diff=18555"/>
				<updated>2011-08-23T12:45:04Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18554</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18554"/>
				<updated>2011-08-23T12:44:45Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
Baustelle in Arbeit&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18553</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18553"/>
				<updated>2011-08-23T12:43:38Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Listen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
Baustelle in Arbeit&amp;lt;br/&amp;gt; &lt;br /&gt;
&lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18552</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18552"/>
				<updated>2011-08-23T12:43:02Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
Tralala.&amp;lt;br/&amp;gt; &lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18551</id>
		<title>Listen</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Listen&amp;diff=18551"/>
				<updated>2011-08-23T12:41:15Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Listen=&lt;br /&gt;
Tralala.&amp;lt;br/&amp;gt; &lt;br /&gt;
[[Bild:Chain7.JPG|center]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Chain7.JPG&amp;diff=18550</id>
		<title>Datei:Chain7.JPG</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Chain7.JPG&amp;diff=18550"/>
				<updated>2011-08-23T12:36:07Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17576</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17576"/>
				<updated>2011-07-19T12:58:30Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Drehgeber-Auswertung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.&amp;lt;br/&amp;gt; &lt;br /&gt;
[[Sensorarten#Incremental-Geber|Technisches]] findet man hier, [[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln|Grundsätzliches]] über das Quadratur-Signal da, und [[Drehencoder|Programm-Beispiele]] für Drehencoder gibt es auch&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
(Genaugenommen der Zustände unmittelbar danach)&amp;lt;br/&amp;gt;&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.&amp;lt;br/&amp;gt;&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.&amp;lt;br/&amp;gt;&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.&amp;lt;br/&amp;gt; &lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
====INT0/1 oder Polling ?====&lt;br /&gt;
* Wie schon erwähnt, bei AVR's bieten sich die beiden &amp;quot;external interrrupts&amp;quot; geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern.&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).&amp;lt;br/&amp;gt;&lt;br /&gt;
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.&lt;br /&gt;
&lt;br /&gt;
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.&amp;lt;br/&amp;gt;&lt;br /&gt;
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). &lt;br /&gt;
&lt;br /&gt;
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen &amp;quot;schritte in der Zeit&amp;quot; und &amp;quot;Zeit je Schritt&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
====Auswertung der Signale====&lt;br /&gt;
* Bei der INT-Methode mit nur einer Flanke ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. &lt;br /&gt;
&lt;br /&gt;
* mit beiden Flanken muss man beide Kanäle lesen, aber die Sache ist auch nicht kompliziert: &lt;br /&gt;
** A = B --&amp;gt; vorwärts&lt;br /&gt;
** A &amp;lt;&amp;gt; B --&amp;gt; rückwärts&lt;br /&gt;
Wenn der µC diese Interrupt-Art zulässt, kann man damit auch Schrittfehler vermeiden. &lt;br /&gt;
 &lt;br /&gt;
* Auswertung der Übergänge:Das kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich &lt;br /&gt;
** &amp;quot;Select/Case&amp;quot; (Bascom) oder &amp;quot;switch/case&amp;quot; (GCC) an. &lt;br /&gt;
Als Beispiel das [[Bascom_Inside-Code#.28Quadratur-.29_ENCODER|Assembler-listing]] für die Bascom-ENCODER()-Funktion&amp;lt;br/&amp;gt;&lt;br /&gt;
** Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für einen Werte- oder Sprungtabelle zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17575</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17575"/>
				<updated>2011-07-19T12:48:24Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Programmtechnische Umsetzung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.&amp;lt;br/&amp;gt; &lt;br /&gt;
Technisches findet man hier:&lt;br /&gt;
[[Sensorarten#Incremental-Geber]]&amp;lt;br/&amp;gt;&lt;br /&gt;
und Grundsätzliches über das Quadratur-Signal:&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Programmierungs-Beispiele für Drehencoder:&lt;br /&gt;
[[Drehencoder]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.&amp;lt;br/&amp;gt;&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.&amp;lt;br/&amp;gt;&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.&amp;lt;br/&amp;gt; &lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
====INT0/1 oder Polling ?====&lt;br /&gt;
* Wie schon erwähnt, bei AVR's bieten sich die beiden &amp;quot;external interrrupts&amp;quot; geradezu an. Bei vielen fertigen Boards werden die notwendigen Leitungen schon für einen Anschluss herausgeführt. Die Hauptschleife (DO...LOOP) bzw. (while(true)) braucht sich dadurch nicht unbedingt um die Sache zu kümmern.&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei Odometrie-Anwendungen gehen die oben erwähnten möglichen Schrittfehler meistens völlig in der Gesamt-Ungenauigkeit unter (schlupfende Räder etc.).&amp;lt;br/&amp;gt;&lt;br /&gt;
Hat man als Drehgeber ein Dateneingaberad ( z.B. für Menüs u. Parametereingaben) erfolgt ohnehin eine Sichtkontrolle der Position, da kommt es schon garnicht darauf an.&lt;br /&gt;
&lt;br /&gt;
* Die Sache liegt anders, wenn wirklich kein Tick falsch interpretiert werden soll, also z.B. bei Positionier-Anwendungen. Da ist es wohl besser, die Eingänge zu pollen und alle Übergänge auszuwerten.&amp;lt;br/&amp;gt;&lt;br /&gt;
Das kann man durchaus in der Hauptschleife machen, wenn dann die Abtastfrequenz ausreichend ist, man kann aber auch durch einen TIMER-Interrupt das Polling auslösen (mit dem gleichen Kriterium). &lt;br /&gt;
&lt;br /&gt;
* Ganz elegant ist natürlich eine Mischform: wenn man auch die Geschwindigkeit misst, kann man abhängig davon die Methode ändern, denn die genannten Schrittfehler werden bei höherem Tempo kaum auftreten, eher bei Stillstand, genauso, wie auch die Geschwindigkeitsmessung selbst wechseln kann zwischen &amp;quot;schritte in der Zeit&amp;quot; und &amp;quot;Zeit je Schritt&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
====Auswertung der Signale====&lt;br /&gt;
* Bei der INT-Methode mit nur einer Flanke ist die Sache einfach: Es erfolgt ein Schritt, die Richtung zeigt der andere Kanal. &lt;br /&gt;
&lt;br /&gt;
* mit beiden Flanken muss man beide Kanäle lesen, aber die Sache ist auch nicht kompliziert: &lt;br /&gt;
** A = B --&amp;gt; vorwärts&lt;br /&gt;
** A &amp;lt;&amp;gt; B --&amp;gt; rückwärts&lt;br /&gt;
Wenn der µC diese Interrupt-Art zulässt, kann man damit auch Schrittfehler vermeiden. &lt;br /&gt;
 &lt;br /&gt;
* Auswertung der Übergänge:Das kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich &amp;quot;Select/Case&amp;quot; (Bascom) oder &amp;quot;switch/case&amp;quot; (GCC) an.&amp;lt;br/&amp;gt; &lt;br /&gt;
Als Beispiel das Assembler-listing für die Bascom-ENCODER()-Funktion:&lt;br /&gt;
[[Bascom_Inside-Code#.28Quadratur-.29_ENCODER]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für einen Werte- oder Sprungtabelle zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert.&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17574</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17574"/>
				<updated>2011-07-19T09:33:46Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.&amp;lt;br/&amp;gt; &lt;br /&gt;
Technisches findet man hier:&lt;br /&gt;
[[Sensorarten#Incremental-Geber]]&amp;lt;br/&amp;gt;&lt;br /&gt;
und Grundsätzliches über das Quadratur-Signal:&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Programmierungs-Beispiele für Drehencoder:&lt;br /&gt;
[[Drehencoder]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.&amp;lt;br/&amp;gt;&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.&amp;lt;br/&amp;gt;&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.&amp;lt;br/&amp;gt; &lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
====ISR oder Polling ?====&lt;br /&gt;
&lt;br /&gt;
====Auswertung der Signale====&lt;br /&gt;
Das kann man natürlich auf viele Arten machen. Je nach Sprache bietet sich &amp;quot;Select/Case&amp;quot; (Bascom) oder &amp;quot;switch/case&amp;quot; (GCC) an. Ein Hinweis, dem man folgen kann: Bascom prüft ab, '''ob''' ein Schritt erfolgt ist, danach prüft er aber '''nur''' die Werte für &amp;quot;vorwärts&amp;quot; einzeln ab, alle anderen Möglichkeiten müssen dann ja zwangsläufig &amp;quot;rückwärts&amp;quot; bedeuten.&amp;lt;br/&amp;gt; &lt;br /&gt;
Das Assembler-listing für die Bascom-ENCODER()-Funktion:&lt;br /&gt;
[[Bascom_Inside-Code#.28Quadratur-.29_ENCODER]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Eine andere Möglichkeit ist es, die neuen und die vorhergegangenen AB Werte zusammenzufassen und als Index für einen Werte- oder Sprungtabelle zu verwenden. In einer solche Tabelle muss man allerdings auch die ungültigen AB Kombinationen belegen, damit das funktioniert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17573</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17573"/>
				<updated>2011-07-19T08:48:50Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Drehgeber-Auswertung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen.&amp;lt;br/&amp;gt; &lt;br /&gt;
Technisches findet man hier:&lt;br /&gt;
[[Sensorarten#Incremental-Geber]]&amp;lt;br/&amp;gt;&lt;br /&gt;
und Grundsätzliches über das Quadratur-Signal:&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Beispiel_Drehzahlmessung_mit_Drehgeber#Richtung.2C_Geschwindigkeit_und_Position_mit_Doppellichtschranke_ermitteln]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Programmierungs-Beispiele für Drehencoder:&lt;br /&gt;
[[Drehencoder]]&amp;lt;br/&amp;gt;&lt;br /&gt;
Ich möchte dem einige grundsätzliche Überlegungen zur Drehgeber-Auswertung hinzufügen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.&amp;lt;br/&amp;gt;&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis.&amp;lt;br/&amp;gt;&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt.&amp;lt;br/&amp;gt; &lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
Da gibt es Varianten, die hier sehr schön beschrieben sind:&lt;br /&gt;
&lt;br /&gt;
[[Drehencoder]]&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17555</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17555"/>
				<updated>2011-07-18T14:58:39Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Programmtechnische Umsetzung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.  &lt;br /&gt;
&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis. &lt;br /&gt;
&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt. &lt;br /&gt;
&lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
Da gibt es Varianten, die hier sehr schön beschrieben sind:&lt;br /&gt;
&lt;br /&gt;
[[Drehencoder]]&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17554</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17554"/>
				<updated>2011-07-18T14:56:54Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.  &lt;br /&gt;
&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis. &lt;br /&gt;
&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt. &lt;br /&gt;
&lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
&lt;br /&gt;
===Programmtechnische Umsetzung===&lt;br /&gt;
Da gibt es Variante, die hier sehr schön beschrieben sind:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17553</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17553"/>
				<updated>2011-07-18T14:45:51Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* ÜbergangsTabelle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.  &lt;br /&gt;
&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis. &lt;br /&gt;
&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt. &lt;br /&gt;
&lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17552</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17552"/>
				<updated>2011-07-18T14:41:19Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Auswertung der Übergänge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
Die genaueste Auswertung ist möglich, wenn man alle Übergänge steigend/fallend von beiden Kanälen (A u. B) bewertet. Das kann man ebenfalls mit ISR's machen, wenn man den µC so konfigurieren kann, dass er beide Flankenarten auslöst. Da dafür aber nur ein ISR-Vektor zur Verfügung steht, muss man in der Routine sowohl Kanal A als auch B abfragen und auch, ob fallend oder steigend.  &lt;br /&gt;
&lt;br /&gt;
Am einfachsten merkt man sich immer den Zustand von A u. B und vergleicht mit dem neuen Abtastergebnis. &lt;br /&gt;
&lt;br /&gt;
Im Grunde ist das dann dasselbe, als wenn man ohne ISR periodisch die Eingänge abtastet, was natürlich oft genug geschehen muss, damit nichts verlorengeht. Die Flanken selbst erkennt man an dem Unterschien AB / AB-alt. &lt;br /&gt;
&lt;br /&gt;
In beiden Fällen erfolgt dann die Auswertung an den strichlierten senkrechten Linien.    &lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Von dem Mitte nach rechts gelesen bekommt also an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 01&lt;br /&gt;
* 10  vorher: 00&lt;br /&gt;
* 11  vorher: 10&lt;br /&gt;
* 01  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
====Rückwärts====&lt;br /&gt;
Von dem Mitte nach links gelesen bekommt dann an den bezeichneten Stellen die Werte&lt;br /&gt;
* 00  vorher: 10&lt;br /&gt;
* 01  vorher: 00&lt;br /&gt;
* 11  vorher: 01&lt;br /&gt;
* 10  vorher: 11&lt;br /&gt;
* usw&lt;br /&gt;
&lt;br /&gt;
=== ÜbergangsTabelle===&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
 |'''A-B'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Vorher'''&lt;br /&gt;
 |01&lt;br /&gt;
 |10&lt;br /&gt;
 |00&lt;br /&gt;
 |11&lt;br /&gt;
 |10&lt;br /&gt;
 |01&lt;br /&gt;
 |11&lt;br /&gt;
 |00&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Richtung'''&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17551</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17551"/>
				<updated>2011-07-18T13:45:22Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Auswertung mit ISR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung der Flanken===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
=== ÜbergangsTabelle===&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
 |'''A-B'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Vorher'''&lt;br /&gt;
 |01&lt;br /&gt;
 |10&lt;br /&gt;
 |00&lt;br /&gt;
 |11&lt;br /&gt;
 |10&lt;br /&gt;
 |01&lt;br /&gt;
 |11&lt;br /&gt;
 |00&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Richtung'''&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17550</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17550"/>
				<updated>2011-07-18T13:23:56Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Auswertung mit ISR */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung mit ISR===&lt;br /&gt;
Gerade bei den AVR's bietet es sich an, das A-Signal an einen &amp;quot;Externen Interrupt&amp;quot; anzuschliessen. &lt;br /&gt;
Da man meist auch zwei Drehgeber hat (für links u. rechts), kommt man da wunderbar mit INT0 u. INT1 aus. &lt;br /&gt;
====Vorwärts====&lt;br /&gt;
Bei einer Konfiguration &amp;quot;steigende Flanke&amp;quot; wird bei den dicken Pfeilen der Interrupt ausgelöst. Liest man in der ISR den B-Kanal, kann man daraus direkt die Richtung entnehmen (im Beispiel ist das ebenfalls &amp;quot;1&amp;quot;) und auf den Schrittzähler 1 addieren.  &lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
Wird nun die Drehrichtung geändert, wird die bis dahin fallende Flanke von A zur steigenden Flanke, und B ist zu diesem Zeitpunkt dann &amp;quot;0&amp;quot;. Die Sache ist eindeutig, man kann nun von Schrittzähler eins abziehen.  &lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
Dabei tritt aber natürlich eine Verschiebung auf. Wenn die Impulsfolge im Verhältnis zum zurückgelegten Weg gross ist, kann man das ignorieren, es geht ja letztlich meistens nur um ein paar Millimeter, und wo genau sich ein Drehgeber zwischen zwei Impulsen befindet, kann man ohnehin nie sagen.  &lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
Es gibt aber auch Situationen, wo es zu Fehlern kommt. Hier wird der zweite Interrupt noch ausgelöst und (richtig) als Schritt vorwärts gezählt. Dann bewegt sich der Geber etwas zurück, kommt aber nicht bis zu nächsten steigenden Flanke, sondern bewegt sich wieder nach vorne.  &lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
Unsere Logik zählt nun einen Impuls zweimal, obwohl der drehgeber sich immer noch am selben Platz befindet.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
=== ÜbergangsTabelle===&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
 |'''A-B'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Vorher'''&lt;br /&gt;
 |01&lt;br /&gt;
 |10&lt;br /&gt;
 |00&lt;br /&gt;
 |11&lt;br /&gt;
 |10&lt;br /&gt;
 |01&lt;br /&gt;
 |11&lt;br /&gt;
 |00&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Richtung'''&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17549</id>
		<title>Überlegungen zur Drehgeber-Auswertung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=%C3%9Cberlegungen_zur_Drehgeber-Auswertung&amp;diff=17549"/>
				<updated>2011-07-18T11:18:54Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Drehgeber-Auswertung=&lt;br /&gt;
Es gibt in RN-Wissen schon einige (sehr gute) Artikel über Drehgeber und Auswertungsvarianten. Denen will ich hier keine Konkurrenz machen. Ich möchte nur einige grundsätzliche Überlegungen zur Drehgeber-Auswertung anstellen, damit es einem Einsteiger leichter fällt, eine Auswahl für seine konkreten Anforderungen zu treffen. &lt;br /&gt;
&lt;br /&gt;
==Signalfolge==&lt;br /&gt;
Zur Erinnerung:&lt;br /&gt;
[[Bild:quad_0.png|center]]&lt;br /&gt;
===Auswertung mit ISR===&lt;br /&gt;
====Vorwärts====&lt;br /&gt;
[[Bild:quad_isr1.png|center]]&lt;br /&gt;
====Richtungsumkehr====&lt;br /&gt;
[[Bild:quad_isr2.png|center]]&lt;br /&gt;
====Möglicher Fehler====&lt;br /&gt;
[[Bild:quad_err.png|center]]&lt;br /&gt;
=== Auswertung der Übergänge===&lt;br /&gt;
[[Bild:quad_states.png|center]]&lt;br /&gt;
=== ÜbergangsTabelle===&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
 |'''A-B'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''00'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''10'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''11'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |'''01'''&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Vorher'''&lt;br /&gt;
 |01&lt;br /&gt;
 |10&lt;br /&gt;
 |00&lt;br /&gt;
 |11&lt;br /&gt;
 |10&lt;br /&gt;
 |01&lt;br /&gt;
 |11&lt;br /&gt;
 |00&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Richtung'''&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |vorwärts&lt;br /&gt;
 |rückwarts&lt;br /&gt;
 |-&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Sensorarten]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Sensorarten&amp;diff=17548</id>
		<title>Sensorarten</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Sensorarten&amp;diff=17548"/>
				<updated>2011-07-18T11:01:52Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: /* Drehgeber Sharp GP1A30 und GP1A38 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Welche Sensorarten gibt es==&lt;br /&gt;
Damit sich ein Roboter in seiner Umgebung bewegen kann, ohne an Hindernisse anzustoßen, und damit er ein Ziel finden und ansteuern kann, muß er irgendwie seine Umwelt &amp;quot;wahrnehmen&amp;quot;. &lt;br /&gt;
Hauptanwendung für diese Informationen aus der Umwelt ist die [[Navigation]].&lt;br /&gt;
Auf dieser Seite werden die wichtigsten Sensorarten und damit es praktischer wird auch zugleich die populärsten Robotik- und Modellbau-Sensortypen vorgestellt. Auch die Pinbelegung und Anschlussmöglichkeiten dieser Sensoren werden kurz erläutert.&lt;br /&gt;
&lt;br /&gt;
Es ist auch möglich Informationen für die Steuerung des Roboters nicht nur aus eigens dafür gebaute Sensoren zu gewinnen. Durch Ausnutzung der Eigenschaften von schon vorhandenen Bauteilen können dadurch eventuell separate Sensoren eingespart werden. Beispiel ist im verlinkten Forumbeitrag unter den Weblinks zu finden.&lt;br /&gt;
&lt;br /&gt;
==Bumpers==&lt;br /&gt;
Als Bumpers bezeichnet man eine Art Stoßstange, die einen Mikro-Switch auslöst. &lt;br /&gt;
&lt;br /&gt;
[[Bild:microschalter.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
Die Auswertung erfolgt am besten digital an einem Pin mit Pull-up Widerstand. Man kann den Eingang pollen (abfragen), aber auch einen Interrupt auslösen lassen. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Whiskers (Fühler)==&lt;br /&gt;
Das sind flexible Kunststoff-Streifen von ca. 10 cm Länge, eigentlich für Datenhandschuhe vorgesehen. Aber man kann sie auch als Fühler einsetzen, und daher auch für die Kollisions-Sensorik verwenden. Beim Verbiegen steigt der Widerstand an.  &lt;br /&gt;
&lt;br /&gt;
[[Bild:flexs_200.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
Um zu messen, wird mit einem zweiten Widerstand ein Spannungsteiler aufgebaut, der dann mit einem Analog-Eingang einen Grad der Verbiegung erkennen läßt. Sie werden häufig auch Flexsensoren genannt. --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Incremental-Geber==&lt;br /&gt;
Dienen zur Erfassung von Drehzahl bzw. der Messung von Wegstrecken&lt;br /&gt;
&lt;br /&gt;
===austriamicrosystems AS5040===&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
[[Bild:AMS-general_200x200.jpg|right]]&lt;br /&gt;
[[Bild:AMS-chip_109x104.jpg|left]]&lt;br /&gt;
&lt;br /&gt;
Der AS5040 kann absolute Winkelpositionen an einer Achse messen. Die Messung wird indirekt über ein Magnetfeld mittels Hallsensoren durchgeführt. Aufgrund der Anordnung der Hallsensoren und der Integration der Sensoren direkt in Silizium wird eine sehr hohe Genauigkeit bei der Messung erreicht. Das Messprinzip kompensiert ausserdem Störgrössen, wie externes Magnetfeld, Alterungseinflüsse, Temperaturschwankungen und mechanische Toleranzen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
Die komplette Auswertung der Sensorsignale geschieht auf dem Chip. &amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:AMS-block_700x242.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:AMS-pcb_200x152.jpg|right]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
Durch die hohe Integration reduziert sich die zusätzliche Beschaltung des Chips auf wenige Bauteile, beispielsweise ein paar Filterkondensatoren. &lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
Die Winkelencoderfamilie von austriamicrosystems besitzt vielfältige Schnittstellen, um den Winkelwert zu übertragen&lt;br /&gt;
&lt;br /&gt;
{| {{Blauetabelle}}&lt;br /&gt;
 |Seriell SSI&lt;br /&gt;
 |Standard Schnittstelle für Winkelencoder in der Industrie&lt;br /&gt;
 |-&lt;br /&gt;
 |PWM&lt;br /&gt;
 |erzeugt abhängig vom Winkel eine entsprechende Pulslänge, läßt sich sehr schön mit der Capturefunktion eines Controllers messen und braucht nur eine Leitung&lt;br /&gt;
 |-&lt;br /&gt;
 |I2C&lt;br /&gt;
 |direkte Schnittstelle zum Microcontroller&lt;br /&gt;
 |-&lt;br /&gt;
 |Analog&lt;br /&gt;
 |kompatible Schnittstelle um beispielsweise Potiapplikationen zu ersetzen&lt;br /&gt;
 |-&lt;br /&gt;
&lt;br /&gt;
 |Inkremental&lt;br /&gt;
 |klassische Schnittstelle um relative Bewegungen zu messen, keine Absolutmessung möglich&lt;br /&gt;
 |-&lt;br /&gt;
 |BLDC&lt;br /&gt;
 |erzeugt direkt die Kommutierung für einen bürstenlosen Motor. Durch die höhere Auflösung gegenüber einer 3-Hall-Schalterlösung kann der BLDC mit einem wesentlich höheren Drehmoment gestartet werden&lt;br /&gt;
 |}&lt;br /&gt;
&lt;br /&gt;
[[Bild:AMS-magnet_354x421.jpg|left]]&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
Bei der Auswahl des Magnets ist darauf zu achten, dass sogenannte Selten-Erde-Magnete verwendet werden. http://de.wikipedia.org/wiki/Seltene_Erden Diese erzeugen ein besonders starkes Magnetfeld, das im 1-Tesla-Bereich liegt. Im Zentrum des Magnetfelds ist ein linearer Bereich, der für die Genauigkeit der Messung ausschlaggebend ist. Solange die Hallsensoren in diesem Bereich liegen, kann eine unkalibrierte Genauigkeit von +/- 0.5 Grad gewährleistet werden. Weiterhin ist zu beachten, dass der Magnet nicht direkt auf eine Eisenwelle montiert wird. Die Eisenwelle verursacht quasi einen magnetischen 'Kurzschluss' und entzieht damit den Hallsensoren das Magnetfeld. Idealerweise sollte eine NE-Welle verwendet werden, oder wenn nicht anders machbar muss eine Isolation aus NE-Material zwischen Eisenwelle und Magnet eingefügt werden.&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&lt;br /&gt;
Ein einfacher erfolgreicher Test des Sensors ist hier beschrieben: http://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=322373#322373&lt;br /&gt;
&lt;br /&gt;
===Drehgeber Sharp GP1A30 und GP1A38===&lt;br /&gt;
&lt;br /&gt;
[[Bild:gp1a30.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
Beim Sharp GP1A30 und GP1A38 handelt es sich um Gabellichtschranken mit inkrementaler Drehgeberfunktion. Beide sind im Grunde von den Anschlüssen identisch, lediglich kann GP1A38 noch etwas höhere Drehzahlen messen. Im Gegensatz zu herkömmlichen Gabellichtschranken können mit dieser sowohl Drehzahl als auch Drehrichtung ermittelt werden. Notwendig sind dafür nur 2 digitale Ports. Verzichtet man auf die Auswertung der Drehrichtung, reicht nur ein Port.&lt;br /&gt;
Der Sensor hat den Vorteil, dass die Signale bereits TTL-kompatibel sind und direkt an ein Controllerboard angeschlossen werden können. Ein Beispielprogramm zu [[RN-Control]] findet man hier&lt;br /&gt;
&lt;br /&gt;
* [[Überlegungen zur Drehgeber-Auswertung]]&lt;br /&gt;
* [[Beispiel Drehzahlmessung mit RN-Control]]&lt;br /&gt;
* [[Beispiel Drehzahlmessung mit Drehgeber]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:GP1A30.gif|center]]&lt;br /&gt;
&lt;br /&gt;
Die Auswertung inkrementaler Drehgeber ist recht einfach. Ein Ausgang liefert einen Impuls pro Markierung (Scheibe auf der Welle). Prüft man bei jedem Impuls (Flanke) noch gleichzeitig den aktuellen Pegel des zweiten Ausganges, so kann daraus die Drehrichtung abgeleitet werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Signalverlauf an den Sensorausgängen ist phasenversetzt:&lt;br /&gt;
&lt;br /&gt;
[[Bild:inkremental1.gif|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:drehgeber_an_rncontrol.gif|center|framed|Beispielschaltung, wenn man lediglich die Drehzahl ohne Drehrichtung auswerten möchte. Als Widerstand hat sich 330 Ohm als gut erwiesen.]]&lt;br /&gt;
&lt;br /&gt;
==Optische Sensoren==&lt;br /&gt;
===Helligkeit=== &lt;br /&gt;
Die Hauptanwendung solcher Sensoren ist, den hellsten Fleck im Raum zu finden.&lt;br /&gt;
Dies ist auch Teil vieler Roboterwettbewerbe. (Robo Callenge (2002 sic!), 'Ein Platz an der Sonne')&lt;br /&gt;
&lt;br /&gt;
Solche Sensoren können einfache [[LDR]]s oder [[Fotodiode|Photodioden]] bzw. [[Fototransistor|Phototransistoren]] sein.&lt;br /&gt;
LDRs haben den Vorteil, dass sie relativ hoch aussteuern, der große Nachteil ist aber, dass diese Sensoren ziemlich wärmeempfindlich und etwas träge sind.&lt;br /&gt;
Photodioden und Phototransistoren haben den Vorteil, dass sie sehr genau sind, aber nicht besonders hoch aussteuern. Diese sollte man evtl. über einen OpAmp verstärken.&lt;br /&gt;
&lt;br /&gt;
[[Bild:fotowiderstand.jpg|center|framed|Ein typischer Fotowiderstand]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:SFH300.jpg|center|framed|SFH300, ein typischer Phototransistor]]&lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch ein digitaler Lichtsensor verwendet werden, den man direkt an den I2C- oder SM-Bus anschliessen kann. Man erspart sich Auswertung, Temperaturkompensation und Signalaufbereitung, allerdings ist der Anschluss meistens schwieriger, weil der Sensor in SMD-Bauform gebaut ist und 3,3 anstatt 5 Volt braucht.&lt;br /&gt;
[[Bild:Lichtsensor.jpg|center|framed|Ein digitaler Lichtsensor für den I2C-Bus (TSL2561 der Frima Taos)]]&lt;br /&gt;
&lt;br /&gt;
Die Suche nach dem hellsten Fleck im Raum ist wie ich finde ein gutes Einsteigerthema!&lt;br /&gt;
&lt;br /&gt;
===CNY70===&lt;br /&gt;
&lt;br /&gt;
siehe [[CNY70]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Distanzsensor IS471F===&lt;br /&gt;
&lt;br /&gt;
IS471F - Hinderniserkennung mit Infrarot Distanzsensor&lt;br /&gt;
&lt;br /&gt;
Das hier vorgestellte IC IS471F erlaubt eine einfache und sogar recht preisgünstige Hinderniserkennung per Infrarot. Dazu muss im Wesentlichen nur noch eine Infrarot-Diode an das IC angeschlossen werden. Das modulierte Licht wird von einem Gegenstand direkt auf das IC zurückgeworfen und somit das Hindernis erkannt. Tageslicht/Fremdlicht stört den IS471 überhaupt nicht, da das Licht mit einer bestimmten Frequenz moduliert wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:is471.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der normale Schaltungsaufbau sieht also wie oben abgebildet aus! In der Praxis kann das dann in etwa so aussehen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:is471beispiel.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das englische Datenblatt findet man im Roboternetz-Download-Bereich&lt;br /&gt;
&lt;br /&gt;
Die normale Reichweite ist in gewissen Grenzen abhängig von der Farbe des Hindernisses. In der Regel reicht sie jedoch einige cm, so dass langsame Roboter durchaus sehr gut damit zurechtkommen. Durch besonders helle Infrarot-LEDs und durch zusätzliche LED-Fassungen kann man die Reichweite erhöhen. Wem das noch nicht reicht, der kann über einen zusätzlichen Transistor die Strahlungsleistung der LED noch weiter erhöhen. &lt;br /&gt;
&lt;br /&gt;
[[Bild:is471verstaerkung.png|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
http://www.roboternetz.de/bilder/mehrereis471.gif&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sharp Infrarotsensoren===&lt;br /&gt;
&lt;br /&gt;
Sharp hat eine ganz interessante Serie von Bausteinen herausgebracht, mit denen ein Roboter sogar recht genau Entfernungen messen kann. Das Messergebnis ist nahezu unabhängig von der Farbe und Helligkeit des gemessenen Objektes. Die Ansteuerung ist sehr einfach, der Sensorausgang kann zum Beispiel direkt an einen analogen Eingang eines Mikrocontrollers angeschlossen werden. Die Sensoren sind unter den Roboter-Bastlern weit verbreitet. &lt;br /&gt;
Bei vielen Händlern erhältlich kosten die Sensoren derzeit je nach Typ und Händlerspanne zwischen 13 bis 25 Euro.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Sharpentfernungssensor.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Oft werden selbst in kleinen Roboter-Projekten mehrere dieser Sensoren genutzt, da sie nur einen äußerst engen Erfassungsbereich haben. Sie eignen sich daher sehr gut zur Vermessung einer Umgebung, insbesondere dann, wenn sie drehbar auf einem Servo montiert werden. Als Kollisionsschutz sind sie wegen des engen Winkels nur bedingt geeignet, zu diesem Zweck eignen sich zum Beispiel Ultraschallsensoren besser.&lt;br /&gt;
Das Funktionsprinzip der Sensoren ist einfach. Der Sensor besteht aus Sender und Empfänger. Der Sender sendet einen Infrarot-Strahl aus, der vom Hindernis reflektiert wird. Je nach Entfernung trifft der reflektierte Strahl an einer unterschiedlichen Stelle auf den Empfänger (ein sog. Position Sensitive Device, PSD). Der Empfänger setzt den Auftreffpunkt in einen analogen Spannungswert um. Eine schöne Skizze dazu:  &lt;br /&gt;
&lt;br /&gt;
[[Bild:sharpfunktion.jpg|center]]&lt;br /&gt;
{{FarbigerRahmen|&lt;br /&gt;
Wenn man diese Sensoren an Robotern einsetzt, will man meist Hindernisse mit senkrecht stehenden Kanten erkennen. Montiert man den Sensor jedoch in waagrechter Lage, können sich Fehlmessungen ergeben, wenn sich ein Objekt am Sensor vorbeibewegt (z.B. wenn sich der Roboter dreht). Es wird an der Kante kurzzeitig eine Entfernung gemessen, die geringer als die tatsächliche Entfernung zum Hindernis ist (oder auch größer als die Entfernung zu einem zweiten Hindernis hinter dem ersten, je nach Bewegungsrichtung!). Man löst dieses Problem, indem man den Sensor in senkrechter Position montiert, also so, dass die beiden Linsen übereinander statt nebeneinander liegen. Dies wird auch im Datenblatt des Sensors empfohlen: der Sensor sollte immer senkrecht zur Bewegungsrichtung des Hindernisses montiert werden.&lt;br /&gt;
&lt;br /&gt;
Sehr deutlich erkennt man diese Messfehler, wenn man den Sensor als eine Art 3D-Scanner einsetzt: http://www.team-iwan.de/technik/sharp1.php&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
|&lt;br /&gt;
Die Gehäuse dieser Sensoren sehen wie ganz gewöhnlicher Kunststoff aus. Sie bestehen jedoch aus leitfähigem Material! (Kaum zu glauben, aber einfach nachprüfbar: zwischen den beiden Befestigungslöchern misst man einen Widerstand von etwa 250 bis 300 Ohm). Außerdem ist das Gehäuse mit dem Masseanschluss verbunden. Dies ist normalerweise nicht weiter wichtig, da die Sensoren meist entweder isoliert (zB auf Kunststoffteilen) oder auf einem geerdeten/mit Masse verbundenen Metallchassis montiert werden. Wenn man aber die Sensoren abschalten will, um Strom zu sparen (die Sensoren verbrauchen etwa 30 bis 40mA), muss man bei nicht isolierter Montage unbedingt die Plusleitung schalten! Wenn man die Masse-Zuleitung unterbricht, ist der Sensor sonst trotzdem noch über das Gehäuse und das Chassis mit Masse verbunden und wird nicht deaktiviert!&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
|&lt;br /&gt;
[[Bild:Sharp_filter.png|thumb|Filter für einen analogen SHARP-Sensor zur Vermeidung von Störungen auf der +5V Versorgung und dem Ausgang.]]&lt;br /&gt;
Die Stromaufnahme des Sensors von 30-40mA kann täuschen. Mit einer Wiederholrate von etwa 1kHz benötigt der Sensor kurzzeitig eine Stromaufnahme von ca. 1 Ampere! Diese Stromstärke braucht der Sharp-Sensor, um damit seine Infrarot-Sende-LED zu treiben. Durch die hohe Stromaufnahme kann der Sensor infrarotes Licht kurzzeitig mit hoher Intensität aussenden.&lt;br /&gt;
Die Verwendung eines oder sogar mehrerer IR-Distanzsensoren dieser Sorte kann zu erheblichen Spannungseinbrüchen auf der 5V-Versorgungsleitung führen. Am Analogausgang des Sensors können ebenso noch Störungen vorhanden sein. Um Rückwirkungen auf die Versorgung zu vermeiden und die Störungen auf der Ausgangsleitung (für einen Sensor mit analogem Ausgang!) zu verringern, soll hier auf den nebenstehenden Schaltplan verwiesen werden. Der Elko vor dem Sensor sollte die angegebene Kapazität haben, da der Sharp-Sensor seine Stromspitzen hauptsächlich aus diesem bezieht. Dieser Elko wird über den 3,3-Ohm-Widerstand aufgeladen. Der Widerstand kann auch durch eine Induktivität ersetzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Einige der beliebtesten Bausteine sind:'''&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2D12==== &lt;br /&gt;
Distanz 10-80 cm - Entfernung wird durch analoge Spannung am Ausgang übermittelt.&lt;br /&gt;
Dies ist der am häufigsten eingesetzte Sharp-Typ bei mobilen Robotern.&lt;br /&gt;
Dieser Sensor wird seit 2010 nicht mehr produziert. Der Nachfolger ist der GP2Y0A21YK0F.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:GP2D12anschluss.gif|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2Y0A21YK0F (Nachfolger des GP2D12)==== &lt;br /&gt;
Distanz 10-80 cm - Entfernung wird durch analoge Spannung am Ausgang übermittelt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:GP2Y0A21YK0F.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:GP2Y0A21YK0F_diagram.gif|center]]&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2YA21YK==== &lt;br /&gt;
Distanz 10-80 cm - Entfernung wird durch analoge Spannung am Ausgang übermittelt.&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2D120====&lt;br /&gt;
Distanz 4-30 cm - Entfernung wird durch analoge Spannung am Ausgang übermittelt.&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2Y0A02YK====&lt;br /&gt;
Distanz 20-150 cm - Entfernung wird durch analoge Spannung am Ausgang übermittelt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
http://www.shop.robotikhardware.de/shop/catalog/images/artikelbilder/sensoren/gp2y0a02yk_diagramm.gif&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2D02====&lt;br /&gt;
Distanz 10-80 cm - Entfernung wird durch 8-Bit-Digitalausgang übermittelt.&lt;br /&gt;
&lt;br /&gt;
====Sharp GP2D150====&lt;br /&gt;
Distanz 3-30 cm - 1 Bit Schaltausgang.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Datenblätter zu den oberen Sensoren findet man im Download-Bereich des Roboternetzes, siehe unter [[#Weblinks]].&lt;br /&gt;
 &lt;br /&gt;
Um die analogen Spannungen der Sensoren in eine Entfernung umzurechnen, kann man sich mit Hilfe des Datenblattes oder einfacher Probemessungen Tabellen erstellen, welche dann vom Controller zur Umsetzung genutzt werden.&lt;br /&gt;
Bequemer ist allerdings eine Näherungsformel:&lt;br /&gt;
&lt;br /&gt;
====Formel zur Entfernungsberechnung==== &lt;br /&gt;
 D = A/(X-B) &lt;br /&gt;
&lt;br /&gt;
 D ist die Entfernung &lt;br /&gt;
 X ist der Ausgabewert des Sensors &lt;br /&gt;
 A ist die Steigung der Kurve A/X &lt;br /&gt;
 B ist der Offset der Kurve &lt;br /&gt;
&lt;br /&gt;
Die Konstanten A und B ermittelt man am besten über zwei Probemessungen: &lt;br /&gt;
&lt;br /&gt;
D und X sind die Entfernung und der Ausgabewert der ersten Messung (z.B. bei 20 cm) &lt;br /&gt;
D' und X' sind die Entfernung und der Ausgabewert der zweiten Messung (z.B. bei 60 cm) &lt;br /&gt;
&lt;br /&gt;
 A = ((X' - X) * D' * D) /(D - D' ) &lt;br /&gt;
 B = (D' * X' - D * X) /(D' - D)&lt;br /&gt;
&lt;br /&gt;
Mehrere Messungen ergeben ein besseres Ergebnis:&lt;br /&gt;
Dazu werden für jeweils zwei Messungen die Konstanten A und B errechnet (s. o.) und in ein Plotprogramm als Graph dargestellt.&lt;br /&gt;
Plotprogramm (OpenSource): [http://www.padowan.dk Graph]&lt;br /&gt;
&lt;br /&gt;
 Funktion: &lt;br /&gt;
 f(x)=A/(x-B)&lt;br /&gt;
&lt;br /&gt;
Man kann dann durch Verändern von A und B einen Graphen erstellen, der die meisten Eigenschaften der anderen Graphen enthällt. Diese Konstanten entsprechen dann ziemlich genau dem Realwert.&lt;br /&gt;
&lt;br /&gt;
====GP2D12 Messkurve====&lt;br /&gt;
&lt;br /&gt;
[[Bild:gpd12kurve.gif|center]]&lt;br /&gt;
&lt;br /&gt;
====GPD120 Messkurve====&lt;br /&gt;
&lt;br /&gt;
[[Bild:gpd120kurve.gif|center]]&lt;br /&gt;
&lt;br /&gt;
==Ultraschall Sensoren== &lt;br /&gt;
===Ultraschallsensoren SRF04===&lt;br /&gt;
Die Firma '''Devantech''' hat eine Serie von sehr günstigen und kleinen Ultraschallsensoren entwickelt, die sich im Bereich &amp;quot;autonome Robotersysteme&amp;quot; durchgesetzt haben. Sie sind sehr verbreitet, da sie auch für &amp;quot;Hobby-Robotiker&amp;quot; erschwinglich sind und es sich nicht lohnen würde vergleichbare Module selber zu bauen, da diese meistens ungenauer und teurer wären.&lt;br /&gt;
&lt;br /&gt;
Bekannt wurde die Serie durch den '''SRF04'''. Mit seinen kleinen Abmessungen, der niedrigen Stromaufnahme und der hohen Genauigkeit ist er für kleine Messaufgaben im Entfernungsbereich von 3 cm bis 3 m gut geeignet. Der SRF04 kann einen 3 cm dicken Besenstiel in 2 m Entfernung erkennen und wird durch ein [[PWM]]-Signal ausgewertet.&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf_04a.jpg]] [[Bild:srf_04.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Ultraschallsensor SRF05===&lt;br /&gt;
Der Nachfolger des SRF04 ist der neue SRF05. Er besitzt noch eine etwas höhere Reichweite bis 4 Meter. Zudem besitzt er neben dem kompatibel Mode zu SRF04 noch eine Betriebsart bei der er über einen einzigen Port (Pin) gesteuert wird. Das heißt sowohl der Start der Messung und das Ergebnis wird über die gleiche Leitung übertragen. [[Ultraschall SRF05 an RN-MiniControl|Programmbeispiel in Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:Srf05mode1.jpg|center]] &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf05mode2.gif|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf05mode2diagram.gif|center]]&lt;br /&gt;
&lt;br /&gt;
===SRF08===&lt;br /&gt;
Die Alternative zu SRF04 und SRF05 ist der beliebte  '''SRF08''' und dessen Nachfolger '''SRF10''', der nun über den [[I2C]]-Bus ausgewertet werden kann und über eine Reichweite von 3 cm bis 6 m verfügt. Er hat eine noch kleinere Stromaufnahme und zusätzlich befindet sich auf der Platinenfront ein Fotowiderstand (LDR), dessen Lichtmesswerte sich ebenfalls über den [[I2C]]-Bus auswerten lassen. Durch den SRF08 wird es möglich, auch bis zu 16 Mehrfachechos von weiter hinten gelegenen Gegenständen auszuwerten, die bei dem SRF04 ignoriert wurden. Über den [[I2C]]-Bus kann man die Messwerte in Zentimeter, Zoll und in der Laufzeit µs auslesen und spart sich somit die externe Auswertung der Laufzeit wie bei dem SRF04. Weiterhin können insgesamt 16 SRF08-Module an einen [[I2C]]-Bus angeschlossen werden.&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf08_a.jpg]]  [[Bild:srf08_b.jpg]]&lt;br /&gt;
&lt;br /&gt;
===SRF08 Programmbeispiel===&lt;br /&gt;
&lt;br /&gt;
[[SRF08 mit avr-gcc|SRF08 Programmbeispiel mit AVR-GCC]]&lt;br /&gt;
&lt;br /&gt;
SFR08 Programmbeispiel mit [[Bascom]]&lt;br /&gt;
Über die grundsätzliche Behandlung des [[I2C]]-Bus mit [[Bascom]] kann man [[I2C|hier]] nachlesen. &lt;br /&gt;
&lt;br /&gt;
 Const Sf08_adr_0 = &amp;amp;HE0             ' I2C Adresse&lt;br /&gt;
 Const Sf08_c_range = 100            ' Reichweite&lt;br /&gt;
 Const Sf08_c_gain = 1               ' Empfindlichkeit&lt;br /&gt;
&lt;br /&gt;
Die Adresse ist der Default-Wert für den Sensor und kann eingestellt werden. &lt;br /&gt;
Range und Gain sind anzupassen, die angegebenen Werte sind aber mal grundsätzlich verwendbar.&lt;br /&gt;
&lt;br /&gt;
'''Setup'''&lt;br /&gt;
Nach dem Reset sind einmal Range und Gain zu setzen.&lt;br /&gt;
&lt;br /&gt;
'''Setzen Range'''&lt;br /&gt;
&lt;br /&gt;
      I2cstart&lt;br /&gt;
      I2cwbyte Sf08_adr_0   ' Device I2C Adresse&lt;br /&gt;
      I2cwbyte 2            ' register &amp;quot;range&amp;quot;&lt;br /&gt;
      I2cwbyte Sf08_c_range  &lt;br /&gt;
      I2cstop&lt;br /&gt;
 &lt;br /&gt;
'''Setzen Gain'''&lt;br /&gt;
&lt;br /&gt;
      I2cstart&lt;br /&gt;
      I2cwbyte Sf08_adr_0   ' Device I2C Adresse&lt;br /&gt;
      I2cwbyte 1            ' register &amp;quot;gain&amp;quot;&lt;br /&gt;
      I2cwbyte Sf08_c_gain    &lt;br /&gt;
      I2cstop&lt;br /&gt;
&lt;br /&gt;
'''Abfrage Trigger'''&lt;br /&gt;
&lt;br /&gt;
Die Abfrage soll laut Beschreibung in zwei Schritten erfolgen, zwischen denen ca 70 mS gewartet werden soll. Diese Zeit braucht das Gerät zum Messen. &lt;br /&gt;
&lt;br /&gt;
'''Trigger'''  &lt;br /&gt;
      I2cstart&lt;br /&gt;
      I2cwbyte Sf08_adr_0   ' Device I2C Adresse&lt;br /&gt;
      I2cwbyte 0            ' register &amp;quot;Trigger&amp;quot;&lt;br /&gt;
      I2cwbyte 81           ' Meßwert in Zentimetern&lt;br /&gt;
&lt;br /&gt;
      Waitms 70 &lt;br /&gt;
&lt;br /&gt;
'''Ergebnis abholen'''&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 DIM Lsb as Byte&lt;br /&gt;
 DIM Msb as Byte&lt;br /&gt;
 DIM IVal as word&lt;br /&gt;
&lt;br /&gt;
      I2cstart              ' Repeated Start&lt;br /&gt;
      I2cwbyte Sf08_adr_0   ' Device I2C Adresse&lt;br /&gt;
      I2cwbyte 2            ' Meßwert US&lt;br /&gt;
&lt;br /&gt;
      I2cstart                 ' repeated Start&lt;br /&gt;
      I2cwbyte Sf08_adr_0 + 1  ' Device I2C Adresse READ!&lt;br /&gt;
      I2crbyte Msb , Ack      ' Bit 8-15&lt;br /&gt;
      I2crbyte Lsb , Nack     ' Bit 0-7&lt;br /&gt;
      I2cstop&lt;br /&gt;
&lt;br /&gt;
      Ival = Makeint(lsb , Msb) ' umwandeln in Word (16 Bit)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SRF10 Ultraschallsensor ===&lt;br /&gt;
SRF10 ist quasi der Nachfolger von SRF08. Er bietet die gleiche Leistung und ist sogar gegenüber Spannungsschwankungen noch etwas unempfindlicher als der Vorgänger. Der größte Vorteil besteht jedoch darin das er ca. 1/3 kleiner als sein Vorgänger ist. Allerdings bietet er im Gegensatz zum SRF08 weder einen Lichtsensor noch die Möglichkeit, die Mehrfachechos auszulesen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf10foto1.jpg]] [[Bild:srf10foto2.gif]]&lt;br /&gt;
&lt;br /&gt;
Die technischen Daten sind ähnlich:&lt;br /&gt;
* Betriebsspannung: 5V &lt;br /&gt;
* Stromaufnahme: ca. 3mA Standby, ca. 15mA während des Messens Frequenz: 40KHz &lt;br /&gt;
* Maximale Reichweite: 6 m &lt;br /&gt;
* Minimale Reichweite: 4 cm &lt;br /&gt;
* Messwerterfassung: intern, kein externer Controller zur Zeitmessung notwendig &lt;br /&gt;
* Interface: Standard – I2C (passend zu zahlreichen Controllerboards, z.B. RN-Control) &lt;br /&gt;
* Ausgabeformat: µs, cm oder Zoll &lt;br /&gt;
* Feature: Analogverstärkung 40 - 700 (einstellbar, 16 Stufen) &lt;br /&gt;
* Abmessungen: 32mm x 15mm x 10mm &lt;br /&gt;
* Hersteller: Devantech Ltd&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*[[Ultraschall_SRF10_an_RN-Control|Programmbeispiel in Bascom]]&lt;br /&gt;
&lt;br /&gt;
[[Bild:srf10ausbreitung.gif|center]]&lt;br /&gt;
&lt;br /&gt;
===SRF02 Ultraschallsensor===&lt;br /&gt;
Der erste Sensor aus der SRF-Reihe, der mit nur einem Ultraschallwandler auskommt. Dennoch können sich die Leistungen zeigen. Vor allem die Tatsache, dass sowohl RS232- und I2C-Bus-Schnittstelle vorhanden ist, dürfte viele Bastler erfreuen.&lt;br /&gt;
&lt;br /&gt;
* Betriebsspannung 5V (stabilisiert) &lt;br /&gt;
* Stromaufnahme nur 4mA (typisch) &lt;br /&gt;
* Ultraschallfrequenz 40kHz &lt;br /&gt;
* Reichweite 15 cm bis 6 Meter &lt;br /&gt;
* Schnittstelle RS232 (TTL) und I2C-Bus &lt;br /&gt;
* Ausgabeeinheit wahlweise mm, inch oder uS &lt;br /&gt;
* Einfachste Verwendung, keine Kalibration/Justierung notwendig&lt;br /&gt;
* Größe 24mm x 20mm x 17mm &lt;br /&gt;
&lt;br /&gt;
[[Bild:srf02_germany.jpg]]&lt;br /&gt;
&lt;br /&gt;
Einige Bascom-Programmbeispiele zum SRF02 findet man unter [[Ultraschallsensor SRF02 am RN-Board]]&lt;br /&gt;
&lt;br /&gt;
=== Vergleichstabelle ===&lt;br /&gt;
&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
{|{{Blaueschmaltabelle}}&lt;br /&gt;
 |&lt;br /&gt;
 |'''SRF10'''&lt;br /&gt;
 |'''SRF08'''&lt;br /&gt;
 |'''SRF05'''&lt;br /&gt;
 |'''SRF04'''&lt;br /&gt;
 |'''SRF02'''&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Betr. Spannung'''&lt;br /&gt;
 |5V&lt;br /&gt;
 |5V&lt;br /&gt;
 |5V&lt;br /&gt;
 |5V&lt;br /&gt;
 |5V&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Stromaufnahme'''&lt;br /&gt;
 |3mA typ.,&amp;lt;br&amp;gt; 15mA max.&lt;br /&gt;
 |3mA typ.,&amp;lt;br&amp;gt; 15mA max.&lt;br /&gt;
 |4mA typ.,&amp;lt;br&amp;gt; 30mA max.&lt;br /&gt;
 |30mA typ.,&amp;lt;br&amp;gt; 50mA max.&lt;br /&gt;
 |4mA typ.&amp;lt;br&amp;gt;&amp;amp;nbsp;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''US Leistung'''&lt;br /&gt;
 |100 – 150mW&lt;br /&gt;
 |100 – 150mW&lt;br /&gt;
 |100 – 150mW&lt;br /&gt;
 |100 – 150mW&lt;br /&gt;
 |?? mW&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Frequenz'''&lt;br /&gt;
 |40kHz&lt;br /&gt;
 |40kHz&lt;br /&gt;
 |40kHz&lt;br /&gt;
 |40kHz&lt;br /&gt;
 |40kHz&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Bereich'''&lt;br /&gt;
 |72&amp;amp;ordm;&lt;br /&gt;
 |55&amp;amp;ordm;&lt;br /&gt;
 |55&amp;amp;ordm;&lt;br /&gt;
 |55&amp;amp;ordm;&lt;br /&gt;
 |55&amp;amp;ordm;&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Reichweite'''&lt;br /&gt;
 |4cm - 6m&lt;br /&gt;
 |3cm - 6m&lt;br /&gt;
 |3cm - 4m&lt;br /&gt;
 |3cm - 3m&lt;br /&gt;
 |15cm - 6m&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Triggerimpuls'''&lt;br /&gt;
 |&lt;br /&gt;
 |&lt;br /&gt;
 |10µs min.&lt;br /&gt;
 |10µs min.&lt;br /&gt;
 |&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Abmessung in mm'''&lt;br /&gt;
 |32 x 15 x 10&lt;br /&gt;
 |43 x 20 x 17&lt;br /&gt;
 |43 x 20 x 17&lt;br /&gt;
 |43 x 20 x 17&lt;br /&gt;
 |24 x 20 x 17&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Interface'''&lt;br /&gt;
 |I2C&lt;br /&gt;
 |I2C&lt;br /&gt;
 |TTL Impuls&lt;br /&gt;
 |TTL Impuls&lt;br /&gt;
 |I2C o. RS232 TTL&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Feature'''&lt;br /&gt;
 |Minimodul&lt;br /&gt;
 |Lichtsensor&lt;br /&gt;
 |SRF04 Modus&lt;br /&gt;
 |&lt;br /&gt;
 |nur 1 US-Kapsel&lt;br /&gt;
 |-&lt;br /&gt;
 |'''Preis ca. *'''&lt;br /&gt;
 |44,00 €&lt;br /&gt;
 |44,00 €&lt;br /&gt;
 |22,00 €&lt;br /&gt;
 |25,00 €&lt;br /&gt;
 |19,00 €&lt;br /&gt;
 |}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''*''' Der Preis dient nur zum Vergleich untereinander, nicht als Referenz !&lt;br /&gt;
&lt;br /&gt;
==Kompaß== &lt;br /&gt;
===Kompass-Modul CMPS03===&lt;br /&gt;
Laut der Doku ist diese Modul speziell für die Bedürfnisse von Robotern gestaltet worden. Es misst seine Lage relativ zum Erdmagnetfeld und liefert diesen Wert&lt;br /&gt;
* Als PWM-Signal &lt;br /&gt;
* Über I2C entweder 0-255 als Byte oder&lt;br /&gt;
* 0-3599 als 16-Bit Wert (MSB first)&lt;br /&gt;
&lt;br /&gt;
In der vorliegenden Doku ist eine fixe I2C-Adresse von 0xC0 angegeben. Was leider noch fehlt, sind Angaben, ob und wie diese Adresse geändert werden kann.&lt;br /&gt;
&lt;br /&gt;
Bei waagrechter(!) Montage ist das also ein Kompass, bei senkrechter Montage dieser Module kann man auch die Neigung in Nord-Süd-Richtung messen. Hierzu sind 2 Module nötig, die zueinander um 90° gedreht angeordnet sein müssen. Die Neigung in Ost-West-Richtung zu messen, ist dagegen leider nicht möglich. Wenn ein CMPS03 als Kompass verwendet werden soll, sind zwei Dinge zu beachten: &lt;br /&gt;
* Mindestens 20 cm Abstand von Eisen (Motoren)&lt;br /&gt;
* Exakte horizontale Ausrichtung.&lt;br /&gt;
Eine Montage an einem langen Stab aus Holz oder Kunststoff hoch über dem Roboter ist sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Das Ganze ist eine Anwendung des Philips-KMZ51 Magnet-Feld-Sensors mittels eines PIC16F872&lt;br /&gt;
&lt;br /&gt;
Gelegentlich sollte das Modul kalibriert werden. Das kann über den I2C-Bus, aber auch mittels einen Pins am Modul initiiert werden. Dafür muss am Pin 6 für jede Himmelsrichtung einmal kurz GND angelegt werden. Diese 4 Messpunkte nimmt das Modul auf und speichert sie im EEPROM. &lt;br /&gt;
&lt;br /&gt;
Ein Beispielprogramm findet man hier:&lt;br /&gt;
* [[Bascom und Kompass CMPS03]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:cmps3pin.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
==Beschleunigung==&lt;br /&gt;
&lt;br /&gt;
Beschleunigungssensoren messen die Beschleunigung, die auf sie wirkt - wenn sie auf dem Tisch liegen, messen sie beispielsweise die Erdbeschleunigung von 1g. In einem Auto können weitere Beschleunigungen hinzukommen: Zur Seite beim Kurvenfahren, nach vorne oder hinten beim Beschleunigen, oder bei zügiger Fahrt über eine Bergkuppe auch mal welche in vertikaler Richtung.&amp;lt;br&amp;gt;&lt;br /&gt;
Beschleunigungssensoren sind meist mehrdimensional ausgelegt, so dass für mehrere Achsen ein Sensor verwendet werden kann.&lt;br /&gt;
&lt;br /&gt;
[[Bild:beschleunigungssensor_rh.gif|center]]&lt;br /&gt;
&lt;br /&gt;
Anwendungen finden sie in ESP-Systemen in Autos, die u.a. über die Querbeschleunigung ausrechnen können, ob das Fahrzeug seitlich driftet oder gar schleudert.&amp;lt;br&amp;gt;&lt;br /&gt;
Eine andere wichtige Aufgabe übernehmen sie zusammen mit Gyroskopen in Trägheitsnavigationssystemen, die dann sowohl die Lage im Raum als auch die Bewegung feststellen können.&lt;br /&gt;
&lt;br /&gt;
Moderne Piezo-Sensoren sind nur noch chip-gross und können direkt in Schaltungen integriert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiele für solche Chips ist die Chipfamilie [[ADXL]] von Analog Devices. Sie umfaßt 1- und 2-Achs Accelerometer in verschiedenen Empfindlichkeiten. Der Test eines solchen Sensors ist hier beschrieben. http://www.roboternetz.de/phpBB2/viewtopic.php?t=8525&lt;br /&gt;
&lt;br /&gt;
siehe auch: [[Sensoren - Beschleunigung]]&lt;br /&gt;
&lt;br /&gt;
==Drehung==&lt;br /&gt;
&lt;br /&gt;
Um Drehungen zu messen, werden sogenannte Gyroskope, kurz &amp;quot;Gyros&amp;quot; verwendet. Typische Vertreter sind hier die [[ADXRS]]-Familie von Analog Devices. Diese sind allerdings, anders als die Beschleunigungssensoren, meistens nur für eine Achse ausgelegt. Die Ausgabe erfolgt meist analog, jede Ausgangsspannung entspricht einer bestimmten Drehrate in °/Sekunde.&lt;br /&gt;
&lt;br /&gt;
Gemessen wird die Auswirkung der Corioliskraft auf zwei gleiche, in Bewegung gehaltene Massen. Diese schwingen radial, d.h. 90° verdreht zu der Achse, auf der die Drehung gemessen werden soll. Wird der Sensor um die Achse gedreht, ändert sich die Geschwindigkeit dieser Massen, da sie auf einer gedachten Scheibe abwechselnd nach innen und außen wandern. Je weiter sie außen sind, desto höher ist ihre Geschwindigkeit, je weiter innen, desto langsamer sind sie. Bei der Bewegung nach außen müssen sie also beschleunigt werden, andersherum genauso. Die dabei auftretenden Beschleunigungen werden mittels einiger Fühler gemessen, die einen Plattenkondensator bilden.&lt;br /&gt;
&lt;br /&gt;
Eine Anwendung sind im Flugmodellbau Gyro-&amp;quot;Taillocks&amp;quot;, die in Hubschraubern die Heckstabilität verbessern und ESP-Systeme in Autos, die u.a. durch die Messung der Gierrate, also der Drehung des Fahrzeugs um die Hochachse, feststellen können, ob das Fahrzeug seitlich driftet oder sogar schleudert.&lt;br /&gt;
&lt;br /&gt;
==Geschwindigkeit==&lt;br /&gt;
&lt;br /&gt;
Geschwindigkeit kann unterschiedlich erfasst werden.&lt;br /&gt;
* Direkt am Rad oder Antrieb. Diese Methode wird häufig auch in Kfz verwendet, stößt jedoch an ihre Grenzen, wenn die Räder viel Schlupf haben. &lt;br /&gt;
* Gegenüber der Luft mittels [[Pitot-Tube]]. Das wird bei Flugzeugen so gemacht, oder z.B. in der Formel 1. Bei niedrigen Geschwindigkeiten nicht zu gebrauchen&lt;br /&gt;
* Mittels eines optischen Sensors gegenüber dem Untergrund. Dafür kann ein [[Maussensor]] aus einer optischen Maus verwendent werden, evtl. mit einer Anpassung der Optik für einen geänderten Abstand.&lt;br /&gt;
* Mittels GPS-Empfang. Funktioniert nur unter freiem Himmel.&lt;br /&gt;
Siehe auch unter [[Sensoren für die Geschwindigkeitsmessung]]&lt;br /&gt;
&lt;br /&gt;
==Temperatur== &lt;br /&gt;
===NTCs und PTCs===&lt;br /&gt;
&lt;br /&gt;
siehe [[PTC/NTC]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== KTY: Silizium Temperatursensoren ===&lt;br /&gt;
&lt;br /&gt;
Die KTY-Temperatursensoren sind verglichen mit anderen Temperatursensoren verhältnismäßig günstig, haben dafür aber auch mehr Nachteile.&amp;lt;br&amp;gt;&lt;br /&gt;
Die Genauigkeit ist ziemlich eingeschränkt, weshalb die KTY-Temperatursensoren zuerst kalibriert werden sollten. Dies muss schaltungstechnisch oder in Software erfolgen, eine eingebaute Kalibrierung gibt es nicht.&amp;lt;br&amp;gt;&lt;br /&gt;
Andererseits lassen sich mittels Oversampling sehr hoch aufgelöst Temperaturen messen, was aufgrund mittelmäßiger Linearität (auch nach der Linearisierung) aber nur bei der Messung kleiner Temperaturunterschiede empfehlenswert ist.&amp;lt;br&amp;gt;&lt;br /&gt;
Linearisiert werden die KTY an 5 Volt mit einem Reihenwiderstand von etwa 2,7 kOhm bis 3,3 kOhm.&amp;lt;br&amp;gt; &lt;br /&gt;
http://www.roboternetz.de/phpBB2/viewtopic.php?p=342164&amp;amp;sid=ad63fd51eb08bcf225389af5bbc4fcfd#342164&lt;br /&gt;
&lt;br /&gt;
=== Diode als Temperatursensor ===&lt;br /&gt;
Die Flußspannung von Dioden ist temperaturabhängig. Für Siliziumdioden verringert sich die Spannung um etwa 2 mV pro Grad Temperaturerhöhung. Der Absolutwert der Flußspannung hängt vom Strom ab und variiert auch zwischen verschiedenen Chargen. Es ist also fast immer wenigstens ein Abgleich nötig.&lt;br /&gt;
&lt;br /&gt;
===LM335 und LM35===&lt;br /&gt;
&lt;br /&gt;
[[Bild:LM335.JPG|center]]&lt;br /&gt;
&lt;br /&gt;
Der Temperatursensor LM335 funktioniert wie eine Z-Diode, die ihre Durchbruchspannung proportional zur Temperatur ändert. Beim Sensor LM335 beträgt diese Änderung 10mV/K, beim Sensor LM35 beträgt die Änderung 10mV/&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C. &lt;br /&gt;
&lt;br /&gt;
Der größte Unterschied zwischen den Sensoren ist der, dass am Ausgang des LM335 bei 0&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C etwa 2,73V anliegen (273K = 0&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C) und beim LM35 0V. Sollen negative Temperaturen gemessen werden, so muss man für den LM35 das Bezugspotential GND anheben (z.B. mit zwei Si-Dioden).&lt;br /&gt;
&lt;br /&gt;
Von den Sensoren LM335 und LM35 gibt es auch noch genauere Ausführungen, welche einen geringeren Temperatur-Fehler ab Werk haben. Diese Versionen heißen LM15/LM25 bzw. LM135/LM235 und sind entsprechend teurer als die 3er Version (zum Vergleich: LM335 kostet bei Reichelt 0,89€, der LM135 jedoch 7,75€). &lt;br /&gt;
&lt;br /&gt;
Der LM335 und LM35 messen nach einer einfachen Kalibrierung mit einem Poti, bis auf 1&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C genau. Dazu muss man mit dem Poti die Ausgangsspannung bei 25&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C auf 2,98V einstellen (2,98V / 0,01V/K = 298K = 25&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C). Da der Sensor über seinen Messbereich  sehr linear ist, braucht man nur bei einer Temperatur kalibrieren. Durch den Einsatz mehrerer in Reihe geschalteter Sensoren kann man das Ausgangssignal verstärken (bei drei LM335 erhält man dann 30mV/K anstatt 10mV/K) oder, wenn die Sensoren im Raum verteilt sind, einen einfachen Mittelwert bilden.&lt;br /&gt;
&lt;br /&gt;
====Beschaltung und Dimensionierung eines LM335====&lt;br /&gt;
[[Bild:lm335schaltung.jpg|center]]&lt;br /&gt;
Da sich der Sensor wie eine Z-Diode verhält, muss der Strom durch diese begrenzt werden, um eine Verfälschung des Signals oder gar eine Zerstörung zu verhindern. Fließt jedoch zu wenig Strom durch die Diode, so treten Störungen auf und die Genauigkeit sinkt.&lt;br /&gt;
&lt;br /&gt;
Der Strom wird auf einfache Weise wie bei einer LED durch einen Widerstand (hier R1) begrenzt. Der Strom sollte zwischen mindestens 450µA und maximal 5mA liegen. Bei einer Betriebsspannung von 5V kann also für 2mA ein Widerstand von etwa 2,5kOhm benutzt werden (R = U/I). &lt;br /&gt;
&lt;br /&gt;
Die max. Betriebsspannung sollte 30V nicht überschreiten!&lt;br /&gt;
&lt;br /&gt;
Zur Kalibrierung schließt man an ein 10kOhm Potentiometer zwischen V+ und V- (GND) des Sensors an und legt dessen Schleifer auf den ADJ-Pin. Nun stellt man die Ausgangsspannung in Abhängigkeit von der Raumtemperatur (welche bekannt sein sollte) ein (1&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C = 1K, 273K = 0&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C). Kalibriert man den LM335 nicht, so kann dieser um bis zu 9K falsch gehen!&lt;br /&gt;
&lt;br /&gt;
Im letzten Bild (ganz rechts) ist die schon angesprochene Reihenschaltung aufgezeichnet. Um ein größeres Ausgangssignal zu erhalten, kann man das Signal auch mit einem [[Operationsverstärker#Verstärker|Operationsverstärker]] verstärken. Das ist günstiger und man hat die Möglichkeit, das Signal um ein Vielfaches zu verstärken ohne einige Dutzend Sensoren einzusetzen!&lt;br /&gt;
&lt;br /&gt;
Pin-Belegungen und weitere Daten finden sich im Datenblatt (kann man z.B. bei Reichelt herunterladen).&lt;br /&gt;
&lt;br /&gt;
===Temperatur-Sensor LM75===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:lm75.gif|center]]&lt;br /&gt;
&lt;br /&gt;
Dieser Chip von National Semiconductors kann die Umgebungstemperatur mit einer Auflösung von 0.5&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt; Grad Celsius messen und wird über einen I2C Bus angesprochen. &lt;br /&gt;
&lt;br /&gt;
Der Messbereich: &lt;br /&gt;
*−25&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C bis 100&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C bei ±2&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C(max)Genauigkeit.&lt;br /&gt;
*−55&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C bis 125&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C bei ±3&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C(max)Genauigkeit. &lt;br /&gt;
&lt;br /&gt;
Der LM75 bietet:&lt;br /&gt;
* Einen Schaltausgang (invertierbar) - z.B. für Ventilator, Heizung etc.&lt;br /&gt;
* Programmierbarer oberer und unterer Schaltpunkt.&lt;br /&gt;
* Schaltausgang alternativ auch als Interrupt verwendbar.&lt;br /&gt;
* Über von außen einstellbare I2C-Adresse. Bit 0 = R/W, Bits 1-3 einstellbar, Bit 4-7 fixe Adresse 0x9n &lt;br /&gt;
* Maximal 8 Stück LM75 an einem I2C Bus. &lt;br /&gt;
&lt;br /&gt;
Durch I2C-Lesebefehle kann man die Temperatur (2x8Bit)auslesen.&lt;br /&gt;
&lt;br /&gt;
Das erste Byte ist der Temperatur-Vorkommawert in ganzen Grad, als normales signed char. Das Bit 7 vom zweiten Byte bestimmt den Nachkommawert. Wenn Bit 7 gleich 1 so ist der Nachkommawert + 0.5&amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C. Auch dann + 0.5 &amp;lt;sup&amp;gt;o&amp;lt;/sup&amp;gt;C wenn Vorkommawert negativ. Wenn Bit 7 gleich 0 so ist der Nachkommawert = 0. Bit 0-6 vom zweiten Byte sind ohne Bedeutung.&lt;br /&gt;
&lt;br /&gt;
====Programm-Beispiel für LM75====&lt;br /&gt;
Der LM75 wird über den [[I2C]]-Bus angesprochen.&lt;br /&gt;
In BasCom, wo es ja keine signed char gibt, wird das Interpretieren auf sehr unterschiedliche Art empfohlen. &lt;br /&gt;
&lt;br /&gt;
Ich empfehle:&lt;br /&gt;
&lt;br /&gt;
 dim Ival as integer&lt;br /&gt;
 dim Msb as byte&lt;br /&gt;
 dim Lsb as byte&lt;br /&gt;
         I2cstart&lt;br /&gt;
         I2cwbyte &amp;amp;H91   ' Lese-adresse !  &lt;br /&gt;
         If Err = 1 Then&lt;br /&gt;
            I2cstop       ' kein ACK vom LM75 --&amp;gt; irgendein Fehler&lt;br /&gt;
            Ival = 9999   ' Zeichen, daß der Wert ungültig ist !&lt;br /&gt;
         Else&lt;br /&gt;
            I2crbyte Msb , Ack&lt;br /&gt;
            I2crbyte Lsb , Nack&lt;br /&gt;
            I2cstop&lt;br /&gt;
            If Msb.7 = 1 Then&lt;br /&gt;
               Ival = Makeint(msb , &amp;amp;HFF)   ' auffüllen mit den Vorzeichen bits&lt;br /&gt;
            Else&lt;br /&gt;
               Ival = Makeint(msb , &amp;amp;H00)   ' positiv, also bleibt es so&lt;br /&gt;
            End If&lt;br /&gt;
            Ival = Ival * 10                ' erweitern &lt;br /&gt;
            If Lsb.7 = 1 Then&lt;br /&gt;
              Ival = Ival + 5               ' fünf Zehntel Grad dazu &lt;br /&gt;
            End If&lt;br /&gt;
         End if&lt;br /&gt;
&lt;br /&gt;
Man erhält als Ergebnis die Temperatur in 0,5 Grad Schritten (Natürlich nur, wenn's keine Fehler gab).&lt;br /&gt;
&lt;br /&gt;
Ein weiteres Beispiel [http://www.darc-coburg.de/modules/wiwimod/index.php?page=LM75]&lt;br /&gt;
&lt;br /&gt;
==Resistive Sensoren==&lt;br /&gt;
Resistive Sensoren bzw. Linearpotentiometer arbeiten als Spannungsteiler über einer Hybridleitplastik- schicht und sind in unterschiedlichen Bauformen erhältlich; z.B. für Zylindereinbau, Klemmbock- u. Gelenkaugenbefestigung oder Taster.&lt;br /&gt;
Deren Einsatzgebiete sind vorwiegend in der Industrie.&lt;br /&gt;
&lt;br /&gt;
==Kapazitive Sensoren==&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel für kapazitive Sensoren sind Luftfeuchtigkeitsmesser, die ihre Kapazität entsprechend der Luftfeuchtigkeit ändern. Das Auslesen der Kapazität kann prinzipiell über das Ausmessen von Ladekurven geschehen. Dies ist jedoch bei kleinen Kapazitäten nicht praktisch durchführbar, weshalb man in diesem Falle meist einen Schwingkreis baut, dessen Frequenz man dann misst und so dann zusammen mit der bekannten Induktivität der Spule die Kapazität des Kondensators ausrechnen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:feuchtesensor.gif|framed|center|Valvo Feuchtigkeitssensor 10-90% (Kapazität: 122 pF bei TU = 25 °)]]&lt;br /&gt;
&lt;br /&gt;
==Induktive Sensoren==&lt;br /&gt;
Induktiver Näherungsschalter&lt;br /&gt;
&lt;br /&gt;
Sensoren die ihre Induktivität entsprechend der Messgröße ändern können auch mit Hilfe eines Schwingkreises mit bekannter Kapazität ausgemessen werden.&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
==Piezoelektrische Sensoren==&lt;br /&gt;
Hier ist ein Experiment dargestellt das die Funktion von Piezowandlern demonstriert.&lt;br /&gt;
&lt;br /&gt;
Drückt man auf eine längliche Glasplatte (Mikroskopträger Länge 76mm), die an den Enden auf je einem Piezo Wandler gelagert ist, dann ergeben sich Signale, die von den Kräften auf die Wandler abhängig sind. &lt;br /&gt;
&lt;br /&gt;
Die Summe der beiden Kräfte entspricht der Gesamtkraft. Das Verhältnis der beiden Kräfte entspricht dem Verhältnis der Abstände des Druckpunktes zum Wandler. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Bild:Piezowandler.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Versuch wurde mit der Frequenz von ca. 4Hz (0,5s/div) von einem Ende bis zum anderen auf die Platte gedrückt. (Tonleiter). Die Signale der beiden Wandler sind im Oszillogramm aufgetragen und die Folge der Druck-Positionen ist auf den ersten Blick schon mal nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
==PIR Passiv Infrarot Sensoren==&lt;br /&gt;
Hier handelt es sich um Passiv-Infrarot-Bewegungsmelder. Bewegungsmelder regieren beim Eintritt einer Person (Tier) in das Erfassungsfeld des Sensors. Die Anwendungsmöglichkeiten sind nahezu unbegrenzt, man kennt die Technik ja von vielen Terrassenlampen, welche sich beim vorbeigehen automatisch einschalten.&lt;br /&gt;
Reagiert wird also auf die Körperwärme einer sich im Erfassungsfeld bewegenden Person.&lt;br /&gt;
[[Bild:pir.jpg|right]]&lt;br /&gt;
Zum Prinzip: &lt;br /&gt;
Wärmestrahlen, die einen Erfassungsvorgang auslösen, liegen im Infrarot-Bereich des Wellenspektrums. In diesem Bereich gibt der menschliche Körper seine Wärmestrahlung ab. Leuchtmittel wie Glüh- , Halogen- und Entladungslampen, die für eine Strahlung im sichtbaren Bereich um 0,555 μm entwickelt wurden, geben jedoch auch einen erheblichen Teil an Wärmestrahlung im Infrarot-Bereich ab.&lt;br /&gt;
Im Spektrum oberhalb des sichtbaren Licht, ab 0,780 μm, beginnt der Infrarot-Bereich. Die Wellenlänge&lt;br /&gt;
dieser IR-Strahlung ist abhängig von der Temperatur eines Körpers. Die Wärmestrahlung&lt;br /&gt;
des Menschen hat ihr Maximum zwischen 9 und 10 μm im Infrarot-Bereich.&lt;br /&gt;
Diese Tatsache nutzt der PIR Sensor mittels sogenannter pyroelektrischer IRDetektoren, welche eine hohe Empfindlichkeit im langwelligen Infrarot-Bereich aufweisen. Die Infrarot-&lt;br /&gt;
Strahlung verhält sich ähnlich wie sichtbares Licht. Sie kann reflektiert und durch Linsen&lt;br /&gt;
gebündelt werden.&lt;br /&gt;
Basis eines solchen IR-Detektors (Sensors) sind Lithium-Tantalatkristalle. Diese Kristalle erzeugen,&lt;br /&gt;
bei Wärmeänderung (positive oder negative Temperaturänderung), eine elektrische Spannung.&lt;br /&gt;
Die von den Kristallen abgegebene Spannung liegt im Bereich von einigen μV (μV = millionstel Volt) und ist von folgenden Bedingungen abhängig: &lt;br /&gt;
* Der Intensität der Wärmequelle (Temperatur und Größe)&lt;br /&gt;
* Dem Umgebungsmedium (Temperatur, unterschiedliche Luftfeuchtigkeit)&lt;br /&gt;
* Der Entfernung zwischen Wärmequelle und IR-Sensor&lt;br /&gt;
* Der Bewegungsgeschwindigkeit und Bewegungsrichtung der Wärmequelle&lt;br /&gt;
* Der Empfindlichkeit des PIR-Elementes (frequenzabhängiges Bandpaßverhalten mit Maximum bei ca. 0,1 Hz)&lt;br /&gt;
&lt;br /&gt;
Zur Unterdrückung von Einflüssen aus der Umgebung (übliche wetterbedingte Temperaturänderungen), sind in jedem Sensor 2 Kristalle antiparallel geschaltet. &lt;br /&gt;
Einer der Kristalle gibt, bei Auftreffen von Wärmestrahlung einen positiven, der andere einen negativen Spannungsimpuls ab. Wärmeänderungen die gleichzeitig und mit gleicher Intensität auf beide Kristalle einwirken lösen so keinen Erfassungsvorgang aus, denn die beiden Impulse heben sich gegenseitig auf. Dadurch ist ein Auslösen bei Wärmeänderungen der Umgebung weitgehend ausgeschlossen.&lt;br /&gt;
Anders verhält es sich bei schnellen Bewegungen. Die Lithiumtantalat-Kristalle geben, entsprechend&lt;br /&gt;
der Bewegung und der dadurch hervorgerufenen Wärmeänderung im Erfassungsfeld, ihre&lt;br /&gt;
Impulse zeitversetzt ab. Die beiden Impulse addieren sich zu einer Wechselgröße mit höherer&lt;br /&gt;
Signalamplitude. Dieses elektrische Ausgangssignal ist proportional der Wärmeänderung und führt zur Meldung einer Bewegung.&lt;br /&gt;
&lt;br /&gt;
==Autoren==&lt;br /&gt;
* [[Benutzer:PicNick|PicNick]]&lt;br /&gt;
* [[Benutzer:Frank|Frank]] &lt;br /&gt;
* [[Benutzer:Dennis.strehl|Dennis.strehl]]&lt;br /&gt;
* [[Benutzer:Florian|Florian]]&lt;br /&gt;
* [[Benutzer:BASTIUniversal|BASTIUniversal]]&lt;br /&gt;
* Topic&lt;br /&gt;
* [[Benutzer:Manf|Manf]]&lt;br /&gt;
&lt;br /&gt;
==Siehe auch==&lt;br /&gt;
* [[Navigation]]&lt;br /&gt;
* [[Sensoren]]&lt;br /&gt;
* [[Graycode]]&lt;br /&gt;
* [[Ultraschall SRF10 an RN-Control]]&lt;br /&gt;
* [[Ultraschall SRF05 an RN-MiniControl]]&lt;br /&gt;
* [[Ultraschallsensor SRF02 am RN-Board]]&lt;br /&gt;
* [[Bascom und Kompass CMPS03]]&lt;br /&gt;
&lt;br /&gt;
==Weblinks==&lt;br /&gt;
*[http://www.roboternetz.de/community/showthread.php?51885-Drehzahlmessung-mit-B%FCrstenimpulsen-an-kleinem-DC-Motor Forumbeitrag: Drehzahlmessung bei Gleichstrommotoren über Bürstenimpulsauswertung]&lt;br /&gt;
*[http://www.ikm.uni-karlsruhe.de/forschung/pzt_webseiten/de/grundlagen/pyro.html Piezoelektrische Sensoren]&lt;br /&gt;
*[http://www.roboternetz.de/phpBB2/dload.php?action=category&amp;amp;cat_id=2 Datenblätter im Download-Bereich des Roboternetz]&lt;br /&gt;
*[http://www.nxp.com/acrobat_download/various/SC17_GENERAL_TEMP_1996_3.pdf KTY Sensor Datenblatt]&lt;br /&gt;
*[http://www.tranzistoare.ro/datasheets2/83/83853_1.pdf NTC Datenblatt]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Robotikeinstieg]]&lt;br /&gt;
[[Category:Grundlagen]]&lt;br /&gt;
[[Category:Elektronik]]&lt;br /&gt;
[[Category:Sensoren]]&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Quad_states.png&amp;diff=17547</id>
		<title>Datei:Quad states.png</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Quad_states.png&amp;diff=17547"/>
				<updated>2011-07-18T10:57:22Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: Drehgeberauswertung, States&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Drehgeberauswertung, States&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Datei:Quad_err.png&amp;diff=17546</id>
		<title>Datei:Quad err.png</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Datei:Quad_err.png&amp;diff=17546"/>
				<updated>2011-07-18T10:46:13Z</updated>
		
		<summary type="html">&lt;p&gt;PicNick: QUadraturauswertung mit ISR, Fehlerfall&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;QUadraturauswertung mit ISR, Fehlerfall&lt;/div&gt;</summary>
		<author><name>PicNick</name></author>	</entry>

	</feed>